Branch for IW4X practically everything refactored

This commit is contained in:
RaidMax 2017-05-26 17:49:27 -05:00
parent 85a658b987
commit 10075b0d3f
107 changed files with 7426 additions and 3995 deletions

5
.gitignore vendored
View File

@ -186,4 +186,7 @@ FakesAssemblies/
# LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
ModelManifest.xml
/.vs/IW4M Admin/v15
/.vs/IW4M Admin/v15/Browse.VC.db
/.vs/IW4M Admin/v15

24
Admin/Commands.cs Normal file
View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using SharedLibrary;
using SharedLibrary.Network;
using System.Threading.Tasks;
namespace IW4MAdmin
{
class Plugins : Command
{
public Plugins(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override async Task ExecuteAsync(Event E)
{
await E.Origin.Tell("^5Loaded Plugins:");
foreach (SharedLibrary.Extensions.IPlugin P in PluginImporter.potentialPlugins)
{
await E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
}
}
}
}

73
Admin/Config.cs Normal file
View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using SharedLibrary.Interfaces;
namespace IW4MAdmin
{
public class Config : Serialize<Config>
{
public string IP;
public int Port;
public string Password;
public string FtpPrefix;
public override string Filename()
{
return $"config/servers/{IP}_{Port}.cfg";
}
public static Config Generate()
{
string IP = String.Empty;
int Port = 0;
string Password;
while(IP == String.Empty)
{
try
{
Console.Write("Enter server IP: ");
string input = Console.ReadLine();
IPAddress.Parse(input);
IP = input;
}
catch (Exception)
{
continue;
}
}
while(Port == 0)
{
try
{
Console.Write("Enter server port: ");
Port = Int32.Parse(Console.ReadLine());
}
catch (Exception)
{
continue;
}
}
Console.Write("Enter server RCON password: ");
Password = Console.ReadLine();
var config = new Config() { IP = IP, Password = Password, Port = Port };
config.Write();
Console.WriteLine("Config saved, add another? [y/n]:");
if (Console.ReadLine().ToLower().First() == 'y')
Generate();
return config;
}
}
}

View File

@ -10,16 +10,15 @@ namespace IW4MAdmin
public Heartbeat(Server I)
{
Handle = new Connection("http://raidmax.org/IW4M/Admin");
Instance = I;
}
public void Send()
public void Send(Server S)
{
String URI = String.Format("http://raidmax.org/IW4M/Admin/heartbeat.php?address={0}&name={1}&map={2}&players={3}&version={4}", Instance.getPort().ToString(), Instance.getName(), Instance.getMap(), Instance.getClientNum() + '/' + Instance.getMaxClients().ToString(), IW4MAdmin.Program.Version.ToString());
String URI = String.Format("http://raidmax.org/IW4M/Admin/heartbeat.php?port={0}&name={1}&map={2}&players={3}&version={4}&gametype={5}&servercount={6}", S.getPort(), S.getName(), S.CurrentMap.Name, S.getPlayers().Count, IW4MAdmin.Program.Version.ToString(), S.Gametype, Manager.GetInstance().Servers);
// blind fire
Handle.Request(URI);
}
private Connection Handle;
private Server Instance;
}
}

View File

@ -9,7 +9,7 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>IW4MAdmin</RootNamespace>
<AssemblyName>IW4MAdmin</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
@ -61,6 +61,8 @@
<Prefer32Bit>false</Prefer32Bit>
<RunCodeAnalysis>false</RunCodeAnalysis>
<CodeAnalysisIgnoreGeneratedCode>false</CodeAnalysisIgnoreGeneratedCode>
<DocumentationFile>
</DocumentationFile>
</PropertyGroup>
<PropertyGroup>
<TargetZone>LocalIntranet</TargetZone>
@ -88,25 +90,29 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<Reference Include="SharedLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<Reference Include="Kayak, Version=0.7.2.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>lib\SharedLibrary.dll</HintPath>
<HintPath>lib\Kayak.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Data" />
<Reference Include="System.Data.SQLite, Version=1.0.96.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>libs\System.Data.SQLite.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Management" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Command.cs" />
<Compile Include="Commands.cs" />
<Compile Include="Config.cs" />
<Compile Include="Connection.cs" />
<Compile Include="Heartbeat.cs" />
<Compile Include="Kayak.cs" />
<Compile Include="Main.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Plugins.cs" />
@ -117,19 +123,15 @@
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="Server.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="IW4_GameStructs.cs" />
<Compile Include="WebService.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
<None Include="lib\Kayak.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="lib\System.Data.SQLite.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<Content Include="IW4AdminIcon.ico" />
<Content Include="lib\AdminInterface.dll">
<Content Include="lib\Kayak.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="lib\Newtonsoft.Json.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="lib\SharedLibrary.dll">
@ -138,41 +140,47 @@
<Content Include="lib\SQLite.Interop.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="plugins\SimpleStatsPlugin.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="plugins\WebfrontPlugin.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="version.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="webfront\bans.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\console.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\error.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\footer.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\graph.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\header.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\main.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\main.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\mobile.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\notfound.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\player.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Content Include="webfront\penalties.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="webfront\stats.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<Content Include="webfront\players.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="config\maps.cfg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@ -186,6 +194,10 @@
<None Include="app.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="config\web.cfg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="IW4MAdmin.exe.config" />
<None Include="m2demo\admin\commands.gsc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
@ -198,6 +210,7 @@
<None Include="m2demo\settings\main.gsc">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
<None Include="Properties\app.manifest" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
@ -298,10 +311,24 @@
<FileType>Assembly</FileType>
</PublishFile>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
<PostBuildEvent>move "$(TargetDir)Newtonsoft.Json.dll" "$(TargetDir)lib\Newtonsoft.Json.dll"
copy /Y "$(SolutionDir)lib\*.dll" "$(TargetDir)lib"
copy /Y "$(TargetDir)$(TargetName).exe" "$(SolutionDir)BUILD"
copy /Y "$(TargetDir)IW4MAdmin.exe.config" "$(SolutionDir)BUILD"
copy /Y "$(ProjectDir)lib\Kayak.dll" "$(SolutionDir)BUILD\lib"
copy /Y "$(ProjectDir)lib\SQLite.Interop.dll" "$(SolutionDir)BUILD\lib"
</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -0,0 +1,11 @@
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.5" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="lib"/>
</assemblyBinding>
</runtime>
</configuration>

View File

@ -1,109 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace IW4MAdmin
{
[StructLayout(LayoutKind.Explicit)]
public struct netadr_t
{
[FieldOffset(0x0)]
Int32 type;
[FieldOffset(0x4)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public Byte[] ip;
[FieldOffset(0x8)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public Byte[] ipx;
[FieldOffset(0x12)]
public short port;
}
[StructLayout(LayoutKind.Explicit)]
public struct client_s
{
[FieldOffset(0x0)]
public Int32 state;
[FieldOffset(0x28)]
public netadr_t adr;
[FieldOffset(0x65C)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)]
public String connectInfoString;
[FieldOffset(0x20EA4)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=400)] // doubt this is the correct size
public String lastUserCmd;
[FieldOffset(0x212A4)]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public String name;
[FieldOffset(0x212C0)]
public int snapNum;
[FieldOffset(0x212C8)]
public short ping;
[FieldOffset(0x41AF0)]
public int isBot;
[FieldOffset(0x43F00)]
public UInt64 steamid;
};
[StructLayout(LayoutKind.Explicit)]
public struct dvar_t
{
[FieldOffset(0)]
public IntPtr name;
[FieldOffset(4)]
public IntPtr description;
[FieldOffset(8)]
public uint flags;
[FieldOffset(12)]
public Byte type;
[FieldOffset(16)]
public IntPtr current;
[FieldOffset(32)]
public IntPtr latched;
[FieldOffset(48)]
public IntPtr _default;
[FieldOffset(64)]
public IntPtr min;
[FieldOffset(68)]
public IntPtr max;
}
class Helpers
{
public static String NET_AdrToString(netadr_t a)
{
// not worrying about NA_TYPE
StringBuilder s = new StringBuilder(64);
s.AppendFormat("{0}.{1}.{2}.{3}:{4}", a.ip[0], a.ip[1], a.ip[2], a.ip[3], a.port);
return s.ToString();
}
public static unsafe T ReadStruct<T>(byte[] buffer) where T : struct
{
fixed (byte* b = buffer)
return (T)Marshal.PtrToStructure(new IntPtr(b), typeof(T));
}
}
}

View File

@ -1,63 +1,55 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Kayak;
using Kayak.Http;
using System.Net;
using Kayak;
namespace Webfront_Plugin
namespace IW4MAdmin
{
static class Manager
{
public static IScheduler webScheduler { get; private set; }
public static Framework webFront { get; private set; }
public static IPAddress lastIP;
public static IServer webServer;
public static void Init()
{
webScheduler = KayakScheduler.Factory.Create(new SchedulerDelegate());
webServer = KayakServer.Factory.CreateHttp(new RequestDelegate(), webScheduler);
webFront = new Framework();
using (webServer.Listen(new IPEndPoint(IPAddress.Any, 1624)))
webScheduler.Start();
}
}
class SchedulerDelegate : ISchedulerDelegate
class Scheduler: ISchedulerDelegate
{
public void OnException(IScheduler scheduler, Exception e)
{
Manager.GetInstance().Logger.Write("Web service has encountered an error - " + e.StackTrace);
}
public void OnStop(IScheduler scheduler)
{
Manager.GetInstance().Logger.Write("Web service has been stopped...");
}
}
class RequestDelegate : IHttpRequestDelegate
class Request : IHttpRequestDelegate
{
public void OnRequest(HttpRequestHead request, IDataProducer requestBody, IHttpResponseDelegate response)
{
DefaultKayakServer castCrap = (DefaultKayakServer)Manager.webServer;
Manager.lastIP = castCrap.clientAddress.Address;
string body = Manager.webFront.processRequest(request);
// Manager.GetInstance().mainLog.Write("HTTP request received", SharedLibrary.Log.Level.Debug);
NameValueCollection querySet = new NameValueCollection();
if (request.QueryString != null)
querySet = System.Web.HttpUtility.ParseQueryString(SharedLibrary.Utilities.removeNastyChars(request.QueryString));
querySet.Set("IP", ((DefaultKayakServer)(WebService.webService)).clientAddress.Address.ToString());
SharedLibrary.HttpResponse requestedPage = WebService.getPage(request.Path, querySet, request.Headers);
var headers = new HttpResponseHead()
{
Status = "200 OK",
Headers = new Dictionary<string, string>()
Headers = new Dictionary<string, string>()
{
{ "Content-Type", "text/html" },
{ "Content-Length", body.Length.ToString() },
{ "Content-Type", requestedPage.contentType },
{ "Content-Length", requestedPage.content.Length.ToString() },
{ "Access-Control-Allow-Origin", "*" },
}
};
response.OnResponse(headers, new BufferedProducer(body));
foreach (var key in requestedPage.additionalHeaders.Keys)
headers.Headers.Add(key, requestedPage.additionalHeaders[key]);
response.OnResponse(headers, new BufferedProducer(requestedPage.content));
}
}
@ -68,6 +60,7 @@ namespace Webfront_Plugin
public BufferedProducer(string data) : this(data, Encoding.UTF8) { }
public BufferedProducer(string data, Encoding encoding) : this(encoding.GetBytes(data)) { }
public BufferedProducer(byte[] data) : this(new ArraySegment<byte>(data)) { }
public BufferedProducer(ArraySegment<byte> data)
{
this.data = data;
@ -113,4 +106,4 @@ namespace Webfront_Plugin
resultCallback(str);
}
}
}
}

View File

@ -1,65 +1,75 @@
#define USINGMEMORY

#define USINGMEMORY
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using SharedLibrary;
using System.Threading.Tasks;
using System.IO;
namespace IW4MAdmin
{
class Program
{
static public double Version { get; private set; }
static private Manager serverManager;
static private Manager ServerManager;
static void Main(string[] args)
{
Version = 1.1;
Version = 1.3;
double latestVersion = 0;
handler = new ConsoleEventDelegate(OnProcessExit);
SetConsoleCtrlHandler(handler, true);
double.TryParse(checkUpdate(), out latestVersion);
double.TryParse(CheckUpdate(), out latestVersion);
Console.WriteLine("=====================================================");
Console.WriteLine(" IW4M ADMIN");
Console.WriteLine(" by RaidMax ");
if (latestVersion != 0)
Console.WriteLine(" Version " + Version + " (latest " + latestVersion + ")");
else
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
Console.WriteLine(" Version " + Version + " (unable to retrieve latest)");
Console.WriteLine("=====================================================");
serverManager = new Manager();
Thread serverMGRThread = new Thread(serverManager.Init);
serverMGRThread.Name = "Server Manager thread";
serverMGRThread.Start();
while(!serverManager.isReady())
try
{
SharedLibrary.Utilities.Wait(1);
CheckDirectories();
ServerManager = Manager.GetInstance();
ServerManager.Init();
/*Task.Run(() =>
{
String userInput;
Player Origin = new Player("IW4MAdmin", "", -1, Player.Permission.Console, -1, "", 0, "");
do
{
userInput = Console.ReadLine();
if (userInput.ToLower() == "quit")
ServerManager.Stop();
if (ServerManager.Servers.Count == 0)
return;
Event E = new Event(Event.GType.Say, userInput, Origin, null, ServerManager.Servers[0]);
Origin.lastEvent = E;
ServerManager.Servers[0].ExecuteEvent(E);
Console.Write('>');
} while (userInput != null && ServerManager.Running);
});*/
}
if (serverManager.getServers() != null)
getManager().mainLog.Write("IW4M Now Initialized!", Log.Level.Production);
String userInput;
Server serverToExecuteOn = serverManager.getServers()[0];
Player Origin = new Player("IW4MAdmin", "", -1, Player.Permission.Console, -1, "", 0, "");
do
catch(Exception e)
{
userInput = Console.ReadLine();
Event E = new Event(Event.GType.Say, userInput, Origin, null, serverToExecuteOn);
Origin.lastEvent = E;
serverToExecuteOn.processEvent(E);
Console.Write('>');
Console.WriteLine($"Fatal Error during initialization: {e.Message}");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
return;
}
} while (userInput != null && serverManager.isRunning());
serverMGRThread.Join();
serverManager.mainLog.Write("Shutting down IW4MAdmin...", Log.Level.Debug);
ServerManager.Start();
}
static ConsoleEventDelegate handler;
@ -68,22 +78,8 @@ namespace IW4MAdmin
{
try
{
foreach (Server S in getServers())
{
if (S == null)
continue;
S.Broadcast("^5IW4MAdmin ^7is going ^1offline^7");
S.isRunning = false;
if (Utilities.shutdownInterface(S.pID()))
getManager().mainLog.Write("Successfully removed IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
else
getManager().mainLog.Write("Could not remove IW4MAdmin from server with PID " + S.pID(), Log.Level.Debug);
}
getManager().shutDown();
return false;
ServerManager.Stop();
return true;
}
catch
@ -96,20 +92,31 @@ namespace IW4MAdmin
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetConsoleCtrlHandler(ConsoleEventDelegate callback, bool add);
static private String checkUpdate()
static private String CheckUpdate()
{
Connection Ver = new Connection("http://raidmax.org/IW4M/Admin/version.php");
return Ver.Read();
}
static public Server[] getServers()
static void CheckDirectories()
{
return serverManager.getServers().ToArray();
}
if (!Directory.Exists("Lib"))
throw new Exception("Lib folder does not exist");
static public Manager getManager()
{
return serverManager;
if (!Directory.Exists("Config"))
{
Console.WriteLine("Warning: Config folder does not exist");
Directory.CreateDirectory("Config");
}
if (!Directory.Exists("Config/Servers"))
Directory.CreateDirectory("Config/Servers");
if (!Directory.Exists("Logs"))
Directory.CreateDirectory("Logs");
if (!Directory.Exists("Database"))
Directory.CreateDirectory("Database");
}
}
}

View File

@ -7,238 +7,134 @@ using System.Runtime.InteropServices;
using System.Net;
using System.Threading;
using SharedLibrary;
using System.IO;
using SharedLibrary.Network;
using System.Threading.Tasks;
namespace IW4MAdmin
{
class Manager
class Manager : SharedLibrary.Interfaces.IManager
{
private List<Server> Servers;
private SortedDictionary<int, Thread> ThreadList;
private List<int> activePIDs;
public Log mainLog;
private bool initialized = false;
static Manager Instance;
public List<Server> Servers { get; private set; }
List<Command> Commands;
Kayak.IScheduler webServiceTask;
Thread WebThread;
public bool Running { get; private set; }
#if FTP_LOG
const double UPDATE_FREQUENCY = 15000;
#else
const double UPDATE_FREQUENCY = 300;
#endif
public Manager()
public Log Logger;
private Manager()
{
ThreadList = new SortedDictionary<int, Thread>();
IFile logFile = new IFile("IW4MAdminManager.log", true);
mainLog = new Log(logFile, Log.Level.Production, 0);
IFile logFile = new IFile("Logs/IW4MAdminManager.log", true);
Logger = new Log(logFile, Log.Level.Production, 0);
Servers = new List<Server>();
Commands = new List<Command>();
}
public void Init()
{
while (getCurrentIW4MProcesses().Count == 0)
{
mainLog.Write("No viable IW4M instances detected.", Log.Level.Production);
SharedLibrary.Utilities.Wait(10);
}
PluginImporter.Load();
activePIDs = getCurrentIW4MProcesses();
Servers = loadServers();
foreach (Server S in Servers)
{
Server IW4MServer = S;
Thread IW4MServerThread = new Thread(IW4MServer.Monitor);
IW4MServerThread.Name = "Monitor thread for " + S.pID();
ThreadList.Add(IW4MServer.pID(), IW4MServerThread);
IW4MServerThread.Start();
}
initialized = true;
while (activePIDs.Count > 0)
{
List<Server> defunctServers = new List<Server>();
lock (Servers)
{
foreach (Server S in Servers)
{
if (S == null)
continue;
if (!isIW4MStillRunning(S.pID()))
{
Thread Defunct = ThreadList[S.pID()];
S.isRunning = false;
if (Defunct != null)
{
Defunct.Join();
ThreadList[S.pID()] = null;
}
if (!S.isRunning)
Utilities.shutdownInterface(S.pID());
mainLog.Write("Server with PID #" + S.pID() + " can no longer be monitored.", Log.Level.Production);
activePIDs.Remove(S.pID());
defunctServers.Add(S);
}
}
}
foreach (Server S in defunctServers)
Servers.Remove(S);
defunctServers = null;
scanForNewServers();
SharedLibrary.Utilities.Wait(5);
}
mainLog.Write("Manager shutting down...");
}
public void shutDown()
{
foreach (Server S in Servers)
S.isRunning = false;
activePIDs = new List<int>();
foreach (KeyValuePair<int, Thread> T in ThreadList)
ThreadList[T.Key].Join();
}
public bool isRunning()
{
return activePIDs.Count != 0;
}
public List<Server> getServers()
public List<Server> GetServers()
{
return Servers;
}
private void scanForNewServers()
public List<Command> GetCommands()
{
List<int> newProcesses = getCurrentIW4MProcesses();
return Commands;
}
if (activePIDs == null)
return;
public static Manager GetInstance()
{
return Instance == null ? Instance = new Manager() : Instance;
}
foreach (int pID in newProcesses)
public void Init()
{
var Configs = Directory.EnumerateFiles("config/servers").Where(x => x.Contains(".cfg"));
if (Configs.Count() == 0)
Config.Generate();
PluginImporter.Load();
foreach (var file in Configs)
{
if (pID == 0)
continue;
var Conf = Config.Read(file);
var ServerInstance = new IW4MServer(this, Conf.IP, Conf.Port, Conf.Password);
if (activePIDs.IndexOf(pID) == -1 && !ThreadList.ContainsKey(pID))
Task.Run(async () =>
{
Server S = loadIndividualServer(pID);
if (S == null)
try
{
mainLog.Write("Unable to load new detected server!", Log.Level.Debug);
continue;
await ServerInstance.Initialize();
Servers.Add(ServerInstance);
Logger.Write($"Now monitoring {ServerInstance.Hostname}", Log.Level.Production);
}
Servers.Add(S);
Thread IW4MServerThread = new Thread(S.Monitor);
ThreadList.Add(pID, IW4MServerThread);
mainLog.Write("New server detected on port " + S.getPort(), Log.Level.Production);
IW4MServerThread.Start();
}
}
}
private bool isIW4MStillRunning(int pID)
{
if (pID > 0)
{
try
{
Process P = Process.GetProcessById(pID);
return true;
}
catch (System.ArgumentException)
{
return false;
}
}
return false;
}
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
private List<int> getCurrentIW4MProcesses()
{
List<int> PIDs = new List<int>();
foreach (Process P in Process.GetProcessesByName("iw4m"))
{
IntPtr Handle = OpenProcess(0x10, false, P.Id);
Byte[] isClient = new Byte[1];
int numberRead = 0;
ReadProcessMemory((int)Handle, 0x5DEC04, isClient, 1, ref numberRead);
if (isClient[0] == 0)
PIDs.Add(P.Id);
}
return PIDs;
}
private List<Server> loadServers()
{
List<Server> activeServers = new List<Server>();
foreach (int pID in activePIDs)
{
Server S = loadIndividualServer(pID);
if (S != null)
activeServers.Add(S);
}
return activeServers;
}
private Server loadIndividualServer(int pID)
{
if (pID > 0)
{
IntPtr Handle = OpenProcess(0x10, false, pID);
if (Handle != null)
{
int timeWaiting = 0;
bool sv_running = false;
while(!sv_running) // server is still booting up
catch (SharedLibrary.Exceptions.ServerException e)
{
int sv_runningPtr = Utilities.getIntFromPointer(0x1AD7934, (int)Handle) + 0x10; // where the dvar_t struct is stored + the offset for current value
sv_running = Utilities.getBoolFromPointer(sv_runningPtr, (int)Handle);
SharedLibrary.Utilities.Wait(1);
timeWaiting++;
if (timeWaiting > 30) // don't want to get stuck waiting forever if the server is frozen
return null;
Logger.Write($"Not monitoring server {Conf.IP}:{Conf.Port} due to uncorrectable errors", Log.Level.Production);
if (e.GetType() == typeof(SharedLibrary.Exceptions.DvarException))
Logger.Write($"Could not get the dvar value for {(e as SharedLibrary.Exceptions.DvarException).Data["dvar_name"]} (ensure the server has a map loaded)", Log.Level.Production);
else if (e.GetType() == typeof(SharedLibrary.Exceptions.NetworkException))
Logger.Write("Could not communicate with the server (ensure the configuration is correct)", Log.Level.Production);
}
});
if (timeWaiting > 5)
SharedLibrary.Utilities.Wait(2);
dvar net_ip = Utilities.getDvarOld(0x64A1DF8, (int)Handle);
dvar net_port = Utilities.getDvarOld(0x64A3004, (int)Handle);
return new IW4MServer(net_ip.current, Convert.ToInt32(net_port.current), "", (int)Handle, pID);
}
return null;
}
return null;
SharedLibrary.WebService.Init();
webServiceTask = WebService.getScheduler();
WebThread = new Thread(webServiceTask.Start);
WebThread.Name = "Web Thread";
WebThread.Start();
while (Servers.Count < 1)
Thread.Sleep(500);
Running = true;
}
public void Start()
{
int Processed;
DateTime Start;
while(Running)
{
Processed = 0;
Start = DateTime.Now;
foreach (Server S in Servers)
Processed += S.ProcessUpdatesAsync().Result;
// ideally we don't want to sleep on the thread, but polling
// as much as possible will use unnecessary CPU
int ElapsedTime = (int)(DateTime.Now - Start).TotalMilliseconds;
while ((Processed != Servers.Count || ElapsedTime < UPDATE_FREQUENCY) && Running)
{
Thread.Sleep((int)(UPDATE_FREQUENCY - ElapsedTime));
ElapsedTime = (int)(DateTime.Now - Start).TotalMilliseconds;
}
}
#if !DEBUG
foreach (var S in Servers)
S.Broadcast("^1IW4MAdmin going offline!");
#endif
Servers.Clear();
WebThread.Abort();
webServiceTask.Stop();
}
public bool isReady()
public void Stop()
{
return initialized;
Running = false;
}
}
}

View File

@ -3,17 +3,20 @@ using System.IO;
using System.Collections.Generic;
using System.Reflection;
using SharedLibrary;
using SharedLibrary.Extensions;
namespace IW4MAdmin
{
public class PluginImporter
{
public static List<Command> potentialCommands = new List<Command>();
public static List<Plugin> potentialPlugins = new List<Plugin>();
public static Plugin webFront = null;
public static List<IPlugin> potentialPlugins = new List<IPlugin>();
public static IPlugin webFront = null;
//private static AppDomain pluginDomain;
public static bool Load()
{
//pluginDomain = AppDomain.CreateDomain("Plugins");
string[] dllFileNames = null;
if (Directory.Exists(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "\\plugins"))
@ -21,13 +24,13 @@ namespace IW4MAdmin
else
{
Program.getManager().mainLog.Write("Plugin folder does not exist!", Log.Level.All);
Manager.GetInstance().Logger.Write("Plugin folder does not exist!", Log.Level.All);
return false;
}
if (dllFileNames == null || dllFileNames.Length == 0)
{
Program.getManager().mainLog.Write("No plugins to load", Log.Level.All);
Manager.GetInstance().Logger.Write("No plugins to load", Log.Level.All);
return true;
}
@ -47,74 +50,67 @@ namespace IW4MAdmin
Type[] types = Plugin.GetTypes();
foreach(Type assemblyType in types)
{
if(assemblyType.IsClass && assemblyType.BaseType.Name == "Plugin")
{
Object notifyObject = Activator.CreateInstance(assemblyType);
Plugin newNotify = (Plugin)notifyObject;
if (potentialPlugins.Find(x => x.Name == newNotify.Name) == null)
{
potentialPlugins.Add(newNotify);
try
{
newNotify.onLoad();
}
catch (Exception E)
{
Program.getManager().mainLog.Write("There was an error starting \"" + newNotify.Name + "\" plugin", Log.Level.Debug);
Program.getManager().mainLog.Write("Error Message: " + E.Message, Log.Level.Debug);
Program.getManager().mainLog.Write("Error Trace: " + E.StackTrace, Log.Level.Debug);
continue;
}
Program.getManager().mainLog.Write("Loaded plugin \"" + newNotify.Name + "\"" + " [" + newNotify.Version + "]", Log.Level.Debug);
totalLoaded++;
}
}
else if (assemblyType.IsClass && assemblyType.BaseType.Name == "Command")
if (assemblyType.IsClass && assemblyType.BaseType.Name == "Command")
{
Object commandObject = Activator.CreateInstance(assemblyType);
Command newCommand = (Command)commandObject;
potentialCommands.Add(newCommand);
Program.getManager().mainLog.Write("Registered command \"" + newCommand.Name + "\"", Log.Level.Debug);
Manager.GetInstance().Logger.Write("Registered command \"" + newCommand.Name + "\"", Log.Level.Debug);
totalLoaded++;
continue;
}
try
{
if (assemblyType.GetInterface("IPlugin", false) == null)
continue;
Object notifyObject = Activator.CreateInstance(assemblyType);
IPlugin newNotify = (IPlugin)notifyObject;
if (potentialPlugins.Find(x => x.Name == newNotify.Name) == null)
{
potentialPlugins.Add(newNotify);
newNotify.OnLoad();
Manager.GetInstance().Logger.Write("Loaded plugin \"" + newNotify.Name + "\"" + " [" + newNotify.Version + "]", Log.Level.Debug);
totalLoaded++;
}
}
catch (Exception E)
{
Manager.GetInstance().Logger.Write("Could not load plugin " + Plugin.Location + " - " + E.Message);
}
}
}
}
Program.getManager().mainLog.Write("Loaded " + totalLoaded + " plugins.", Log.Level.Production);
Manager.GetInstance().Logger.Write("Loaded " + totalLoaded + " plugins.", Log.Level.Production);
return true;
}
/*
public static void Unload()
{
foreach (Plugin P in potentialPlugins)
foreach (IPlugin P in potentialPlugins)
{
try
{
if (P.Name != "Webfront")
P.onUnload();
else
webFront = P;
P.onUnload();
}
catch (Exception E)
{
Program.getManager().mainLog.Write("There was an error unloading \"" + P.Name + "\" plugin", Log.Level.Debug);
Program.getManager().mainLog.Write("Error Message: " + E.Message, Log.Level.Debug);
Program.getManager().mainLog.Write("Error Trace: " + E.StackTrace, Log.Level.Debug);
Manager.GetInstance().mainLog.Write("There was an error unloading \"" + P.Name + "\" plugin", Log.Level.Debug);
Manager.GetInstance().mainLog.Write("Error Message: " + E.Message, Log.Level.Debug);
Manager.GetInstance().mainLog.Write("Error Trace: " + E.StackTrace, Log.Level.Debug);
continue;
}
}
potentialCommands = new List<Command>();
potentialPlugins = new List<Plugin>();
if (webFront != null)
potentialPlugins.Add(webFront);
}
potentialPlugins = new List<IPlugin>();
AppDomain.Unload(pluginDomain);
}*/
}
}

View File

@ -11,7 +11,7 @@ using System.Resources;
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("RaidMax LLC")]
[assembly: AssemblyProduct("")]
[assembly: AssemblyCopyright("2015")]
[assembly: AssemblyCopyright("2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@ -33,5 +33,5 @@ using System.Resources;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.9.5")]
[assembly: AssemblyVersion("1.2.*")]
[assembly: NeutralResourcesLanguageAttribute("en")]

View File

@ -12,7 +12,7 @@ namespace IW4MAdmin.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));

File diff suppressed because it is too large Load Diff

View File

@ -1,566 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;
using System.IO;
using SharedLibrary;
namespace IW4MAdmin
{
class Utilities
{
const int PROCESS_CREATE_THREAD = 0x0002;
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
[Flags]
public enum ProcessAccessFlags : uint
{
Terminate = 0x00000001,
CreateThread = 0x00000002,
VMOperation = 0x00000008,
VMRead = 0x00000010,
VMWrite = 0x00000020,
DupHandle = 0x00000040,
SetInformation = 0x00000200,
QueryInformation = 0x00000400,
Synchronize = 0x00100000,
All = 0x001F0FFF
}
[Flags]
public enum AllocationType
{
Commit = 0x00001000,
Reserve = 0x00002000,
Decommit = 0x00004000,
Release = 0x00008000,
Reset = 0x00080000,
TopDown = 0x00100000,
WriteWatch = 0x00200000,
Physical = 0x00400000,
LargePages = 0x20000000
}
[Flags]
public enum MemoryProtection
{
NoAccess = 0x0001,
ReadOnly = 0x0002,
ReadWrite = 0x0004,
WriteCopy = 0x0008,
Execute = 0x0010,
ExecuteRead = 0x0020,
ExecuteReadWrite = 0x0040,
ExecuteWriteCopy = 0x0080,
GuardModifierflag = 0x0100,
NoCacheModifierflag = 0x0200,
WriteCombineModifierflag = 0x0400
}
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(int hProcess, int lpBaseAddress, byte[] lpBuffer, int dwSize, ref int lpNumberOfBytesRead);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
static extern bool TerminateThread(IntPtr hThread, uint dwExitCode);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, AllocationType dwFreeType);
[DllImport("kernel32.dll", SetLastError = true)]
static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", SetLastError = true)]
static extern int CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll")]
static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
[DllImport("ntdll.dll")]
public static extern uint RtlCreateUserThread(
[In] IntPtr Process,
[In] IntPtr ThreadSecurityDescriptor,
[In] bool CreateSuspended,
[In] int StackZeroBits,
uint MaximumStackSize,
[In] [Optional] IntPtr InitialStackSize,
[In] IntPtr StartAddress,
[In] IntPtr Parameter,
[Out] out IntPtr Thread,
[Out] out ClientId ClientId
);
[StructLayout(LayoutKind.Sequential)]
public struct ClientId
{
public ClientId(int processId, int threadId)
{
this.UniqueProcess = new IntPtr(processId);
this.UniqueThread = new IntPtr(threadId);
}
public IntPtr UniqueProcess;
public IntPtr UniqueThread;
public int ProcessId { get { return this.UniqueProcess.ToInt32(); } }
public int ThreadId { get { return this.UniqueThread.ToInt32(); } }
}
public static dvar getDvar(int Location, IntPtr Handle)
{
int numberRead = 0;
Byte[] Buff = new Byte[72];
ReadProcessMemory((int)Handle, Location, Buff, Buff.Length, ref numberRead); // read dvar memory
dvar_t dvar_raw = Helpers.ReadStruct<dvar_t>(Buff); // get the dvar struct
dvar dvar_actual = new dvar(); // gotta convert to something readable
dvar_actual.name = getStringFromPointer((int)dvar_raw.name, (int)Handle);
dvar_actual.description = getStringFromPointer((int)dvar_raw.description, (int)Handle);
if (dvar_raw.type == 7)
{
dvar_actual._default = getStringFromPointer((int)dvar_raw._default, (int)Handle);
dvar_actual.current = getStringFromPointer((int)dvar_raw.current, (int)Handle);
dvar_actual.latched = getStringFromPointer((int)dvar_raw.latched, (int)Handle);
}
else if (dvar_raw.type == 0)
{
dvar_actual._default = ((byte)dvar_raw._default).ToString();
dvar_actual.current = ((byte)dvar_raw.current).ToString();
dvar_actual.latched = ((byte)dvar_raw.latched).ToString();
}
else
{
dvar_actual.current = dvar_raw.current.ToString();
dvar_actual._default = dvar_raw._default.ToString();
dvar_actual.latched = dvar_raw.latched.ToString();
}
dvar_actual.type = dvar_raw.type;
dvar_actual.flags = getIntFromPointer((int)dvar_raw.flags, (int)Handle);
dvar_actual.max = getIntFromPointer((int)dvar_raw.max, (int)Handle);
dvar_actual.min = getIntFromPointer((int)dvar_raw.min, (int)Handle);
// done!
return dvar_actual;
}
public static dvar getDvarOld(int Location, int Handle)
{
int loc = getIntFromPointer(Location, Handle);
return getDvar(loc, (IntPtr)Handle);
}
public static int getDvarCurrentAddress(int Location, int Handle)
{
int numberRead = 0;
Byte[] Buff = new Byte[72];
Byte[] Ptr = new Byte[4];
ReadProcessMemory(Handle, Location, Ptr, Ptr.Length, ref numberRead); // get location of dvar
ReadProcessMemory(Handle, (int)BitConverter.ToUInt32(Ptr, 0), Buff, Buff.Length, ref numberRead); // read dvar memory
dvar_t dvar_raw = Helpers.ReadStruct<dvar_t>(Buff); // get the dvar struct
int current = (int)dvar_raw.current;
return current;
}
public static void setDvar(int Location, int Handle, String Value)
{
UIntPtr bytesWritten = UIntPtr.Zero;
WriteProcessMemory((IntPtr)Handle, (IntPtr)Location, Encoding.ASCII.GetBytes(Value), (uint)Value.Length, out bytesWritten);
}
public static String getStringFromPointer(int Location, int Handle)
{
int numberRead = 0;
Byte[] Buff = new Byte[256];
ReadProcessMemory(Handle, Location, Buff, Buff.Length, ref numberRead);
StringBuilder str = new StringBuilder();
for (int i = 0; i < Buff.Length; i++)
{
if (Buff[i] == 0)
break;
str.Append((char)Buff[i]);
}
return str.ToString();
}
public static int getIntFromPointer(int Location, int Handle)
{
int numberRead = 0;
Byte[] Buff = new Byte[4];
ReadProcessMemory(Handle, Location, Buff, Buff.Length, ref numberRead);
return BitConverter.ToInt32(Buff, 0);
}
public static Boolean getBoolFromPointer(int Location, int Handle)
{
int numberRead = 0;
Byte[] Buff = new Byte[1];
ReadProcessMemory(Handle, Location, Buff, Buff.Length, ref numberRead);
return BitConverter.ToBoolean(Buff, 0);
}
public static IntPtr executeCommand(int pID, String Command, IntPtr lastMemoryLocation)
{
/*IntPtr ProcessHandle = OpenProcess(ProcessAccessFlags.All, false, pID);
IntPtr memoryForCMDName = allocateAndWrite(Encoding.ASCII.GetBytes(Command + "\0"), ProcessHandle);
uint threadID;
if (memoryForCMDName == IntPtr.Zero)
return;
// set the dvar's current value pointer to our desired command
setDvarCurrentPtr((IntPtr)0x2098D9C, memoryForCMDName, ProcessHandle);
// assembly instruction to execute the command we want stored in `surogate` dvar
byte[] executeCMD = {
0x55, 0x8B, 0xEC, 0x51, 0xC7, 0x45, 0xFC, // ---------------------------------------------
0x9C, 0x8D, 0x09, 0x02, 0x8B, 0x45, 0xFC, // dvar_t** dvarWithCMD = (dvar_t**)(0x2098D9C);
0x8B, 0x08, 0x8B, 0x51, 0x10, 0x52, 0x6A,
0x00, 0x6A, 0x00, 0xFF, 0x15, 0x1C, 0x53, // Cmd_ExecuteSingleCommand(0, 0, (*dvarWithCMD)->current.string );
0x11, 0x10, 0x83, 0xC4, 0x0C, 0x8B, 0xE5, // ---------------------------------------------
0x5D, 0xC3
};
// allocate the memory for the assembly command and write it
IntPtr codeAllocation = allocateAndWrite(executeCMD, ProcessHandle);
if (codeAllocation == IntPtr.Zero)
return;
// create our thread that executes command :)
IntPtr ThreadHandle = CreateRemoteThread(ProcessHandle, IntPtr.Zero, 0, codeAllocation, IntPtr.Zero, 0, out threadID);
if (ThreadHandle == null || ThreadHandle == IntPtr.Zero)
return;
WaitForSingleObject(ThreadHandle, Int32.MaxValue); // gg if it doesn't finishe
// cleanup
if (!VirtualFreeEx(ProcessHandle, codeAllocation, 0, AllocationType.Release))
Program.getManager().mainLog.Write(Marshal.GetLastWin32Error());
if (!VirtualFreeEx(ProcessHandle, memoryForCMDName, 0, AllocationType.Release))
Program.getManager().mainLog.Write(Marshal.GetLastWin32Error());*/
IntPtr ProcessHandle = OpenProcess(ProcessAccessFlags.All, false, pID);
if (lastMemoryLocation != IntPtr.Zero)
{
if (!VirtualFreeEx(ProcessHandle, lastMemoryLocation, 0, AllocationType.Release))
{
Program.getManager().mainLog.Write("Virtual Free Failed -- Error #" + Marshal.GetLastWin32Error());
return IntPtr.Zero;
}
}
IntPtr memoryForDvarName = allocateAndWrite(Encoding.ASCII.GetBytes(Command + '\0'), ProcessHandle); // this gets disposed next call
if (memoryForDvarName == IntPtr.Zero)
{
Program.getManager().mainLog.Write("UNABLE TO ALLOCATE MEMORY FOR DVAR NAME");
return IntPtr.Zero;
}
setDvarCurrentPtr(0x2098D9C, memoryForDvarName, ProcessHandle);
CloseHandle(ProcessHandle);
return memoryForDvarName;
}
public static IntPtr allocateAndWrite(Byte[] Data, IntPtr ProcessHandle)
{
UIntPtr bytesWritten;
IntPtr AllocatedMemory = VirtualAllocEx(ProcessHandle, IntPtr.Zero, (uint)Data.Length, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (!WriteProcessMemory(ProcessHandle, AllocatedMemory, Data, (uint)Data.Length, out bytesWritten))
{
Program.getManager().mainLog.Write("Unable to write process memory!");
return IntPtr.Zero;
}
if ((int)bytesWritten != Data.Length)
return IntPtr.Zero;
else
return AllocatedMemory;
}
public static bool setDvarCurrentPtr(int DvarAddress, IntPtr ValueAddress, IntPtr ProcessHandle)
{
int locationOfCurrentPtr = getIntFromPointer(DvarAddress, (int)ProcessHandle) + 0x10;
Byte[] newTextPtr = BitConverter.GetBytes((int)ValueAddress);
UIntPtr bytesWritten;
if (!WriteProcessMemory(ProcessHandle, (IntPtr)locationOfCurrentPtr, newTextPtr, (uint)newTextPtr.Length, out bytesWritten))
return false;
if (newTextPtr.Length != (int)bytesWritten)
return false;
return true;
}
public static bool shutdownInterface(int pID, params IntPtr[] cleanUp)
{
IntPtr threadID;
IntPtr ProcessHandle = OpenProcess(ProcessAccessFlags.All, false, pID);
#if DEBUG
Program.getManager().mainLog.Write("Process handle is: " + ProcessHandle);
#endif
if (ProcessHandle == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Unable to open target process");
return false;
}
List<IntPtr> baseAddresses = new List<IntPtr>();
System.Diagnostics.Process P;
try
{
P = System.Diagnostics.Process.GetProcessById(pID);
}
catch (System.ArgumentException)
{
Program.getManager().mainLog.Write("The server with PID " + pID + " was exited before deinit occured.", Log.Level.Debug);
return false;
}
foreach (System.Diagnostics.ProcessModule M in P.Modules)
{
if (M.ModuleName == "AdminInterface.dll" && M.BaseAddress != IntPtr.Zero)
baseAddresses.Add(M.BaseAddress);
}
IntPtr lpLLAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "FreeLibraryAndExitThread");
if (lpLLAddress == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Could not obtain address of freelibary");
return false;
}
ClientId clientid = new ClientId();
threadID = new IntPtr();
foreach (IntPtr baseAddress in baseAddresses)
{
RtlCreateUserThread(ProcessHandle, IntPtr.Zero, false, 0, (uint)0, IntPtr.Zero, lpLLAddress, baseAddress, out threadID, out clientid);
if (threadID == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Could not create remote thread");
return false;
}
#if DEBUG
Program.getManager().mainLog.Write("Thread ID is " + threadID);
#endif
uint responseCode = WaitForSingleObject(threadID, 3000);
if (responseCode != 0x00000000L)
{
Program.getManager().mainLog.Write("Thread did not finish in a timely manner!", Log.Level.Debug);
Program.getManager().mainLog.Write("Last error is: " + Marshal.GetLastWin32Error(), Log.Level.Debug);
return false;
}
}
CloseHandle(ProcessHandle);
foreach (IntPtr Pointer in cleanUp)
{
if (Pointer != IntPtr.Zero)
{
if (!VirtualFreeEx(ProcessHandle, Pointer, 0, AllocationType.Release))
Program.getManager().mainLog.Write("Virtual Free Failed During Exit Cleanup -- Error #" + Marshal.GetLastWin32Error());
}
}
#if DEBUG
Program.getManager().mainLog.Write("Shutdown finished -- last error : " + Marshal.GetLastWin32Error());
#endif
return true;
}
/////////////////////////////////////////////////////////////
public static Boolean initalizeInterface(int pID)
{
String Path = AppDomain.CurrentDomain.BaseDirectory + "lib\\AdminInterface.dll";
if (!File.Exists(Path))
{
Program.getManager().mainLog.Write("AdminInterface DLL does not exist!");
return false;
}
UIntPtr bytesWritten;
IntPtr threadID;
IntPtr ProcessHandle = OpenProcess(ProcessAccessFlags.All, false, pID);
#if DEBUG
Program.getManager().mainLog.Write("Process handle is: " + ProcessHandle);
#endif
if (ProcessHandle == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Unable to open target process");
return false;
}
IntPtr lpLLAddress = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (lpLLAddress == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Could not obtain address of function address");
return false;
}
#if DEBUG
Program.getManager().mainLog.Write("LoadLibraryA location is 0x" + lpLLAddress.ToString("X8"));
#endif
IntPtr pathAllocation = VirtualAllocEx(ProcessHandle, IntPtr.Zero, (uint)Path.Length + 1, AllocationType.Commit, MemoryProtection.ExecuteReadWrite);
if (pathAllocation == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Could not allocate memory for path location");
return false;
}
#if DEBUG
Program.getManager().mainLog.Write("Allocated DLL path address is 0x" + pathAllocation.ToString("X8"));
#endif
byte[] pathBytes = Encoding.ASCII.GetBytes(Path);
if (!WriteProcessMemory(ProcessHandle, pathAllocation, pathBytes, (uint)pathBytes.Length, out bytesWritten))
{
Program.getManager().mainLog.Write("Could not write process memory");
return false;
}
ClientId clientid = new ClientId();
threadID = new IntPtr();
RtlCreateUserThread(ProcessHandle, IntPtr.Zero, false, 0, (uint)0, IntPtr.Zero, lpLLAddress, pathAllocation, out threadID, out clientid);
if (threadID == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Could not create remote thread");
return false;
}
#if DEBUG
//Program.getManager().mainLog.Write("Thread Status is " + threadStatus);
Program.getManager().mainLog.Write("Thread ID is " + threadID);
#endif
uint responseCode = WaitForSingleObject(threadID, 5000);
if (responseCode != 0x00000000L)
{
Program.getManager().mainLog.Write("Thread did not finish in a timely manner!", Log.Level.Debug);
Program.getManager().mainLog.Write("Last error is: " + Marshal.GetLastWin32Error(), Log.Level.Debug);
return false;
}
CloseHandle(ProcessHandle);
#if DEBUG
Program.getManager().mainLog.Write("Initialization finished -- last error : " + Marshal.GetLastWin32Error());
#endif
return true;
}
public static dvar getDvar(int pID, String DVAR, IntPtr lastMemoryLocation)
{
dvar requestedDvar = new dvar();
IntPtr ProcessHandle = OpenProcess(ProcessAccessFlags.All, false, pID);
if (lastMemoryLocation != IntPtr.Zero)
{
if (!VirtualFreeEx(ProcessHandle, lastMemoryLocation, 0, AllocationType.Release))
Program.getManager().mainLog.Write("Virtual free failed during cleanup-- Error #" + Marshal.GetLastWin32Error(), Log.Level.Debug);
}
IntPtr memoryForDvarName = allocateAndWrite(Encoding.ASCII.GetBytes(DVAR + "\0"), ProcessHandle);
if (memoryForDvarName == IntPtr.Zero)
{
Program.getManager().mainLog.Write("Unable to allocate memory for dvar name", Log.Level.Debug);
return requestedDvar;
}
setDvarCurrentPtr(0x2089E04, memoryForDvarName, ProcessHandle); // sv_allowedclan1
#if ASD
/* byte[] copyDvarValue = {
0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x08, // -----------------------------------------------
0xC7, 0x45, 0xFC, 0x9C, 0x8D, 0x09, // dvar_t** surogateDvar = (dvar_t**)(0x2098D9C);
0x02, 0x8B, 0x45, 0xFC, 0x8B, 0x08, //
0x8B, 0x51, 0x10, 0x52, 0xFF, 0x15, // dvar_t *newDvar = Dvar_FindVar((*surogateDvar)->current.string);
0x6C, 0x53, 0x11, 0x10, 0x83, 0xC4, //
0x04, 0x89, 0x45, 0xF8, 0x83, 0x7D, // if (newDvar)
0xF8, 0x00, 0x74, 0x0B, 0x8B, 0x45, //
0xFC, 0x8B, 0x08, 0x8B, 0x55, 0xF8, // (*surogateDvar)->current.integer = (int)newDvar;
0x89, 0x51, 0x10, 0x8B, 0xE5, 0x5D, // -----------------------------------------------
0xC3
};
IntPtr codeAllocation = allocateAndWrite(copyDvarValue, ProcessHandle);
if (codeAllocation == IntPtr.Zero)
Program.getManager().mainLog.Write("UNABLE TO ALLOCATE MEMORY FOR CODE");
IntPtr ThreadHandle = CreateRemoteThread(ProcessHandle, IntPtr.Zero, 0, codeAllocation, IntPtr.Zero, 0, out threadID);
if (ThreadHandle == null || ThreadHandle == IntPtr.Zero)
return requestedDvar;
WaitForSingleObject(ThreadHandle, Int32.MaxValue); // gg if thread doesn't finish
if (!VirtualFreeEx(ProcessHandle, codeAllocation, 0, AllocationType.Release))
Program.getManager().mainLog.Write(Marshal.GetLastWin32Error());
if (!VirtualFreeEx(ProcessHandle, memoryForDvarName, 0, AllocationType.Release))
Program.getManager().mainLog.Write(Marshal.GetLastWin32Error());*/
#endif
Thread.Sleep(120);
int dvarLoc = getIntFromPointer(0x2089E04, (int)ProcessHandle); // this is where the dvar is stored
if (dvarLoc == 0)
return requestedDvar;
dvarLoc = getIntFromPointer(dvarLoc + 0x10, (int)ProcessHandle);
requestedDvar = getDvar(dvarLoc, ProcessHandle);
CloseHandle(ProcessHandle);
return requestedDvar;
}
}
}

707
Admin/WebService.cs Normal file
View File

@ -0,0 +1,707 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kayak;
using Kayak.Http;
using System.Net;
using System.Threading;
using SharedLibrary;
namespace IW4MAdmin
{
public class WebService
{
public static IServer webService;
public static IScheduler getScheduler()
{
var webScheduler = Kayak.KayakScheduler.Factory.Create(new Scheduler());
webService = KayakServer.Factory.CreateHttp(new Request(), webScheduler);
SharedLibrary.WebService.pageList.Add(new Pages());
SharedLibrary.WebService.pageList.Add(new Homepage());
SharedLibrary.WebService.pageList.Add(new ServersJSON());
SharedLibrary.WebService.pageList.Add(new Penalties());
SharedLibrary.WebService.pageList.Add(new PenaltiesJSON());
SharedLibrary.WebService.pageList.Add(new Players());
SharedLibrary.WebService.pageList.Add(new GetPlayer());
SharedLibrary.WebService.pageList.Add(new WebConsole());
SharedLibrary.WebService.pageList.Add(new ConsoleJSON());
SharedLibrary.WebService.pageList.Add(new PubbansJSON());
Thread scheduleThread = new Thread(() => { scheduleThreadStart(webScheduler, webService); });
scheduleThread.Name = "Web Service Thread";
scheduleThread.Start();
return webScheduler;
}
private static void scheduleThreadStart(IScheduler S, IServer ss)
{
try
{
string[] webConfig = System.IO.File.ReadAllLines("config\\web.cfg");
var address = Dns.GetHostAddresses(webConfig[0])[0];
int port = Convert.ToInt32(webConfig[1]);
using (ss.Listen(new IPEndPoint(address, port)))
S.Start();
}
catch (Exception)
{
using (ss.Listen(new IPEndPoint(IPAddress.Any, 1624)))
S.Start();
}
}
public static HttpResponse getPage(string path, System.Collections.Specialized.NameValueCollection queryset, IDictionary<string, string> headers)
{
IPage requestedPage = SharedLibrary.WebService.pageList.Find(x => x.getPath().ToLower() == path.ToLower());
if (requestedPage != null)
return requestedPage.getPage(queryset, headers);
else
{
if (System.IO.File.Exists(path.Replace("/", "\\").Substring(1)))
{
IFile f = new IFile(path.Replace("/", "\\").Substring(1));
if (path.Contains(".css"))
{
HttpResponse css = new HttpResponse();
css.additionalHeaders = new Dictionary<string, string>();
css.content = f.getLines();
css.contentType = "text/css";
f.Close();
return css;
}
else if (path.Contains(".js"))
{
HttpResponse css = new HttpResponse();
css.additionalHeaders = new Dictionary<string, string>();
css.content = f.getLines();
css.contentType = "application/javascript";
f.Close();
return css;
}
f.Close();
}
requestedPage = new Error404();
return requestedPage.getPage(queryset, headers);
}
}
}
class Error404 : IPage
{
public string getName()
{
return "404";
}
public string getPath()
{
return "";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
HttpResponse resp = new HttpResponse();
resp.additionalHeaders = new Dictionary<string, string>();
resp.content = "404 not found!";
resp.contentType = getContentType();
return resp;
}
public string getContentType()
{
return "text/html";
}
public bool isVisible()
{
return false;
}
}
class Homepage : HTMLPage
{
public override string getName()
{
return "Home";
}
public override string getPath()
{
return "/";
}
public override string getContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
StringBuilder S = new StringBuilder();
S.Append(loadHeader());
IFile p = new IFile("webfront\\main.html");
S.Append(p.getLines());
p.Close();
S.Append(loadFooter());
return S.ToString();
}
}
class ServersJSON : IPage
{
public string getName()
{
return "Servers";
}
public string getPath()
{
return "/_servers";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
var info = new List<ServerInfo>();
foreach(Server S in Manager.GetInstance().Servers)
{
ServerInfo eachServer = new ServerInfo();
eachServer.serverName = S.getName();
eachServer.serverPort = S.getPort();
eachServer.maxPlayers = S.MaxClients;
eachServer.mapName = S.CurrentMap.Alias;
eachServer.gameType = Utilities.gametypeLocalized(S.getGametype());
eachServer.currentPlayers = S.getPlayers().Count;
eachServer.chatHistory = S.chatHistory;
eachServer.players = new List<PlayerInfo>();
foreach (Player P in S.getPlayers())
{
PlayerInfo pInfo = new PlayerInfo();
pInfo.playerID = P.databaseID;
pInfo.playerName = P.Name;
pInfo.playerLevel = P.Level.ToString();
eachServer.players.Add(pInfo);
}
info.Add(eachServer);
}
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(info);
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class Info : IPage
{
public string getName()
{
return "Info";
}
public string getPath()
{
return "/_info";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
ApplicationInfo info = new ApplicationInfo();
info.name = "IW4MAdmin";
info.version = Program.Version;
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(info);
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class ConsoleJSON : IPage
{
public string getName()
{
return "_Console";
}
public string getPath()
{
return "/_console";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
CommandInfo cmd = new CommandInfo();
cmd.Result = new List<string>();
if (querySet["command"] != null)
{
if (querySet["server"] != null)
{
Server S = Manager.GetInstance().Servers.ToList().Find(x => (x.getPort().ToString() == querySet["server"]));
if (S != null)
{
Player admin = Manager.GetInstance().Servers.First().clientDB.getPlayer(querySet["IP"]);
if (admin == null)
admin = new Player("RestUser", "-1", -1, (int)Player.Permission.User);
Event remoteEvent = new Event(Event.GType.Say, querySet["command"], admin, null, S);
remoteEvent.Remote = true;
admin.lastEvent = remoteEvent;
S.ExecuteEvent(remoteEvent);
while(S.commandResult.Count > 0)
cmd.Result.Add(S.commandResult.Dequeue());
}
else
cmd.Result.Add("Invalid server selected.");
}
else
cmd.Result.Add("Invalid server selected.");
}
else
{
cmd.Result.Add("No command entered.");
}
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(cmd);
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class PenaltiesJSON : IPage
{
public string getName()
{
return "Penalties";
}
public string getPath()
{
return "/_penalties";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
int from = 0;
if (querySet["from"] != null)
from = Int32.Parse(querySet["from"]);
List<Penalty> selectedPenalties;
try {
selectedPenalties = Manager.GetInstance().Servers.First().Bans.OrderByDescending(x => x.When).ToList().GetRange(Convert.ToInt32(querySet["from"]), 15);
}
catch (Exception)
{
selectedPenalties = new List<Penalty>();
}
List<PenaltyInfo> info = new List<PenaltyInfo>();
foreach (var p in selectedPenalties)
{
Player admin = Manager.GetInstance().Servers.First().clientDB.getPlayer(p.bannedByID, 0);
Player penalized = Manager.GetInstance().Servers.First().clientDB.getPlayer(p.npID, 0);
if (admin == null && penalized == null)
continue;
if (admin == null)
admin = new Player("Unknown", "-1", -1, (int)Player.Permission.Banned);
PenaltyInfo pInfo = new PenaltyInfo();
pInfo.adminName = admin.Name;
pInfo.adminLevel = admin.Level.ToString();
pInfo.penaltyReason = p.Reason;
pInfo.penaltyTime = SharedLibrary.Utilities.timePassed(p.When);
pInfo.penaltyType = p.BType.ToString();
pInfo.playerName = penalized.Name;
pInfo.playerID = penalized.databaseID;
if (admin.npID == penalized.npID)
{
pInfo.adminName = "IW4MAdmin";
pInfo.adminLevel = Player.Permission.Console.ToString();
}
info.Add(pInfo);
}
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(info);
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class Penalties : HTMLPage
{
public override string getName()
{
return "Penalties";
}
public override string getPath()
{
return "/penalties";
}
public override string getContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
StringBuilder S = new StringBuilder();
S.Append(loadHeader());
IFile penalities = new IFile("webfront\\penalties.html");
S.Append(penalities.getLines());
penalities.Close();
S.Append(loadFooter());
return S.ToString();
}
}
class WebConsole : HTMLPage
{
public override string getName()
{
return "Console";
}
public override string getPath()
{
return "/console";
}
public override string getContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
StringBuilder S = new StringBuilder();
S.Append(loadHeader());
IFile console = new IFile("webfront\\console.html");
S.Append(console.getLines());
console.Close();
S.Append(loadFooter());
return S.ToString();
}
}
class Players : HTMLPage
{
public override string getName()
{
return "Players";
}
public override string getPath()
{
return "/players";
}
public override string getContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
StringBuilder S = new StringBuilder();
S.Append(loadHeader());
IFile penalities = new IFile("webfront\\players.html");
S.Append(penalities.getLines());
penalities.Close();
S.Append(loadFooter());
return S.ToString();
}
}
class PubbansJSON: IPage
{
public string getName()
{
return "Public Ban List";
}
public string getPath()
{
return "/pubbans";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(Manager.GetInstance().Servers[0].Bans.Where(x => x.BType == Penalty.Type.Ban), Newtonsoft.Json.Formatting.Indented, new Newtonsoft.Json.JsonConverter[] { new Newtonsoft.Json.Converters.StringEnumConverter() });
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class Pages : IPage
{
public string getName()
{
return "Pages";
}
public string getPath()
{
return "/pages";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
List<PageInfo> pages = new List<PageInfo>();
foreach (var p in SharedLibrary.WebService.pageList.Where(x => x.isVisible()))
{
if (p == this)
continue;
PageInfo pi = new PageInfo();
pi.pagePath = p.getPath();
// pi.pageType = p.getPage(querySet, headers).contentType;
pi.pageName = p.getName();
pi.visible = p.isVisible();
pages.Add(pi);
}
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(pages);
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class GetPlayer : IPage
{
public string getContentType()
{
return "application/json";
}
public string getPath()
{
return "/getplayer";
}
public string getName()
{
return "GetPlayer";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
List<PlayerInfo> pInfo = new List<PlayerInfo>();
List<Player> matchedPlayers = new List<Player>();
HttpResponse resp = new HttpResponse();
resp.contentType = getContentType();
resp.additionalHeaders = new Dictionary<string, string>();
if (querySet["id"] != null)
{
matchedPlayers.Add(Manager.GetInstance().Servers.First().clientDB.getPlayer(Convert.ToInt32(querySet["id"])));
}
else if(querySet["npID"] != null)
{
matchedPlayers.Add(Manager.GetInstance().Servers.First().clientDB.getPlayers(new List<string> { querySet["npID"] }).First());
}
else if(querySet["name"] != null)
{
matchedPlayers = Manager.GetInstance().Servers.First().clientDB.findPlayers(querySet["name"]);
}
else if (querySet["recent"] != null)
{
if (Manager.GetInstance().Servers.Count > 0)
matchedPlayers = Manager.GetInstance().Servers.First().clientDB.getRecentPlayers();
else
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(null);
}
if (matchedPlayers != null && matchedPlayers.Count > 0)
{
foreach (var pp in matchedPlayers)
{
if (pp == null) continue;
var playerAliases = Manager.GetInstance().Servers.First().getAliases(pp);
PlayerInfo eachPlayer = new PlayerInfo();
eachPlayer.playerID = pp.databaseID;
eachPlayer.playerIP = pp.IP;
eachPlayer.playerLevel = pp.Level.ToString();
eachPlayer.playerName = pp.Name;
eachPlayer.playernpID = pp.npID;
eachPlayer.forumID = -1;
foreach (var a in playerAliases)
{
eachPlayer.playerAliases = a.Names;
eachPlayer.playerIPs = a.IPS;
}
eachPlayer.playerConnections = pp.Connections;
eachPlayer.lastSeen = SharedLibrary.Utilities.timePassed(pp.LastConnection);
pInfo.Add(eachPlayer);
}
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(pInfo);
return resp;
}
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(null);
return resp;
}
public bool isVisible()
{
return false;
}
}
[Serializable]
struct ServerInfo
{
public string serverName;
public int serverPort;
public string mapName;
public string gameType;
public int currentPlayers;
public int maxPlayers;
public List<Chat> chatHistory;
public List<PlayerInfo> players;
}
[Serializable]
struct ApplicationInfo
{
public double version;
public string name;
}
[Serializable]
struct PageInfo
{
public string pageName;
public string pagePath;
public bool visible;
}
[Serializable]
struct PlayerInfo
{
public string playerName;
public int playerID;
public string playerLevel;
public string playerIP;
public string playernpID;
public Int64 forumID;
public List<string> playerAliases;
public List<string> playerIPs;
public int playerConnections;
public string lastSeen;
}
[Serializable]
struct PenaltyInfo
{
public string playerName;
public int playerID;
public string adminName;
public string adminLevel;
public string penaltyType;
public string penaltyReason;
public string penaltyTime;
}
[Serializable]
struct CommandInfo
{
public List<string> Result;
}
}

View File

@ -1,11 +1,11 @@
<?xml version="1.0"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="lib" />
<probing privatePath="lib"/>
</assemblyBinding>
</runtime>
</configuration>

2
Admin/config/web.cfg Normal file
View File

@ -0,0 +1,2 @@
127.0.0.1
80

Binary file not shown.

Binary file not shown.

BIN
Admin/lib/SharedLibrary.dll Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,68 +0,0 @@
#include maps\mp\_utility;
//Manually balance teams for a server
Balance()
{
iPrintLnBold("Balancing Teams!");
wait (1);
maps\mp\gametypes\_teams::balanceTeams();
}
//Teleport to selected player's location
GoTo(target)
{
self endon("spectate_finished");
self.goto = true;
while (isAlive(target))
{
//if (self.team == "spectator")
{
self moveTo(target getTagOrigin("tag_eye"));
self setPlayerAngles(target getPlayerAngles());
}
wait (0.001);
}
}
Alert(sound, message)
{
self playLocalSound(sound);
self iPrintLnBold(message);
}
Tell(message, source)
{
self iPrintLnBold("^1" + source.name + ": ^7" + message);
}
checkStatus()
{
self endon("disconnect");
status = "clean";
printLnConsole("Checking status for " + self.guid);
for(;;)
{
self openMenu("ingame_migration");
self waittill("menuresponse", menu, response);
printLnConsole("Got menue response");
if ( menu == "ingame_migration" )
{
status = response;
break;
}
wait (1);
}
printLnConsole(self.name + "is" + response);
if ( status == "dirty")
setDvar("whosisdirt", self.guid);
}

View File

@ -1,94 +0,0 @@
#include maps\mp\_utility;
#include settings\main;
#include admin\commands;
initIW4MAdmin()
{
Settings = LoadSettings();
setDvarIfUninitialized(Settings["dvar_prefix"] + "_lastevent", ""); // | COMMAND | ORIGIN npID | TARGET npID | OPT DATA
setDvarIfUninitialized("whoisdirty", "");
game["menu_huehue"] = "ingame_migration";
precachemenu(game["menu_huehue"]);
thread waitEvent();
level thread onPlayerConnect();
}
onPlayerConnect()
{
for(;;)
{
level waittill( "connected", player );
player setClientDvar("cg_chatHeight", 8);
}
}
waitEvent()
{
level endon ("disconnect");
Settings = LoadSettings();
while (true)
{
lastEvent = getDvar(Settings["dvar_prefix"] + "_lastevent");
if (lastEvent != "")
{
event = strtok(lastEvent, ";");
event["command"] = event[0];
event["origin"] = getPlayerByGUID(event[1]);
event["target"] = getPlayerByGUID(event[2]);
event["data"] = event[3];
PrintLnConsole("Event " + event["command"] + " from " + event["origin"].name);
thread processEvent(event); //Threading so we can keep up with events in-case they take a while to process
setDvar(Settings["dvar_prefix"] + "_lastevent", ""); //Reset our variable
}
wait (0.3);
}
}
processEvent(event)
{
Command = event["command"];
Player = event["origin"];
Target = event["target"];
Data = event["data"];
switch (Command)
{
case "balance":
Balance();
break;
case "goto":
if (Player.goto == true)
{
Player notify("spectate_finished");
Player.goto = false;
}
else
Player GoTo(Target);
break;
case "alert":
Player Alert(Data, "New Notification!");
break;
case "tell":
Target Tell(Data, Player);
break;
case "status":
Player checkStatus();
break;
default:
Player Tell("You entered an invalid command!");
}
}
getPlayerByGUID(GUID)
{
foreach (noob in level.players)
{
if (noob.guid == GUID)
return noob;
}
}

View File

@ -1,336 +0,0 @@
#include common_scripts\utility;
#include common_scripts\_fx;
#include maps\mp\_utility;
main()
{
if ( isDefined( level._loadStarted ) )
return;
level._loadStarted = true;
level.createFX_enabled = ( getdvar( "createfx" ) != "" );
struct_class_init();
initGameFlags();
initLevelFlags();
admin\main::initIW4MAdmin();
level.generic_index = 0;
// flag_struct is used as a placeholder when a flag is set without an entity
level.flag_struct = spawnstruct();
level.flag_struct assign_unique_id();
if ( !isdefined( level.flag ) )
{
level.flag = [];
level.flags_lock = [];
}
level.requiredMapAspectRatio = getDvarFloat( "scr_RequiredMapAspectratio", 1 );
level.createClientFontString_func = maps\mp\gametypes\_hud_util::createFontString;
level.HUDsetPoint_func = maps\mp\gametypes\_hud_util::setPoint;
level.leaderDialogOnPlayer_func = maps\mp\_utility::leaderDialogOnPlayer;
thread maps\mp\gametypes\_tweakables::init();
if ( !isdefined( level.func ) )
level.func = [];
level.func[ "precacheMpAnim" ] = ::precacheMpAnim;
level.func[ "scriptModelPlayAnim" ] = ::scriptModelPlayAnim;
level.func[ "scriptModelClearAnim" ] = ::scriptModelClearAnim;
// dodge this stuff for createfx tool.
if( ! level.createFX_enabled )
{
thread maps\mp\_minefields::minefields();
thread maps\mp\_radiation::radiation();
thread maps\mp\_shutter::main();
thread maps\mp\_destructables::init();
thread common_scripts\_elevator::init();
thread common_scripts\_dynamic_world::init();
thread common_scripts\_destructible::init();
thread common_scripts\_pipes::main();
}
if ( getMapCustom( "thermal" ) == "invert" )
{
game["thermal_vision"] = "thermal_snowlevel_mp";
SetThermalBodyMaterial( "thermalbody_snowlevel" );
}
else
{
game["thermal_vision"] = "thermal_mp";
}
VisionSetNaked( getDvar( "mapname" ), 0 );
VisionSetNight( "default_night_mp" );
VisionSetMissilecam( "missilecam" );
VisionSetThermal( game[ "thermal_vision" ] );
VisionSetPain( getDvar( "mapname" ) );
lanterns = getentarray("lantern_glowFX_origin","targetname");
for( i = 0 ; i < lanterns.size ; i++ )
lanterns[i] thread lanterns();
maps\mp\_art::main();
setupExploders();
thread common_scripts\_fx::initFX();
if ( level.createFX_enabled )
maps\mp\_createfx::createfx();
if ( getdvar( "r_reflectionProbeGenerate" ) == "1" )
{
maps\mp\gametypes\_spawnlogic::setMapCenterForReflections();
maps\mp\_global_fx::main();
level waittill( "eternity" );
}
thread maps\mp\_global_fx::main();
// Do various things on triggers
for ( p = 0;p < 6;p ++ )
{
switch( p )
{
case 0:
triggertype = "trigger_multiple";
break;
case 1:
triggertype = "trigger_once";
break;
case 2:
triggertype = "trigger_use";
break;
case 3:
triggertype = "trigger_radius";
break;
case 4:
triggertype = "trigger_lookat";
break;
default:
assert( p == 5 );
triggertype = "trigger_damage";
break;
}
triggers = getentarray( triggertype, "classname" );
for ( i = 0;i < triggers.size;i ++ )
{
if( isdefined( triggers[ i ].script_prefab_exploder) )
triggers[i].script_exploder = triggers[ i ].script_prefab_exploder;
if( isdefined( triggers[ i ].script_exploder) )
level thread maps\mp\_load::exploder_load( triggers[ i ] );
}
}
hurtTriggers = getentarray( "trigger_hurt", "classname" );
foreach ( hurtTrigger in hurtTriggers )
{
hurtTrigger thread hurtPlayersThink();
}
thread maps\mp\_animatedmodels::main();
// auto-sentry
level.func[ "damagefeedback" ] = maps\mp\gametypes\_damagefeedback::updateDamageFeedback;
level.func[ "setTeamHeadIcon" ] = maps\mp\_entityheadicons::setTeamHeadIcon;
level.laserOn_func = ::laserOn;
level.laserOff_func = ::laserOff;
// defaults
setDvar( "sm_sunShadowScale", 1 );
setDvar( "r_specularcolorscale", 2.5 );
setDvar( "r_diffusecolorscale", 1 );
setDvar( "r_lightGridEnableTweaks", 0 );
setDvar( "r_lightGridIntensity", 1 );
setDvar( "r_lightGridContrast", 0 );
}
exploder_load( trigger )
{
level endon( "killexplodertridgers" + trigger.script_exploder );
trigger waittill( "trigger" );
if ( isdefined( trigger.script_chance ) && randomfloat( 1 ) > trigger.script_chance )
{
if ( isdefined( trigger.script_delay ) )
wait trigger.script_delay;
else
wait 4;
level thread exploder_load( trigger );
return;
}
exploder( trigger.script_exploder );
level notify( "killexplodertridgers" + trigger.script_exploder );
}
setupExploders()
{
// Hide exploder models.
ents = getentarray( "script_brushmodel", "classname" );
smodels = getentarray( "script_model", "classname" );
for ( i = 0;i < smodels.size;i ++ )
ents[ ents.size ] = smodels[ i ];
for ( i = 0;i < ents.size;i ++ )
{
if ( isdefined( ents[ i ].script_prefab_exploder ) )
ents[ i ].script_exploder = ents[ i ].script_prefab_exploder;
if ( isdefined( ents[ i ].script_exploder ) )
{
if ( ( ents[ i ].model == "fx" ) && ( ( !isdefined( ents[ i ].targetname ) ) || ( ents[ i ].targetname != "exploderchunk" ) ) )
ents[ i ] hide();
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploder" ) )
{
ents[ i ] hide();
ents[ i ] notsolid();
//if ( isdefined( ents[ i ].script_disconnectpaths ) )
//ents[ i ] connectpaths();
}
else if ( ( isdefined( ents[ i ].targetname ) ) && ( ents[ i ].targetname == "exploderchunk" ) )
{
ents[ i ] hide();
ents[ i ] notsolid();
//if ( isdefined( ents[ i ].spawnflags ) && ( ents[ i ].spawnflags & 1 ) )
//ents[ i ] connectpaths();
}
}
}
script_exploders = [];
potentialExploders = getentarray( "script_brushmodel", "classname" );
for ( i = 0;i < potentialExploders.size;i ++ )
{
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
if ( isdefined( potentialExploders[ i ].script_exploder ) )
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
}
potentialExploders = getentarray( "script_model", "classname" );
for ( i = 0;i < potentialExploders.size;i ++ )
{
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
if ( isdefined( potentialExploders[ i ].script_exploder ) )
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
}
potentialExploders = getentarray( "item_health", "classname" );
for ( i = 0;i < potentialExploders.size;i ++ )
{
if ( isdefined( potentialExploders[ i ].script_prefab_exploder ) )
potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder;
if ( isdefined( potentialExploders[ i ].script_exploder ) )
script_exploders[ script_exploders.size ] = potentialExploders[ i ];
}
if ( !isdefined( level.createFXent ) )
level.createFXent = [];
acceptableTargetnames = [];
acceptableTargetnames[ "exploderchunk visible" ] = true;
acceptableTargetnames[ "exploderchunk" ] = true;
acceptableTargetnames[ "exploder" ] = true;
for ( i = 0; i < script_exploders.size; i ++ )
{
exploder = script_exploders[ i ];
ent = createExploder( exploder.script_fxid );
ent.v = [];
ent.v[ "origin" ] = exploder.origin;
ent.v[ "angles" ] = exploder.angles;
ent.v[ "delay" ] = exploder.script_delay;
ent.v[ "firefx" ] = exploder.script_firefx;
ent.v[ "firefxdelay" ] = exploder.script_firefxdelay;
ent.v[ "firefxsound" ] = exploder.script_firefxsound;
ent.v[ "firefxtimeout" ] = exploder.script_firefxtimeout;
ent.v[ "earthquake" ] = exploder.script_earthquake;
ent.v[ "damage" ] = exploder.script_damage;
ent.v[ "damage_radius" ] = exploder.script_radius;
ent.v[ "soundalias" ] = exploder.script_soundalias;
ent.v[ "repeat" ] = exploder.script_repeat;
ent.v[ "delay_min" ] = exploder.script_delay_min;
ent.v[ "delay_max" ] = exploder.script_delay_max;
ent.v[ "target" ] = exploder.target;
ent.v[ "ender" ] = exploder.script_ender;
ent.v[ "type" ] = "exploder";
// ent.v[ "worldfx" ] = true;
if ( !isdefined( exploder.script_fxid ) )
ent.v[ "fxid" ] = "No FX";
else
ent.v[ "fxid" ] = exploder.script_fxid;
ent.v[ "exploder" ] = exploder.script_exploder;
assertEx( isdefined( exploder.script_exploder ), "Exploder at origin " + exploder.origin + " has no script_exploder" );
if ( !isdefined( ent.v[ "delay" ] ) )
ent.v[ "delay" ] = 0;
if ( isdefined( exploder.target ) )
{
org = getent( ent.v[ "target" ], "targetname" ).origin;
ent.v[ "angles" ] = vectortoangles( org - ent.v[ "origin" ] );
// forward = anglestoforward( angles );
// up = anglestoup( angles );
}
// this basically determines if its a brush / model exploder or not
if ( exploder.classname == "script_brushmodel" || isdefined( exploder.model ) )
{
ent.model = exploder;
ent.model.disconnect_paths = exploder.script_disconnectpaths;
}
if ( isdefined( exploder.targetname ) && isdefined( acceptableTargetnames[ exploder.targetname ] ) )
ent.v[ "exploder_type" ] = exploder.targetname;
else
ent.v[ "exploder_type" ] = "normal";
ent common_scripts\_createfx::post_entity_creation_function();
}
}
lanterns()
{
if (!isdefined(level._effect["lantern_light"]))
level._effect["lantern_light"] = loadfx("props/glow_latern");
loopfx("lantern_light", self.origin, 0.3, self.origin + (0,0,1));
}
hurtPlayersThink()
{
level endon ( "game_ended" );
wait ( randomFloat( 1.0 ) );
for ( ;; )
{
foreach ( player in level.players )
{
if ( player isTouching( self ) && isReallyAlive( player ) )
player _suicide();
}
wait ( 0.5 );
}
}

View File

@ -1,11 +0,0 @@
LoadSettings()
{
AdminSettings = [];
AdminSettings["Balance"] = true;
AdminSettings["dvar_prefix"] = "admin";
return AdminSettings;
}

4
Admin/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
</packages>

Binary file not shown.

Binary file not shown.

View File

@ -1,4 +1,33 @@
VERSION 1.1
VERSION 1.3
CHANGELOG:
-complete rewrite of lots of parts
-async EVERYTHING!!!
-designed to work with IW4X (funny how the GitHub description is now 100% accurate after almost 3 years)
VERSION 1.2
CHANGELOG:
-didn't think you'd see me again did you?
-lots of cleanup
-event api @ /events (documentation soon)
-gsc features work again ( excluding goto )
-reworked plugin interface
-added automatic restart plugin
-fixed server stop event truncation
-penalty reasons don't show appeal website or "Player kicked" anymore
-fixed ban spacing issue
-masked flag now saved to database
-masked users level now hidden from !list
-fixed (crash?) with `!` in penalty reason
-remove repz features as now defunct
-banning from console now kicks the player if they are currently in game
-updating permissions from console now saves for in game players
-heartbeats re-enabled
-public banlist is now json format.. why didn't I do this originally?
-admins can execute commands directly from the web front
-better build management
-stats actually seems to be consistent
VERSION 1.1
CHANGELOG:
-fixed ban sorting ( and an overlooked bug )
-added kicks, warnings and temp-bans to penalty list

View File

@ -0,0 +1,77 @@
<div id="consoleWrap">
<select id="serverSelection">
</select>
<hr/>
<div id="console">
</div>
<hr/>
<div class="playerSearchWrap table">
<input type="text" class="search tableCell" placeholder="Enter Command..."/>
<input type="button" class="searchButton tableCell" name="Search" value="Execute"/>
</div>
</div>
<script>
var cmdResultQueue = [];
$( document ).ready(function() {
cmdResultQueue = [];
$.getJSON("/_servers", function(servers) {
$.each(servers, function(i, server) {
$('select').append("<option value=\"" + server['serverPort'] + "\">" + server['serverName'] + "</option>");
});
});
});
function addCommandResult(result)
{
$.each(result, function(i, line) {
if (line == "You entered an invalid command!" || line == "All commands must start with '!'" )
{
line = getColorForLevel("Banned", line);
}
else
{
line = getColorForLevel("Trusted", line);
}
if (cmdResultQueue.length > 12)
cmdResultQueue.shift();
cmdResultQueue.push(line);
});
}
function formatCommandResults()
{
$('#console').html("");
for (i = 0; i < cmdResultQueue.length; i++)
$('#console').append("<span class=\"commandResult\">"
+ cmdResultQueue[i] + "</span><br/>"
);
}
$('.searchButton').click(function() {
if ($('.search').val().length > 0)
{
if ($('.search').val()[0] != '!')
{
addCommandResult(["All commands must start with '!'"]);
formatCommandResults();
return false;
}
$.getJSON("/_console?command=" + $('.search').val() + "&server=" + $('select').val(), function(result) {
$.each(result, function(i, line) {
addCommandResult(line)
});
}).done(function (data) { formatCommandResults(); $('.search').val(""); });
}
});
$(document).keypress(function(e) {
if(e.which == 13) {
$('.searchButton').click();
}
});
</script>

View File

@ -1,40 +1,7 @@
<div id="footer">IW4M Admin v{{VERSION}} &mdash; <a href="http://raidmax.org/IW4MAdmin">RaidMax.org</a></div>
</div>
<div id="footer">&copy; RaidMax</div>
</body>
<script>
$(function () {
$("#history_dialog").dialog({
autoOpen: false,
modal: true,
width: 1100,
height: 450,
buttons: {
"Dismiss": function () {
$(this).dialog("close");
}
}
});
$("a.history").on("click", function (e) {
e.preventDefault();
$("#history_dialog").html("");
$("#history_dialog").dialog("option", "title", "Player History").dialog("open");
$("#history_dialog").load(this.href);
});
});
getPages();
</script>
<script>
$(function () {
$('.pseudoLinkAlias').click(function (e) {
e.preventDefault();
$(this).next().toggle('fast');
return true;
});
});
$(function () {
$('.pseudoLinkIP').click(function (e) {
e.preventDefault();
$(this).next().toggle('fast');
return true;
});
});
</script>
</body>
</html>
</html>

View File

@ -1,432 +1,145 @@
<!DOCTYPE html>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>{{TITLE}}</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jqueryui/1.11.1/jquery-ui.min.js"></script>
<link href="https://code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css" rel="stylesheet">
<script type='text/javascript' src='//www.google.com/jsapi'></script>
<script type="text/javascript">
var userip;
</script>
<script type="text/javascript" src="http://server.nbsclan.org/ip.php"></script>
<style>
* {
font-family: 'Robot', sans-serif;
margin: 0;
}
<title>IW4MAdmin by RaidMax</title>
<meta name="description" content="Administration tool for IW4M servers. Created by RaidMax">
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
html, body {
width: 100%;
height: 100%;
background-color: #171717;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://use.fontawesome.com/9c581fe29b.js"></script>
#container {
width: 1100px;
background-color: #fff;
margin: 0 auto;
padding: 30px;
}
.h0 {
font-size: 40pt;
text-align: center;
margin-bottom: 0px;
float: right;
line-height: 100px;
}
h1 {
margin-top: 20px;
clear: both;
}
#header_img {
width: 100px;
height: 96px;
float: right;
background-image: url("");
background-size: 100px 96px;
background-repeat: no-repeat;
}
#logo_shit
{
width: 100px;
height: 96px;
float: left;
background-image: url("");
background-size: 100px 96px;
background-repeat: no-repeat;
}
p {
margin-top: 10px;
margin-bottom: 10px;
}
ul {
padding: 0;
margin: 0;
display: table;
width: 100%;
}
ul.tablehead li {
display: table-cell;
list-style-type: none;
font-size: 18pt;
margin: 0;
padding-top: 10px;
padding-bottom: 10px;
}
ul.row li {
overflow: hidden;
display: table-cell;
list-style-type: none;
font-size: 12pt;
}
li {
}
td{
padding: 8px;
overflow: hidden;
white-space: nowrap;
}
tr.row-white {
background-color: #fff;
}
tr.row-grey {
background-color: #ddd;
}
th
{
font-size: 16pt;
}
li.row-green {
background-color: rgba(121, 194, 97, .3);
padding: 10px 0px 10px 0px;
width: 70px;
text-align: center;
}
li.row-red {
background-color: rgba(196, 22, 28, .3);
padding: 10px 0px 10px 0px;
width: 70px;
text-align: center;
}
input[type="submit"] {
border: none;
border-radius: 4px;
color: #fff;
font-size: 14pt;
background-color: rgb(121, 194, 97);
}
input[type="submit"]:hover {
background-color: #fff;
color: #171717;
//border: 1px solid #171717;
}
.question_title {
color: #171717;
font-size: 16pt;
font-weight: bold;
margin-top: 10px;
}
.question_answer {
background-color: rgb(121, 194, 97);
color: #fff;
padding: 5px;
border-radius: 4px;
}
.question_answer a:hover {
color: cyan;
}
ol, ol li {
margin-left: 0;
padding-left: 30px;
}
a:link, a:visited {
text-decoration: none;
color: rgb(38,120,230);
}
a:hover {
color: #171717;
}
.BigList {
font-size: 12pt;
opacity: 0.5;
}
.separator {
position: absolute;
width: 3px;
height: 40px;
background-color: #ccc;
left: 25%;
right: 75%;
}
.asterik {
font-size: 11pt;
color: #171717;
font-style: italic;
}
#commands {
margin: 0 auto;
}
.block {
margin-top: 10px;
margin-bottom: 10px;
}
hr {
background-color: rgb(38,120,230);
border: none;
width: 100%;
height: 5px;
margin-bottom: 5px;
margin-top: 5px;
}
.server {
width: 100%;
text-align: left;
}
.server_title {
font-size: 20pt;
margin-bottom: 20px;
min-width: 530px;
max-width: 545px;
}
.server_info {
font-size: 14pt;
}
.server_map {
min-width: 140px;
}
.server_players {
min-width: 50px;
}
.server_gametype {
min-width: 175px;
}
.players {
width: 40%;
text-align: left;
padding-top: 10px;
padding-bottom: 10px;
}
.bans {
text-align: left;
width: 100%;
}
.bans th
{
font-size: 20pt;
}
#pages{
font-size: 14pt;
text-align:center;
}
#pages a {
margin: 10px;
}
#pagination{
}
#footer{
background-color: #fff;
padding-top: 5px;
padding-bottom: 10px;
text-align: center;
width: 1160px;
margin: 0 auto;
border-radius: 0px 0px 11px 11px;
}
.players {
float: left;
width: 400px;
}
.players tbody tr td
{
padding: 3px;
}
.player_info{
width: 100%;
vertical-align: top;
text-align: left;
}
.player_info td
{
text-align: left;
vertical-align: top;
padding: 0;
}
#player_search {
position: absolute;
top: 0;
left: 0;
right: 0;
height: auto;
width: 300px;
margin: 0 auto;
}
#player_search input[type="submit"] {
padding: 3px;
margin: 3px;
margin-top: 10px;
width: auto;
height: auto;
border: 1px solid rgba(23, 23, 23, 0.49);
border-radius: 0;
}
#player_search input[type="text"] {
font-size: 14pt;
}
.chatFormat_text
{
font-size: 14pt;
width: 505px;
}
.playerAlias, .playerIPs
{
display: none;
}
.chatFormat_submit, .chatFormat_submit:hover
{
padding: 3px;
padding-left: 15px;
padding-right: 15px;
width: 70px;
margin: 3px;
margin-right: 0;
width: auto;
margin-bottom: 10px;
color: grey;
}
.chatHistory {
float: right;
height: auto;
width: 600px;
overflow: hidden;
margin-top: 10px;
}
.chatOutFormat {
float: right;
}
#table_chatHistory {
width: 100%;
padding: 0;
margin: 0;
}
#table_chatHistory td {
padding: 0;
margin: 0;
}
.chat_name
{
width: 140px;
}
.chat_message
{
text-align: left;
}
th
{
font-size: 14pt;
}
th a
{
font-size: 12pt;
padding-left: 10px;
}
</style>
<link rel="stylesheet" type="text/css" href="/webfront/main.css"/>
<link rel="stylesheet" type="text/css" href="/webfront/mobile.css"/>
</head>
<script>
var user;
$.getJSON("_userinfo", function(response) {
user = response;
$(document).trigger("actionEventLoad");
});
function showErrorMessage(msg)
{
$('.alertBox').html(msg).addClass('error').slideDown("fast");
}
function parseGet(val) {
var result = "undefined",
tmp = [];
location.search
.substr(1)
.split("&")
.forEach(function (item) {
tmp = item.split("=");
if (tmp[0] === val) result = decodeURIComponent(tmp[1]);
});
return result;
}
function getColorForLevel(level, name)
{
switch (level)
{
case "User":
case "Guest":
return "<span style='color: rgba(255, 255, 255, 0.85);'>" + name + "</span>";
case "Trusted":
case "Kick":
case "User":
return "<span style='color: rgba(116,147,99,1);''>" + name + "</span>";
case "Flagged":
case "TempBan":
return "<span style='color: rgba(253, 139, 22, 0.85);'>" + name + "</span>";
case "Banned":
case "Ban":
case "Console":
return "<span style='color: rgba(255, 69, 69, 0.85);'>" + name + "</span>";
case "Moderator":
case "Warning":
return "<span style='color: rgba(235, 211, 101, 0.75);'>" + name + "</span>";
case "Administrator":
return "<span style='color: rgba(236, 130, 222, 0.69);'>" + name + "</span>";
case "SeniorAdmin":
return "<span style='color: rgba(50, 177, 185, 0.85);'>" + name + "</span>";
case "Owner":
return "<span style='color: rgb(0, 122, 204);'>" + name + "</span>";
}
}
function formatMessages(messages)
{
var chats = "";
$.each(messages, function(i, chat) {
chats +=
"<div class='chatPlayerName tableCell'>" + chat['Name'] + ":</div><div class='chatPlayerMessage tableCell'>" + $("<div/>").html(chat['Message']).text() + "</div> \
<div style='display:table-row'></div>"
});
return chats;
}
function getPages()
{
$.getJSON("/pages", function(result){
$.each(result, function(i, page){
if (page['visible'] == true)
$("#navContainer").append("<div class=navEntry><a href=\"" + page['pagePath'] + "\">" + page['pageName'] + "</a></div>");
});
});
}
function shouldHideAction(author)
{
// fixme dynamic
if (user.rank == null || author.ranking == null)
return " display: none";
else if (user.rank.name == "Moderator" || user.rank.name == "Administrator" || user.username == author.username)
return "";
else {
return " display: none";
}
}
function formatPlayers(players)
{
var p = "";
for (i = 0; i < players.length; i++)
{
p += "<div class='playerName tableCell'><a href=\"/players?id=" + players[1*i]['playerID'] + "\">" + getColorForLevel(players[1*i]['playerLevel'], players[1*i]['playerName']) + "</a></div>";
if (i % 2 == 1 && i != 0 )
p += "<div style='display: table-row'></div>";
}
return p;
}
function checkJustNow(timestr)
{
if (timestr.includes("just now"))
return timestr;
else
return timestr + " ago";
}
function getDate(datestr)
{
var creationDate = new Date(datestr);
return (creationDate.getMonth() + 1) + '-' + creationDate.getDate() + '-' + creationDate.getFullYear();
}
function checkPrivilege()
{
$.getJSON("_userinfo", function(response) { if (response.rank.id != 1) window.location.replace("home"); else $('.infoBox').show(); });
}
</script>
<body>
<script type="text/javascript">
function loadChatMessages(server, divElem) {
$(divElem).load("/chat?server=" + server);
}
</script>
<script type="text/javascript">
function chatRequest(server, divElem) {
var Message = document.getElementById(divElem).value.replace(/\s/g, "%20").replace(/[\\|\/]/g,"");
if (Message.length > 4 && Message.length < 51)
{
$(".null").load("/chat?server=" + server);
$("#" + divElem).val('');
}
else if (Message.length <= 4)
alert("You must enter at least 4 characters!");
else
alert("Please enter no more than 50 characters");
}
</script>
<script type="text/javascript">
function searchPlayerName() {
var nameValue = document.getElementById("search_playerName").value;
if (nameValue.length > 3)
window.location.href = ("/player?query=" + encodeURIComponent(nameValue));
else
alert("Please enter at least 4 characters of the name");
}
</script>
<div id="player_search">
<form action="javascript:searchPlayerName()">
<input id="search_playerName" type="text" placeholder="Player Name" />
<input type="submit" value="Find" />
</form>
</div>
<div id="header">
<div id="navContainer">
<div id="navHeader">IW4MAdmin</div>
</div>
</div>
<div class="loader"></div>
<div id="content">

12
Admin/webfront/login.html Normal file
View File

@ -0,0 +1,12 @@
<div class="infoBox">
<div class="header">Register</div>
<div class="alertBox">
</div>
<form id="login" method="get">
<input id="username" name="username" type="text"/>
<label for="username">Username</label>
<input id="password" name="password" type="password"/>
<label for="password">Password</label>
<input id="login" value="Login" type="submit"/>
</form>
</div>

194
Admin/webfront/main.css Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,31 @@
<div id="container">
<div class="h0" style="margin-top: 0">IW4M Admin</div><div id="logo_shit"></div>
<div id="history_dialog"></div>
<h1 style="margin-top: 0;">Currently Monitoring</h1>
<hr />
{{SERVERS}}
</div>
<script>
function getServers()
{
$.getJSON("/_servers", function(result){
$("#serverList").html("");
$.each(result, function(i, server){
$("#serverList").append("<div class=serverContainer> \
<div class='serverInfo table'> \
<div class='serverTitle tableCell'>" + server['serverName'] + "</div> \
<div class='serverMap tableCell'>" + server['mapName'] + "</div> \
<div class='serverPlayers tableCell'>" + server['currentPlayers'] + "/" + server['maxPlayers'] + "</div> \
</div> \
<div class='serverChatList table'>" +
formatMessages(server['chatHistory'])
+ "</div> \
<div class='serverPlayerList table'>" +
formatPlayers(server['players'])
+ "</div> \
<div style='clear: both;'></div><hr/></div>"
);
});
});
}
$( document ).ready(function() {
getServers();
setInterval(getServers, 1000)
});
</script>
<div id="serverList">
</div>

View File

@ -0,0 +1,8 @@
@media screen and (max-width: 1200px)
{
div#content { padding-left: 0; margin-left: 1em; padding-right: 0; margin-right: 1em; }
div#view { width: 100%; }
div#threadContainer { width: 90%; }
div#userInfoBox { width: 95%; }
div#userCover { width: 100%; left: 0; }
}

View File

@ -0,0 +1,61 @@
<script>
var curFrom = 0;
function getNextPage()
{
curFrom += 15;
return curFrom;
}
function getPrevPage()
{
if ((curFrom - 15) >= 0)
{
curFrom -= 15;
return (curFrom );
}
else
{
curFrom = 0;
return 0;
}
}
function getPenalties(from)
{
$("#penaltyList").html("");
$(".loader").fadeIn();
$.getJSON("/_penalties?from=" + from, function(result) {
$.each(result, function(i, penalty) {
$("#penaltyList").append(
"<div class=\"playerPenalty table alternate_" + i % 2 + "\"> \
<div class=\"penaltyName tableCell\"><a href=\"/players?id="+ penalty['playerID'] + "\">" + penalty['playerName'] + "</a></div> \
<div class=\"penaltyType tableCell\">"+ getColorForLevel(penalty['penaltyType'], penalty['penaltyType']) + "</div> \
<div class=\"penaltyReason tableCell\">"+ penalty['penaltyReason'] + "</div> \
<div class=\"penaltyOrigin tableCell\">"+ getColorForLevel(penalty['adminLevel'], penalty['adminName']) + "</div> \
<div class=\"penaltyTime tableCell\">"+ penalty['penaltyTime'] + " ago </div> \
</div>"
)
});
}).done(function (data) { $(".loader").fadeOut(); });
}
$( document ).ready(function() {
getPenalties(0);
});
</script>
<div class="penaltyHeader table">
<div class="penaltyName tableCell">Name</div>
<div class="penaltyType tableCell">Type</div>
<div class="penaltyReason tableCell">Reason</div>
<div class="penaltyOrigin tableCell">Admin</div>
<div class="penaltyTime tableCell">Time</div>
</div>
<div id="penaltyList">
</div>
<hr />
<div id="paginationButtons" class="table">
<div id="previousPage" class="tableCell"><a href=# onclick="getPenalties(getPrevPage())"><<</a></div>
<div id="nextPage" class="tableCell"><a href=# onclick="getPenalties(getNextPage())">>></a></div>
</div>

View File

@ -1,7 +0,0 @@
<div id="container">
<div class="h0" style="margin-top: 0; line-height:normal;">PLAYER<br /><a style="padding: 0; margin: 0; font-size: 24px; float: right;" href="/">Back</a></div>
<div id="logo_shit"></div>
<div style="clear:both"></div>
<hr/>
{{PLAYER}}
</div>

110
Admin/webfront/players.html Normal file
View File

@ -0,0 +1,110 @@
<script>
var curFrom = 0;
function getNextPage()
{
curFrom += 15;
return curFrom;
}
function getPrevPage()
{
if ((curFrom - 15) >= 0)
{
curFrom -= 15;
return (curFrom - 15);
}
else
return 0;
}
function formatHidden(data)
{
var p = "<div class=\"hiddenWrapper\">Expand<div class=\"hiddenElements\">";
$.each(data, function(i, dat) {
p += "<span>" + dat + "</span><br/>"
})
p += "</div></div>"
return p;
}
function printPlayer(player, i)
{
$("#playersTable").append(
"<div class=\"playerInfo table alternate_" + i % 2 + "\"> \
<div class=\"tableCell\"><a href=\"/players?id="+ player['playerID'] + "\">" + player['playerName'] + "</a></div> \
<div class=\"tableCell\">"+ formatHidden(player['playerAliases']) + "</div> \
<div class=\"tableCell\">"+ formatHidden(player['playerIPs']) + "</div> \
<div class=\"tableCell\">"+ getColorForLevel(player['playerLevel'], player['playerLevel']) + "</div> \
<div class=\"tableCell\">"+ player['playerConnections'] + "</div> \
<div class=\"tableCell actionButton\" style='width: 2em;'> \
<a target=\"_blank\" href='http://server.nbsclan.org/screen.php?id=" + player.forumID+ "&name=" + player.playerName + "'> \
<i class=\"fa fa-camera\" aria-hidden=\"true\"></i> \
</a> \
<a target=\"_blank\" href='https://v2.mcsebi.ru/memberlist.php?mode=viewprofile&u=" + player.forumID + "'> \
<i class=\"fa fa-user tableCell\" aria-hidden=\"true\"></i> \
</a> \
</div> \
<div class=\"tableCell alignRight\">"+ checkJustNow(player['lastSeen']) + "</div> \
</div>"
)
}
function getPlayer(ident, identValue)
{
$("#playersTable").html("");
$(".loader").fadeIn();
$.getJSON("/getplayer?" + ident + "=" + identValue, function(result) {
$.each(result, function(i, player) {
printPlayer(player, i);
});
}).done(function (data) { $(".loader").fadeOut(); });
}
$( document ).ready(function() {
if (parseGet('id') != "undefined")
getPlayer('id', parseGet('id'));
else if (parseGet('name') != "undefined")
getPlayer('name', parseGet('name'));
else {
getPlayer('recent', '1');
}
});
$('#content').on('click', '.hiddenWrapper', function(){
$(this).find('.hiddenElements').toggle()
});
</script>
<div class="playerSearchWrap">
<input type="button" class="searchButton" name="Search" value="Search"/>
<input type="text" class="search" placeholder="Player Name..."/>
</div>
<div class="contentHeader table">
<div class="contentColumn tableCell">Name</div>
<div class="contentColumn tableCell">Aliases</div>
<div class="contentColumn tableCell">IP</div>
<div class="contentColumn tableCell">Level</div>
<div class="contentColumn tableCell">Connections</div>
<div class="contentColumn tableCell" style="width: 1em;">V2</div>
<div class="contentColumn tableCell alignRight">Last Seen</div>
</div>
<div id="playersTable">
</div>
<hr/>
<script>
$('.searchButton').click(function() {
if ($('.search').val().length > 0)
getPlayer('name', $('.search').val());
});
$(document).keypress(function(e) {
if(e.which == 13) {
$('.searchButton').click();
}
});
</script>

View File

@ -1,5 +0,0 @@
<div id="container">
<div class="h0" style="margin-top: 0; line-height:normal;">STATS<br /><a style="padding: 0; margin: 0; font-size: 24px; float: right;" href="/">Back</a></div>
<div id="logo_shit"></div>
{{STATS}}
</div>

View File

@ -0,0 +1,70 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2321A25F-7871-47C3-8440-02551918D6A1}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Auto_Restart_Plugin</RootNamespace>
<AssemblyName>AutorestartPlugin</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Management" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="Monitoring.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Properties\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\plugins\AutoRestartPlugin.dll"
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,72 @@
using System;
using SharedLibrary;
using SharedLibrary.Extensions;
using System.Threading.Tasks;
namespace Auto_Restart_Plugin
{
public class Main : IPlugin
{
public string Author
{
get
{
return "RaidMax";
}
}
public float Version
{
get
{
return 1.0f;
}
}
public string Name
{
get
{
return "Auto Restart Plugin";
}
}
public async Task OnLoad()
{
return;
}
public async Task OnUnload()
{
return;
}
public async Task OnTick(Server S)
{
switch (Monitoring.shouldRestart())
{
case 300:
await S.Broadcast("^1Server will be performing an ^5AUTOMATIC ^1restart in ^55 ^1minutes.");
break;
case 120:
await S.Broadcast("^1Server will be performing an ^5AUTOMATIC ^1restart in ^52 ^1minutes.");
break;
case 60:
await S.Broadcast("^1Server will be performing an ^5AUTOMATIC ^1restart in ^51 ^1minute.");
break;
case 30:
await S.Broadcast("^1Server will be performing an ^5AUTOMATIC ^1restart in ^530 ^1seconds.");
break;
case 0:
await S.Broadcast("^1Server now performing an ^5AUTOMATIC ^1restart ^5NOW ^1please reconnect.");
Monitoring.Restart(S);
break;
}
}
public async Task OnEvent(Event E, Server S)
{
return;
}
}
}

View File

@ -0,0 +1,127 @@
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Management;
using SharedLibrary;
namespace Auto_Restart_Plugin
{
static class Monitoring
{
[DllImport("kernel32")]
public static extern bool DeleteFile(string name);
public static void Restart(Server goodBye)
{
_Restart(goodBye);
}
private static void _Restart(Server goodBye)
{
try
{
string cmdLine = GetCommandLine(Process.GetProcessById(goodBye.pID()));
var info = new ProcessStartInfo();
// if we don't delete this, we get a prompt..
DeleteFile(Process.GetProcessById(goodBye.pID()).MainModule.FileName + ":Zone.Identifier");
//info.WorkingDirectory = goodBye.Basepath;
info.Arguments = cmdLine;
info.FileName = Process.GetProcessById(goodBye.pID()).MainModule.FileName;
// goodBye.executeCommand("killserver");
Process.GetProcessById(Process.GetProcessById(goodBye.pID()).Parent().Id).Kill();
Process.GetProcessById(goodBye.pID()).Kill();
Process.Start(info);
}
catch (Exception E)
{
goodBye.Log.Write("SOMETHING FUCKED UP BEYOND ALL REPAIR " + E.ToString());
}
}
public static int shouldRestart()
{
var curTime = DateTime.Now;
DateTime restartTime = new DateTime(curTime.Year, curTime.Month, curTime.Day, 4, 0, 0);
var a = Math.Floor((restartTime - curTime).TotalMilliseconds / 1000);
if (a > 0 && a < 2) // just in case of precision
return 0;
else
{
switch((int)a)
{
case 300:
return 300;
case 120:
return 120;
case 60:
return 60;
case 30:
return 30;
default:
return 1337;
}
}
}
//http://stackoverflow.com/questions/2633628/can-i-get-command-line-arguments-of-other-processes-from-net-c
private static string GetCommandLine(this Process process)
{
var commandLine = new StringBuilder();
commandLine.Append(" ");
using (var searcher = new ManagementObjectSearcher("SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + process.Id))
{
foreach (var @object in searcher.Get())
{
if (@object["CommandLine"].ToString().Contains("iw4m"))
commandLine.Append(@object["CommandLine"].ToString().Substring(4));
else
commandLine.Append(@object["CommandLine"]);
commandLine.Append(" ");
}
}
return commandLine.ToString().Trim();
}
}
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
}

View File

@ -4,15 +4,16 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{99E36EBD-1FA1-494C-8A66-BECE64EFF378}</ProjectGuid>
<ProjectGuid>{79406C5E-A8AD-4A50-A7F0-53CE56792A31}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Webfront_Plugin</RootNamespace>
<AssemblyName>Webfront Plugin</AssemblyName>
<RootNamespace>ChatMonitor</RootNamespace>
<AssemblyName>ChatMonitor</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
@ -22,6 +23,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
@ -29,31 +31,28 @@
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="Kayak">
<HintPath>..\Admin\bin\Release\lib\Kayak.dll</HintPath>
</Reference>
<Reference Include="SharedLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\SharedLibrary\bin\Release\SharedLibrary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Web" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Compile Include="Main.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="Framework.cs" />
<Compile Include="Main.cs" />
<Compile Include="Manager.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
</ProjectReference>
<ProjectReference Include="..\Webfront Plugin\Webfront Plugin.csproj">
<Project>{99e36ebd-1fa1-494c-8a66-bece64eff378}</Project>
<Name>Webfront Plugin</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\plugins\WebfrontPlugin.dll"</PostBuildEvent>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\plugins\ChatMonitorPlugin.dll"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using SharedLibrary;
namespace ChatMonitor
{
public class Main : IPlugin
{
public string Author
{
get
{
return "RaidMax";
}
}
public float Version
{
get
{
return 1.0f;
}
}
public string Name
{
get
{
return "Chat Monitor Plugin";
}
}
public void onLoad()
{
lastClear = DateTime.Now;
flaggedMessages = 0;
flaggedMessagesText = new List<string>();
}
public void onUnload()
{
return;
}
public void onTick(Server S)
{
return;
}
public void onEvent(Event E, Server S)
{
}
}
}

73
EventAPI/EventAPI.csproj Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>EventAPI</RootNamespace>
<AssemblyName>EventAPI</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Plugin.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\plugins\EventAPI.dll"
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

183
EventAPI/Plugin.cs Normal file
View File

@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
using System.Text;
using SharedLibrary;
using SharedLibrary.Extensions;
using System.Threading.Tasks;
namespace EventAPI
{
class EventsJSON : IPage
{
private struct EventResponse
{
public int eventCount;
public RestEvent Event;
}
public string getName()
{
return "Events";
}
public string getPath()
{
return "/api/events";
}
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
bool shouldQuery = querySet.Get("status") != null;
EventResponse requestedEvent = new EventResponse();
HttpResponse resp = new HttpResponse();
if (shouldQuery)
{
StringBuilder s = new StringBuilder();
foreach (var S in Events.activeServers)
s.Append(String.Format("{0} has {1}/{4} players playing {2} on {3}\n", S.getName(), S.getPlayers().Count, Utilities.gametypeLocalized(S.getGametype()), S.CurrentMap.Name, S.MaxClients));
requestedEvent.Event = new RestEvent(RestEvent.eType.STATUS, RestEvent.eVersion.IW4MAdmin, s.ToString(), "Status", "", "");
requestedEvent.eventCount = 1;
}
else if (Events.apiEvents.Count > 0)
{
requestedEvent.Event = Events.apiEvents.Dequeue();
requestedEvent.eventCount = 1;
}
else
{
requestedEvent.eventCount = 0;
}
resp.content = Newtonsoft.Json.JsonConvert.SerializeObject(requestedEvent);
resp.contentType = getContentType();
resp.additionalHeaders = new Dictionary<string, string>();
return resp;
}
public string getContentType()
{
return "application/json";
}
public bool isVisible()
{
return false;
}
}
class Events : IPlugin
{
public static Queue<RestEvent> apiEvents { get; private set; }
public static List<Server> activeServers;
DateTime lastClear;
int flaggedMessages;
List<string> flaggedMessagesText;
public String Name
{
get { return "Event API Plugin"; }
}
public float Version
{
get { return 1.0f; }
}
public string Author
{
get
{
return "RaidMax";
}
}
public async Task OnLoad()
{
apiEvents = new Queue<RestEvent>();
flaggedMessagesText = new List<string>();
activeServers = new List<Server>();
WebService.pageList.Add(new EventsJSON());
}
public async Task OnUnload()
{
apiEvents.Clear();
activeServers.Clear();
}
public async Task OnTick(Server S)
{
return;
}
public async Task OnEvent(Event E, Server S)
{
if (E.Type == Event.GType.Start)
{
activeServers.Add(S);
S.Log.Write("Event API now running on " + S.getName(), Log.Level.Production);
}
if (E.Type == Event.GType.Stop)
{
activeServers.RemoveAll(s => s.getPort() == S.getPort());
S.Log.Write("Event API no longer running on " + S.getName(), Log.Level.Production);
}
if (E.Type == Event.GType.Connect)
{
addRestEvent(new RestEvent(RestEvent.eType.NOTIFICATION, RestEvent.eVersion.IW4MAdmin, E.Origin.Name + " has joined " + S.getName(), E.Type.ToString(), S.getName(), E.Origin.Name));
}
if (E.Type == Event.GType.Disconnect)
{
addRestEvent(new RestEvent(RestEvent.eType.NOTIFICATION, RestEvent.eVersion.IW4MAdmin, E.Origin.Name + " has left " + S.getName(), E.Type.ToString(), S.getName(), E.Origin.Name));
}
if (E.Type == Event.GType.Say)
{
if (E.Data.Length != 0 && E.Data[0] != '!')
addRestEvent(new RestEvent(RestEvent.eType.NOTIFICATION, RestEvent.eVersion.IW4MAdmin, E.Data, "Chat", E.Origin.Name, ""));
}
if (E.Type == Event.GType.Say && E.Origin.Level < Player.Permission.Moderator)
{
string message = E.Data.ToLower();
bool flagged = message.Contains(" wh ") || message.Contains("hax") || message.Contains("cheat") || message.Contains("hack") || message.Contains("aim") || message.Contains("wall") || message.Contains("cheto") || message.Contains("hak");
if (flagged)
{
flaggedMessages++;
flaggedMessagesText.Add(String.Format("{0}: {1}", E.Origin.Name, E.Data));
}
if (flaggedMessages > 3)
{
await E.Owner.Broadcast("If you suspect someone of ^5CHEATING ^7use the ^5!report ^7command");
addRestEvent(new RestEvent(RestEvent.eType.ALERT, RestEvent.eVersion.IW4MAdmin, "Chat indicates there may be a cheater", "Alert", E.Owner.getName(), ""));
addRestEvent(new RestEvent(RestEvent.eType.NOTIFICATION, RestEvent.eVersion.IW4MAdmin, String.Join("\n", flaggedMessagesText), "Chat Monitor", E.Owner.getName(), ""));
flaggedMessages = 0;
}
else if ((DateTime.Now - lastClear).TotalMinutes >= 3)
{
flaggedMessages = 0;
flaggedMessagesText.Clear();
lastClear = DateTime.Now;
}
}
}
public static void addRestEvent(RestEvent E)
{
if (apiEvents.Count > 10)
apiEvents.Dequeue();
apiEvents.Enqueue(E);
}
}
}

4
EventAPI/packages.config Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" requireReinstallation="true" />
</packages>

View File

@ -1,17 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.24720.0
# Visual Studio 15
VisualStudioVersion = 15.0.26403.7
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IW4M ADMIN", "Admin\IW4M ADMIN.csproj", "{DD5DCDA2-51DB-4B1A-922F-5705546E6115}"
ProjectSection(ProjectDependencies) = postProject
{2321A25F-7871-47C3-8440-02551918D6A1} = {2321A25F-7871-47C3-8440-02551918D6A1}
{AF097E6B-48D5-4452-9CCF-0A81A21F341D} = {AF097E6B-48D5-4452-9CCF-0A81A21F341D}
{4785AB75-66F3-4391-985D-63A5A049A0FA} = {4785AB75-66F3-4391-985D-63A5A049A0FA}
{99E36EBD-1FA1-494C-8A66-BECE64EFF378} = {99E36EBD-1FA1-494C-8A66-BECE64EFF378}
{D51EECEB-438A-47DA-870F-7D7B41BC24D6} = {D51EECEB-438A-47DA-870F-7D7B41BC24D6}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Webfront Plugin", "Webfront Plugin\Webfront Plugin.csproj", "{99E36EBD-1FA1-494C-8A66-BECE64EFF378}"
ProjectSection(ProjectDependencies) = postProject
{428D8EB9-ECA3-4A66-AA59-3A944378C33F} = {428D8EB9-ECA3-4A66-AA59-3A944378C33F}
{E46C85BD-A99C-484E-BCCE-0F1831C5925E} = {E46C85BD-A99C-484E-BCCE-0F1831C5925E}
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650} = {C9E821BF-23AD-4CB5-B7F9-B3B99B606650}
{D51EECEB-438A-47DA-870F-7D7B41BC24D6} = {D51EECEB-438A-47DA-870F-7D7B41BC24D6}
EndProjectSection
EndProject
@ -27,34 +26,162 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Welcome Plugin", "Welcome P
{D51EECEB-438A-47DA-870F-7D7B41BC24D6} = {D51EECEB-438A-47DA-870F-7D7B41BC24D6}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Auto Restart Plugin", "Auto Restart Plugin\Auto Restart Plugin.csproj", "{2321A25F-7871-47C3-8440-02551918D6A1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Votemap Plugin", "Votemap Plugin\Votemap Plugin.csproj", "{428D8EB9-ECA3-4A66-AA59-3A944378C33F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MessageboardPlugin", "MessageboardPlugin\MessageboardPlugin.csproj", "{E46C85BD-A99C-484E-BCCE-0F1831C5925E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventAPI", "EventAPI\EventAPI.csproj", "{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{26E8B310-269E-46D4-A612-24601F16065F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|x64.ActiveCfg = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|x64.Build.0 = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|x86.ActiveCfg = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Debug|x86.Build.0 = Debug|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|Any CPU.Build.0 = Release|Any CPU
{99E36EBD-1FA1-494C-8A66-BECE64EFF378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{99E36EBD-1FA1-494C-8A66-BECE64EFF378}.Debug|Any CPU.Build.0 = Debug|Any CPU
{99E36EBD-1FA1-494C-8A66-BECE64EFF378}.Release|Any CPU.ActiveCfg = Release|Any CPU
{99E36EBD-1FA1-494C-8A66-BECE64EFF378}.Release|Any CPU.Build.0 = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|x64.ActiveCfg = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|x64.Build.0 = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|x86.ActiveCfg = Release|Any CPU
{DD5DCDA2-51DB-4B1A-922F-5705546E6115}.Release|x86.Build.0 = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|x64.ActiveCfg = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|x64.Build.0 = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|x86.ActiveCfg = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Debug|x86.Build.0 = Debug|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|Any CPU.Build.0 = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|x64.ActiveCfg = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|x64.Build.0 = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|x86.ActiveCfg = Release|Any CPU
{4785AB75-66F3-4391-985D-63A5A049A0FA}.Release|x86.Build.0 = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|x64.ActiveCfg = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|x64.Build.0 = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|x86.ActiveCfg = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Debug|x86.Build.0 = Debug|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|Any CPU.Build.0 = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|x64.ActiveCfg = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|x64.Build.0 = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|x86.ActiveCfg = Release|Any CPU
{D51EECEB-438A-47DA-870F-7D7B41BC24D6}.Release|x86.Build.0 = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|x64.ActiveCfg = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|x64.Build.0 = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|x86.ActiveCfg = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Debug|x86.Build.0 = Debug|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|Any CPU.Build.0 = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|x64.ActiveCfg = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|x64.Build.0 = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|x86.ActiveCfg = Release|Any CPU
{AF097E6B-48D5-4452-9CCF-0A81A21F341D}.Release|x86.Build.0 = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|x64.ActiveCfg = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|x64.Build.0 = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|x86.ActiveCfg = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Debug|x86.Build.0 = Debug|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|Any CPU.Build.0 = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|x64.ActiveCfg = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|x64.Build.0 = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|x86.ActiveCfg = Release|Any CPU
{2321A25F-7871-47C3-8440-02551918D6A1}.Release|x86.Build.0 = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|x64.ActiveCfg = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|x64.Build.0 = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|x86.ActiveCfg = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Debug|x86.Build.0 = Debug|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|Any CPU.Build.0 = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|x64.ActiveCfg = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|x64.Build.0 = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|x86.ActiveCfg = Release|Any CPU
{428D8EB9-ECA3-4A66-AA59-3A944378C33F}.Release|x86.Build.0 = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|x64.ActiveCfg = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|Any CPU.Build.0 = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|x64.ActiveCfg = Release|Any CPU
{E46C85BD-A99C-484E-BCCE-0F1831C5925E}.Release|x86.ActiveCfg = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|x64.Build.0 = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|x86.ActiveCfg = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Debug|x86.Build.0 = Debug|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|Any CPU.Build.0 = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|x64.ActiveCfg = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|x64.Build.0 = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|x86.ActiveCfg = Release|Any CPU
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{4785AB75-66F3-4391-985D-63A5A049A0FA} = {26E8B310-269E-46D4-A612-24601F16065F}
{AF097E6B-48D5-4452-9CCF-0A81A21F341D} = {26E8B310-269E-46D4-A612-24601F16065F}
{2321A25F-7871-47C3-8440-02551918D6A1} = {26E8B310-269E-46D4-A612-24601F16065F}
{428D8EB9-ECA3-4A66-AA59-3A944378C33F} = {26E8B310-269E-46D4-A612-24601F16065F}
{E46C85BD-A99C-484E-BCCE-0F1831C5925E} = {26E8B310-269E-46D4-A612-24601F16065F}
{C9E821BF-23AD-4CB5-B7F9-B3B99B606650} = {26E8B310-269E-46D4-A612-24601F16065F}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,43 @@
using System;
using System.Security.Cryptography;
using System.Text;
//http://codereview.stackexchange.com/questions/96494/user-password-encryption-in-c + SCrypt
namespace MessageBoard.Encryption
{
public static class PasswordHasher
{
public static byte[] ComputeHash(string password, byte[] salt)
{
byte[] pwBytes = Encoding.UTF8.GetBytes(password);
byte[] hashBytes = new byte[64];
CryptSharp.Utility.SCrypt.ComputeKey(pwBytes, salt, 16384, 8, 1, null, hashBytes);
return hashBytes;
}
public static byte[] GenerateSalt(int saltByteSize = 24)
{
RNGCryptoServiceProvider saltGenerator = new RNGCryptoServiceProvider();
byte[] salt = new byte[saltByteSize];
saltGenerator.GetBytes(salt);
return salt;
}
public static bool VerifyPassword(String password, byte[] passwordSalt, byte[] passwordHash)
{
byte[] computedHash = ComputeHash(password, passwordSalt);
return AreHashesEqual(computedHash, passwordHash);
}
//Length constant verification - prevents timing attack
private static bool AreHashesEqual(byte[] firstHash, byte[] secondHash)
{
int minHashLength = firstHash.Length <= secondHash.Length ? firstHash.Length : secondHash.Length;
var xor = firstHash.Length ^ secondHash.Length;
for (int i = 0; i < minHashLength; i++)
xor |= firstHash[i] ^ secondHash[i];
return 0 == xor;
}
}
}

View File

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard.Events
{
public delegate void ActionEventHandler(User origin, EventArgs e);
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard.Exceptions
{
public class ThreadException : Exception
{
public ThreadException(string msg) : base(msg) { }
}
public class UserException : Exception
{
public UserException(string msg) : base(msg) { }
}
public class SessionException : Exception
{
public SessionException(string msg) : base(msg) { }
}
public class CategoryException : Exception
{
public CategoryException(string msg) : base(msg) { }
}
public class PermissionException: Exception
{
public PermissionException(string msg) : base(msg) { }
}
}

1160
MessageboardPlugin/Forum.cs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard
{
interface Identifiable
{
int getID();
}
}

View File

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{E46C85BD-A99C-484E-BCCE-0F1831C5925E}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MessageBoard</RootNamespace>
<AssemblyName>MessageboardPlugin</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Encryption.cs" />
<Compile Include="Events.cs" />
<Compile Include="Exceptions.cs" />
<Compile Include="Forum.cs" />
<Compile Include="Identifiable.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Rank.cs" />
<Compile Include="Session.cs" />
<Compile Include="Storage.cs" />
<Compile Include="Thread.cs" />
<Compile Include="User.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="CryptSharp">
<HintPath>..\packages\CryptSharp.1.2.0.1\lib\net35\CryptSharp.dll</HintPath>
</Reference>
<Reference Include="MarkdownDeep, Version=1.5.4615.26275, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\MarkdownDeep.NET.1.5\lib\.NetFramework 3.5\MarkdownDeep.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net40\Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Security" />
<Reference Include="System.XML" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<Content Include="forum\category.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\home.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\login.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\postthread.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\register.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\thread.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="forum\user.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>mkdir "$(SolutionDir)Admin\bin\$(ConfigurationName)\forum"
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\plugins\MessageBoardPlugin.dll"
xcopy /E /Y "$(TargetDir)forum" "$(SolutionDir)Admin\bin\$(ConfigurationName)\forum"
copy /Y "$(TargetDir)MarkdownDeep.dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\lib\MarkdownDeep.dll"
copy /Y "$(TargetDir)CryptSharp.dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\lib\CryptSharp.dll"
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,65 @@
using System;
using SharedLibrary;
using SharedLibrary.Extensions;
using System.Threading.Tasks;
namespace MessageBoard.Plugin
{
public class Main : IPlugin
{
public static Forum.Manager forum { get; private set; }
public static Server stupidServer { get; private set; }
public string Author
{
get
{
return "RaidMax";
}
}
public float Version
{
get
{
return 0.1f;
}
}
public string Name
{
get
{
return "Message Board Plugin";
}
}
public async Task OnLoad()
{
await Task.Run(() =>
{
forum = new Forum.Manager();
forum.Start();
});
}
public async Task OnUnload()
{
forum.Stop();
}
public async Task OnTick(Server S)
{
return;
}
public async Task OnEvent(Event E, Server S)
{
if (E.Type == Event.GType.Start)
{
if (stupidServer == null)
stupidServer = S;
}
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard
{
public class Rank : Identifiable
{
public string name;
public SharedLibrary.Player.Permission equivalentRank;
public int id;
/// <summary>
/// Initial creation
/// </summary>
/// <param name="name"></param>
/// <param name="equivalentRank"></param>
/// <param name="permissions"></param>
public Rank(string name, SharedLibrary.Player.Permission equivalentRank)
{
this.name = name;
this.equivalentRank = equivalentRank;
id = 0;
}
public Rank(int id, string name, SharedLibrary.Player.Permission equivalentRank)
{
this.name = name;
this.equivalentRank = equivalentRank;
this.id = id;
}
public int getID()
{
return id;
}
}
public class Permission
{
[Flags]
public enum Action
{
NONE = 0x0,
READ = 0x1,
WRITE = 0x2,
MODIFY = 0x4,
DELETE = 0x8
}
public int rankID;
public Action actionable;
public Permission(int rankID, Action actionable)
{
this.rankID = rankID;
this.actionable = actionable;
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard
{
public class Session
{
public User sessionUser;
public string sessionID { get; private set; }
public DateTime sessionStartTime;
public Session(User sessionUser, string sessionID)
{
this.sessionUser = sessionUser;
this.sessionID = sessionID;
sessionStartTime = DateTime.Now;
}
}
}

View File

@ -0,0 +1,554 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace MessageBoard.Storage
{
class Database : SharedLibrary.Database
{
public Database(String FN) : base(FN) { }
public override void Init()
{
if (!System.IO.File.Exists(FileName))
{
string createClientTable = @"CREATE TABLE [USERS] (
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
[ranking] INTEGER DEFAULT 0,
[username] TEXT NOT NULL,
[email] TEXT NOT NULL,
[passwordhash] TEXT NOT NULL,
[passwordsalt] TEXT NOT NULL,
[lastlogin] TEXT NOT NULL,
[creationdate] TEXT NOT NULL,
[subscribedthreads] TEXT DEFAULT 0,
[avatarurl] TEXT
);";
string createSessionTable = @"CREATE TABLE [SESSIONS] (
[sessionid] TEXT NOT NULL,
[sessionuserid] INTEGER NOT NULL,
FOREIGN KEY(sessionuserid) REFERENCES USERS(id)
);";
string createRankingTable = @"CREATE TABLE [RANKS] (
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
[name] TEXT UNIQUE NOT NULL,
[equivalentrank] INTEGER DEFAULT 0
);";
string createCategoryTable = @"CREATE TABLE [CATEGORIES] (
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
[title] TEXT NOT NULL,
[description] TEXT NOT NULL,
[permissions] BLOB
);";
string createThreadTable = @"CREATE TABLE [THREADS] (
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
[title] TEXT NOT NULL,
[categoryid] INTEGER NOT NULL,
[replies] INTEGER DEFAULT 0,
[authorid] INTEGER NOT NULL,
[creationdate] TEXT NOT NULL,
[updateddate] TEXT NOT NULL,
[content] TEXT NOT NULL,
[visible] INTEGER DEFAULT 1,
FOREIGN KEY(authorid) REFERENCES USERS(id),
FOREIGN KEY(categoryid) REFERENCES CATEGORIES(id)
);";
string createReplyTable = @"CREATE TABLE [REPLIES] (
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
[title] TEXT NOT NULL,
[authorid] INT NOT NULL,
[threadid] INT NOT NULL,
[creationdate] TEXT NOT NULL,
[updateddate] TEXT NOT NULL,
[content] TEXT NOT NULL,
[visible] INTEGER DEFAULT 1,
FOREIGN KEY(authorid) REFERENCES USERS(id),
FOREIGN KEY(threadid) REFERENCES THREADS(id)
);";
ExecuteNonQuery(createClientTable);
ExecuteNonQuery(createSessionTable);
ExecuteNonQuery(createRankingTable);
ExecuteNonQuery(createCategoryTable);
ExecuteNonQuery(createThreadTable);
ExecuteNonQuery(createReplyTable);
Rank guestRank = new Rank(1, "Guest", SharedLibrary.Player.Permission.User);
Rank userRank = new Rank(2, "User", SharedLibrary.Player.Permission.Trusted);
Rank modRank = new Rank(3, "Moderator", SharedLibrary.Player.Permission.Moderator);
Rank adminRank = new Rank(4, "Administrator", SharedLibrary.Player.Permission.Owner);
addRank(guestRank);
addRank(userRank);
addRank(modRank);
addRank(adminRank);
List<Permission> defaultCatPerms = new List<Permission> {
new Permission(guestRank.getID(), Permission.Action.READ),
new Permission(userRank.getID(), Permission.Action.READ | Permission.Action.WRITE),
new Permission(modRank.getID(), Permission.Action.READ | Permission.Action.WRITE | Permission.Action.MODIFY),
new Permission(adminRank.getID(), Permission.Action.READ | Permission.Action.WRITE | Permission.Action.MODIFY | Permission.Action.DELETE)
};
Category defaultCat = new Category(1, "Default Category", "This is the default category.", defaultCatPerms);
addCategory(defaultCat);
}
}
#region SESSIONS
public Session getSession(string sessionID)
{
DataTable Result = GetDataTable("SESSIONS", new KeyValuePair<string, object>("sessionid", sessionID));
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
int userID = Int32.Parse(ResponseRow["sessionuserid"].ToString());
User sessionUser = getUser(userID);
// this shouldn't happen.. but it might :c
if (sessionUser == null)
return null;
Session foundSession = new Session(sessionUser, sessionID);
return foundSession;
}
else
return null;
}
public Session setSession(int userID, string sessionID)
{
// prevent duplicated tuples
if (getSession(sessionID) != null)
{
updateSession(sessionID, userID);
return getSession(sessionID);
}
Dictionary<String, object> newSession = new Dictionary<String, object>();
newSession.Add("sessionid", sessionID);
newSession.Add("sessionuserid", userID);
Insert("SESSIONS", newSession);
return getSession(sessionID);
}
public bool updateSession(string sessionID, int userID)
{
if (getSession(sessionID) == null)
return false;
Dictionary<string, object> updatedSession = new Dictionary<string, object>();
updatedSession.Add("sessionuserid", userID);
Update("SESSIONS", updatedSession, new KeyValuePair<string, object>("sessionid", sessionID));
return true;
}
#endregion
#region USERS
private User getUserFromDataTable(DataTable Result)
{
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
int id = Convert.ToInt32(ResponseRow["id"].ToString());
string passwordHash = ResponseRow["passwordhash"].ToString();
string passwordSalt = ResponseRow["passwordsalt"].ToString();
string username = ResponseRow["username"].ToString();
string email = ResponseRow["email"].ToString();
DateTime lastLogon = DateTime.Parse(ResponseRow["lastlogin"].ToString());
DateTime creationDate = DateTime.Parse(ResponseRow["creationdate"].ToString());
Rank ranking = getRank(Convert.ToInt32(ResponseRow["ranking"]));
string avatarURL = ResponseRow["avatarurl"].ToString();
string posts = GetDataTable(String.Format("select (select count(*) from THREADS where authorid = {0}) + (select count(*) from REPLIES where authorid = {0}) as posts;", id)).Rows[0]["posts"].ToString();
User foundUser = new User(id, passwordHash, passwordSalt, username, email, Convert.ToInt32(posts), lastLogon, creationDate, ranking, avatarURL);
return foundUser;
}
return null;
}
private Dictionary<string, object> getDataTableFromUser(User addedUser)
{
Dictionary<String, object> newUser = new Dictionary<String, object>();
newUser.Add("username", addedUser.username);
newUser.Add("email", addedUser.email);
newUser.Add("passwordhash", addedUser.getPasswordHash());
newUser.Add("passwordsalt", addedUser.getPasswordSalt());
newUser.Add("lastlogin", SharedLibrary.Utilities.DateTimeSQLite(addedUser.lastLogin));
newUser.Add("creationdate", SharedLibrary.Utilities.DateTimeSQLite(addedUser.creationDate));
//newUser.Add("subscribedthreads", String.Join<int>(",", addedUser.subscribedThreads));
newUser.Add("ranking", addedUser.ranking.getID());
newUser.Add("avatarurl", addedUser.avatarURL);
return newUser;
}
public User getUser(int userid)
{
DataTable Result = GetDataTable("USERS", new KeyValuePair<string, object>("id", userid));
return getUserFromDataTable(Result);
}
public User getUser(string username)
{
DataTable Result = GetDataTable("USERS", new KeyValuePair<string, object>("username", username));
return getUserFromDataTable(Result);
}
public bool userExists(string username, string email)
{
String Query = String.Format("SELECT * FROM USERS WHERE username = '{0}' or email = '{1}'", username, email);
DataTable Result = GetDataTable(Query);
return Result.Rows.Count > 0;
}
/// <summary>
/// Returns ID of added user
/// </summary>
/// <param name="addedUser"></param>
/// <param name="userSession"></param>
/// <returns></returns>
public User addUser(User addedUser, Session userSession)
{
var newUser = getDataTableFromUser(addedUser);
Insert("USERS", newUser);
// fixme
User createdUser = getUser(addedUser.username);
return createdUser;
}
public bool updateUser(User updatedUser)
{
var user = getDataTableFromUser(updatedUser);
Update("USERS", user, new KeyValuePair<string, object>("id", updatedUser.getID()));
return true;
}
public int getNumUsers()
{
var Result = GetDataTable("SELECT COUNT(id) AS userCount FROM `USERS`;");
return Convert.ToInt32(Result.Rows[0]["userCount"]);
}
#endregion
#region CATEGORIES
private Category getCategoryFromDataTable(DataTable Result)
{
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
int id = Convert.ToInt32(ResponseRow["id"]);
string title = ResponseRow["title"].ToString();
string description = ResponseRow["description"].ToString();
string permissions = Encoding.UTF8.GetString((byte[])ResponseRow["permissions"]);
List<Permission> perms = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Permission>>(permissions);
Category requestedCategory = new Category(id, title, description, perms);
return requestedCategory;
}
return null;
}
public void addCategory(Category addingCategory)
{
Dictionary<String, object> newCategory = new Dictionary<string, object>();
newCategory.Add("title", addingCategory.title);
newCategory.Add("description", addingCategory.description);
newCategory.Add("permissions", Newtonsoft.Json.JsonConvert.SerializeObject(addingCategory.permissions));
Insert("CATEGORIES", newCategory);
}
public Category getCategory(int id)
{
string Query = String.Format("SELECT * FROM CATEGORIES WHERE id = {0}", id);
DataTable Result = GetDataTable(Query);
return getCategoryFromDataTable(Result);
}
public List<Category> getAllCategories()
{
string Query = String.Format("SELECT id FROM CATEGORIES");
List<Category> cats = new List<Category>();
DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0)
{
for (int i = 0; i < Result.Rows.Count; i++)
cats.Add(getCategory(Convert.ToInt32(Result.Rows[i]["id"])));
}
return cats;
}
#endregion
#region THREADS
public Dictionary<string, object> getDataTableFromThread(ForumThread Thread)
{
Dictionary<string, object> newThread = new Dictionary<string, object>();
newThread.Add("title", Thread.title);
newThread.Add("categoryid", Thread.threadCategory.getID());
newThread.Add("replies", Thread.replies);
newThread.Add("authorid", Thread.author.getID());
newThread.Add("creationdate", SharedLibrary.Utilities.DateTimeSQLite(Thread.creationDate));
newThread.Add("updateddate", SharedLibrary.Utilities.DateTimeSQLite(Thread.updatedDate));
newThread.Add("content", Thread.content);
newThread.Add("visible", Convert.ToInt32(Thread.visible));
return newThread;
}
public int addThread(ForumThread Thread)
{
Insert("THREADS", getDataTableFromThread(Thread));
return getThreadID(Thread.creationDate);
}
public bool updateThread(ForumThread updatedThread)
{
var user = getDataTableFromThread(updatedThread);
Update("THREADS", user, new KeyValuePair<string, object>("id", updatedThread.getID()));
return true;
}
public ForumThread getThread(int id)
{
DataTable Result = GetDataTable("THREADS", new KeyValuePair<string, object>("id", id));
return getThreadFromDataTable(Result);
}
private ForumThread getThreadFromDataTable(DataTable Result)
{
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
int id = Convert.ToInt32(ResponseRow["id"].ToString());
int categoryid = Convert.ToInt32(ResponseRow["categoryid"].ToString());
int authorid = Convert.ToInt32(ResponseRow["authorid"].ToString());
int replies = Convert.ToInt32(ResponseRow["replies"].ToString());
string title = ResponseRow["title"].ToString();
var category = getCategory(categoryid);
var author = getUser(authorid);
bool visible = Convert.ToBoolean((Convert.ToInt32(ResponseRow["visible"])));
DateTime creationDate = DateTime.Parse(ResponseRow["creationdate"].ToString());
DateTime updatedDate = DateTime.Parse(ResponseRow["updateddate"].ToString());
string content = ResponseRow["content"].ToString();
ForumThread retrievedThread = new ForumThread(id, title, visible, content, replies, author, category, creationDate, updatedDate);
return retrievedThread;
}
return null;
}
// we have no other unique id yet
private int getThreadID(DateTime creationDate)
{
string Query = String.Format("SELECT * FROM THREADS WHERE creationdate = \"{0}\"", SharedLibrary.Utilities.DateTimeSQLite(creationDate));
DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0)
return Convert.ToInt32(Result.Rows[0]["id"].ToString());
return 0;
}
public List<ForumThread> getRecentThreads(int categoryID)
{
List<ForumThread> threads = new List<ForumThread>();
string Query = String.Format("SELECT id FROM THREADS WHERE categoryid = {0} AND visible = 1 ORDER BY `updateddate` DESC LIMIT 3", categoryID);
DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0)
{
for (int i = 0; i < Result.Rows.Count; i++)
threads.Add(getThread(Convert.ToInt32(Result.Rows[i]["id"])));
}
return threads;
}
public List<ForumThread> getCategoryThreads(int categoryID)
{
List<ForumThread> threads = new List<ForumThread>();
string Query = String.Format("SELECT id FROM THREADS WHERE categoryid = {0} and visible = 1 ORDER BY `updateddate` DESC", categoryID);
DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0)
{
for (int i = 0; i < Result.Rows.Count; i++)
threads.Add(getThread(Convert.ToInt32(Result.Rows[i]["id"])));
}
return threads;
}
#endregion
#region RANKING
public int addRank(Rank newRank)
{
Dictionary<string, object> rank = new Dictionary<string, object>();
rank.Add("name", newRank.name);
rank.Add("equivalentrank", (int)newRank.equivalentRank);
Insert("RANKS", rank);
Rank r = getRank(newRank.name);
if (r == null)
return 0;
return r.getID();
}
public Rank getRank(string rankName)
{
DataTable Result = GetDataTable("RANKS", new KeyValuePair<string, object>("name", rankName));
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
string name = ResponseRow["name"].ToString();
int equivRank = Convert.ToInt32(ResponseRow["equivalentrank"].ToString());
int id = Convert.ToInt32(ResponseRow["id"].ToString());
Rank retrievedRank = new Rank(id, name, (SharedLibrary.Player.Permission)equivRank);
return retrievedRank;
}
return null;
}
public Rank getRank(int rankID)
{
DataTable Result = GetDataTable("RANKS", new KeyValuePair<string, object>("id", rankID));
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
string name = ResponseRow["name"].ToString();
int equivRank = Convert.ToInt32(ResponseRow["equivalentrank"].ToString());
Rank retrievedRank = new Rank(rankID, name, (SharedLibrary.Player.Permission)equivRank);
return retrievedRank;
}
return null;
}
#endregion
#region REPLIES
public int addReply(Post reply)
{
Insert("REPLIES", getDataTableFromReply(reply));
return getReplyID(reply.creationDate);
}
public bool updateReply(Post reply)
{
return Update("REPLIES", getDataTableFromReply(reply), new KeyValuePair<string, object>("id", reply.id));
}
public Post getReply(int id)
{
DataTable Result = GetDataTable("REPLIES", new KeyValuePair<string, object>("id", id));
return getReplyFromDataTable(Result);
}
public List<Post> getRepliesFromThreadID(int threadID)
{
List<Post> replies = new List<Post>();
//var Result = GetDataTable("REPLIES", new KeyValuePair<string, object>("threadid", threadID));
var Result = GetDataTable("SELECT * FROM REPLIES WHERE threadid = " + threadID + " AND visible = 1");
foreach (DataRow row in Result.Rows)
{
replies.Add(getReply(Convert.ToInt32(row["id"].ToString())));
}
return replies;
}
private Dictionary<string, object> getDataTableFromReply(Post reply)
{
Dictionary<string, object> newReply = new Dictionary<string, object>();
newReply.Add("title", reply.title);
newReply.Add("authorid", reply.author.getID());
newReply.Add("threadid", reply.threadid);
newReply.Add("creationdate", SharedLibrary.Utilities.DateTimeSQLite(reply.creationDate));
newReply.Add("updateddate", SharedLibrary.Utilities.DateTimeSQLite(reply.updatedDate));
newReply.Add("content", reply.content);
newReply.Add("visible", Convert.ToInt32(reply.visible));
return newReply;
}
private Post getReplyFromDataTable(DataTable Result)
{
if (Result != null && Result.Rows.Count > 0)
{
DataRow ResponseRow = Result.Rows[0];
int id = Convert.ToInt32(ResponseRow["id"].ToString());
int threadid = Convert.ToInt32(ResponseRow["threadid"].ToString());
int authorid = Convert.ToInt32(ResponseRow["authorid"].ToString());
string title = ResponseRow["title"].ToString();
var author = getUser(authorid);
DateTime creationDate = DateTime.Parse(ResponseRow["creationdate"].ToString());
DateTime updatedDate = DateTime.Parse(ResponseRow["updateddate"].ToString());
string content = ResponseRow["content"].ToString();
bool visible = Convert.ToBoolean((Convert.ToInt32(ResponseRow["visible"])));
Post retrievedPost = new Post(id, threadid, visible, title, content, author, creationDate, updatedDate);
return retrievedPost;
}
return null;
}
// we have no other unique id yet
private int getReplyID(DateTime creationDate)
{
DataTable Result = GetDataTable("REPLIES", new KeyValuePair<string, object>("creationdate", SharedLibrary.Utilities.DateTimeSQLite(creationDate)));
if (Result != null && Result.Rows.Count > 0)
return Convert.ToInt32(Result.Rows[0]["id"].ToString());
return 0;
}
#endregion
}
}

View File

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MessageBoard
{
public class Post : ForumThread
{
/// <summary>
/// Initial creation
/// </summary>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="author"></param>
/// <param name="parentThread"></param>
///
public int threadid;
public Post(string title, int threadid, string content, User author) : base (title, content, author, null)
{
this.threadid = threadid;
}
public Post(int id, int threadid, bool visible, string title, string content, User author, DateTime creationDate, DateTime updatedDate) : base(id, title, visible, content, 0, author, null, creationDate, updatedDate)
{
this.lastModificationString = SharedLibrary.Utilities.timePassed(creationDate);
this.threadid = threadid;
}
}
public class Category : Identifiable
{
public int id { get; private set; }
public string title { get; private set; }
public string description { get; private set; }
public List<Permission> permissions { get; private set; }
public Category(string title, string description)
{
this.title = title;
this.description = description;
this.permissions = new List<Permission>();
id = 0;
}
public Category(int id, string title, string description, List<Permission> permissions)
{
this.title = title;
this.description = description;
this.id = id;
this.permissions = permissions;
}
public int getID()
{
return id;
}
}
public class ForumThread : Identifiable
{
public string title { get; private set; }
public string content { get; private set; }
public User author { get; private set; }
public Category threadCategory { get; private set; }
public DateTime creationDate { get; private set; }
public DateTime updatedDate;
public string lastModificationString { get; protected set; }
public int id { get; private set; }
public int replies;
public bool visible = true;
/// <summary>
/// Initial creation
/// </summary>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="author"></param>
public ForumThread(string title, string content, User author, Category threadCategory)
{
if (content.Length == 0)
throw new Exceptions.ThreadException("Post is empty");
if (author == null)
throw new Exceptions.ThreadException("No author of post");
if (title.Length == 0)
throw new Exceptions.ThreadException("Title is empty");
this.title = title;
this.content = content;
this.author = author;
this.threadCategory = threadCategory;
creationDate = DateTime.Now;
updatedDate = DateTime.Now;
replies = 0;
id = 0;
}
/// <summary>
/// Loading from database
/// </summary>
/// <param name="id"></param>
/// <param name="title"></param>
/// <param name="content"></param>
/// <param name="author"></param>
/// <param name="creationDate"></param>
public ForumThread(int id, string title, bool visible, string content, int replies, User author, Category threadCategory, DateTime creationDate, DateTime updatedDate)
{
this.id = id;
this.replies = replies;
this.title = title;
this.content = content;
this.author = author;
this.threadCategory = threadCategory;
this.creationDate = creationDate;
this.updatedDate = updatedDate;
this.lastModificationString = SharedLibrary.Utilities.timePassed(updatedDate);
this.visible = visible;
}
public int getID()
{
return id;
}
public bool updateContent(string content)
{
if (content != null && content.Length > 0)
{
this.content = content;
return true;
}
return false;
}
public bool updateTitle(string title)
{
if (title != null && title.Length > 0)
{
this.title = title;
return true;
}
return false;
}
}
}

108
MessageboardPlugin/User.cs Normal file
View File

@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharedLibrary;
namespace MessageBoard
{
public class User : Identifiable
{
private string passwordHash; // byte array -> b64 string
private string passwordSalt; // byte array -> b64 string
public DateTime lastLogin;
public string lastLoginString;
public readonly DateTime creationDate;
public int id { get; private set; }
public string avatarURL;
public string username { get; private set; }
public string email { get; private set; }
public Rank ranking;
public int posts;
public int privateMessages;
public int warnings;
public List<int> subscribedThreads { get; private set; }
public User()
{
username = "Guest";
ranking = Plugin.Main.forum.guestRank;
}
/// <summary>
/// When creating a new user
/// </summary>
/// <param name="username"></param>
/// <param name="email"></param>
/// <param name="passwordHash"></param>
/// <param name="passwordSalt"></param>
/// <param name="posts"></param>
/// <param name="privateMessage"></param>
/// <param name="warnings"></param>
public User(string username, string matchedUsername, string email, string passwordHash, string passwordSalt, Rank ranking)
{
if (username.Length < 1)
throw new Exceptions.UserException("Username is empty");
if (email.Length < 1)
throw new Exceptions.UserException("Email is empty");
lastLogin = DateTime.Now;
subscribedThreads = new List<int>();
this.username = username;
this.email = email;
this.posts = 0;
this.privateMessages = 0;
this.warnings = 0;
this.ranking = ranking;
this.passwordHash = passwordHash;
this.passwordSalt = passwordSalt;
this.creationDate = DateTime.Now;
this.avatarURL = "";
id = 0;
}
public User(int id, string passwordHash, string passwordSalt, string username, string email, int posts, DateTime lastLogin, DateTime creationDate, Rank ranking, string avatarURL)
{
this.id = id;
this.passwordHash = passwordHash;
this.passwordSalt = passwordSalt;
this.username = username;
this.email = email;
this.lastLogin = lastLogin;
this.creationDate = creationDate;
this.ranking = ranking;
this.avatarURL = avatarURL;
this.posts = posts;
this.lastLoginString = SharedLibrary.Utilities.timePassed(lastLogin);
}
public int getID()
{
return this.id;
}
public string getPasswordSalt()
{
return this.passwordSalt;
}
public string getPasswordHash()
{
return this.passwordHash;
}
}
public struct UserInfo
{
public string username;
public string email;
public string matchedUsername;
public Rank rank;
}
}

View File

@ -0,0 +1,86 @@
<div id="view">
<div style="float: left;" id="categoryHeader">
</div>
<a href="home"><i class="fa fa-reply themeBlue" aria-hidden="true" style="padding: 0 0.5em; font-size: 24pt; cursor: pointer; margin-top: -5px;"></i></a>
<a href="" id="postThreadButton">
<div style="float: right;" id="postThreadCaption">
<i class="fa fa-plus" aria-hidden="true"></i>
Post
</div>
</a>
<div style="clear: both;"></div>
<hr class="simple"/>
<div id="categoryContainer">
</div>
<hr/>
</div>
<script>
$('#postThreadButton').attr("href", "postthread?id=" + parseGet("id"));
$( document ).on("actionEventLoad", function() {
$.getJSON("_categorythreads?id=" + parseGet("id"), function(response) {
var result = "";
if (response.errorCode != null)
{
if (response.errorCode == 1)
$('#categoryHeader').append('Permission Denied');
else if (response.errorCode == 13)
{
$('#categoryHeader').html("Invalid Category");
$('#postThreadButton').hide();
}
return;
}
if (response.length == 0)
{
$('#categoryHeader').append('No Posts');
return;
}
$.each(response, function(i, thread) {
result +=
"<div class=\"categoryThread table\"> \
<i class=\"fa fa-circle-o themeBlue tableCell\" aria-hidden=\"true\"></i> \
<div class=\"threadTitle tableCell\"><a href=\"thread?id=" + thread["id"] + "\">" + decodeURIComponent(thread["title"]) + "</a><span class=\"threadAuthor tableCell\"><a href=\"user?id=" + thread["author"].id + "\">" + thread["author"].username + "</a></span></div> \
<div class=\"threadTime tableCell\">Last response " + checkJustNow(thread["lastModificationString"]) + "</div> \
<div class=\"threadReplyCount tableCell\"><div class=\"threadReplyBG\">"+ thread.replies +"</div></div> \
<div class=\"threadActions tableCell\" style='vertical-align: middle; " + shouldHideAction(thread.author) +"'><i postid='"+ thread.id + "' class=\"fa fa-times actionHover actionDelete\" aria-hidden=\"true\"></i></div>\
</div>";
});
$('#categoryHeader').html(response[0]["threadCategory"].title);
$('#categoryContainer').append(result);
});
});
$('#content').on('click', '.actionDelete', function(e) {
$.getJSON("_editthread",
{
id : $(this).attr("postid"),
delete : true
},
function(response) {
if (response.success)
window.location.replace(response.destination);
});
});
</script>
<!--
<div id="categoryContainer">
<div class="categoryThread table">
<i class="fa fa-circle-o themeBlue tableCell" aria-hidden="true"></i>
<div class="threadTitle tableCell"><a href="#">This is the particular thread title</a><span class="threadAuthor tableCell"><a href="#">Example Author</a></span></div>
<div class="threadTime tableCell">5 minutes ago</div>
<div class="threadReplyCount tableCell"><div class="threadReplyBG">0</div></div>
</div>-->

View File

@ -0,0 +1,60 @@
<div id="view" class="table">
<div id="threadView" class="tableCell">
<div class="threadBox">
</div>
<hr/>
</div>
<div id="recentView" class="tableCell">
<div id="recentTitle">
Online Users
</div>
<div id="onlineUsers">
</div>
</div>
</div>
<script>
$( document ).ready(function() {
$.getJSON("_recentthreads", function(response) {
var result = "";
$.each(response, function(i, category) {
result += "<div class=\"categoryTitle datThing\"> \
<div class=\"title\"><a href=\"category?id=" + category["categoryID"] + "\">" + category["categoryTitle"] + "</a></div> \
<div class=\"categoryDescription\">" + category["categoryDescription"] + "</div>" +
"</div> \
<div class=\"threadPreview table\">";
$.each(category["recentThreads"], function(i, thread)
{
result += "<div class=\"individualThreadInfo\">";
result += "<i class=\"fa fa-comment\" aria-hidden=\"true\"></i>";
result += "<span class=\"threadTitle tableCell\"><a href=\"thread?id=" + thread.id + "\">" + decodeURIComponent(thread.title) + "</a> &mdash; <a style='opacity: 0.5;' href=\"user?id=" + thread.author.id + "\">" + thread.author['username'] + "</a></span>";
result += "<span class=\"threadInfo tableCell\"><a class=\"themeOrange\" href=\"" + "user?id=" + thread.author['id'] + "\"></a><span class=\"light\">" + checkJustNow(thread.lastModificationString) + "</span></span>";
result += "<div style=\"display: table-row;\"></div>";
result += "</div>";
});
result += "</div>"
});
$('.threadBox').append(result);
});
$.getJSON("_stats", function(response) {
$.each(response.onlineUsers, function(i, user) {
$('#onlineUsers').append('<a href="user?id=' + user.id + '"><p>' + getColorForLevel(user.ranking.name, user.username) + '<p></a>');
});
});
});
</script>

View File

@ -0,0 +1,59 @@
<div class="infoBox" style="display : none;">
<div class="header">
<i class="fa fa-user" aria-hidden="true"></i>
Login</div>
<div class="alertBox">
</div>
<form id="login" method="get">
<input id="username" name="username" type="text"/>
<label for="username">Username</label>
<input id="password" name="password" type="password"/>
<label for="password">Password</label>
<input id="loginButton" value="Login" type="submit"/>
<a href="register">Register</a>
</form>
</div>
<script>
$( document ).ready(function() {
checkPrivilege();
});
function validateInput()
{
var password = $('form #password');
var username = $('form #username');
if (password.val().length < 1) {
showErrorMessage("Password is required!");
return false;
}
if (username.val().length < 1) {
showErrorMessage("Username is required!");
return false;
}
return true;
}
$("#loginButton").click(function(e) {
e.preventDefault();
if (validateInput())
$.getJSON("_login",
{
username : $('form #username').val(),
password : $('form #password').val()
},
function(result) {
if (result["errorCode"] == 0)
window.location.replace(result["destination"]);
else {
showErrorMessage(result["errorCode"]);
}
}
);
});
</script>

View File

@ -0,0 +1,56 @@
<div id="postThreadContainer">
<div class="infoBox" style="width: 80%;">
<div class="header">
<i class="fa fa-commenting" aria-hidden="true"></i>
<span>Post New Thread</span>
</div>
<div class="alertBox">
</div>
<form>
<div class="table" style="width: 100%;">
<select id="threadCategory" class="tableCell">
</select>
<input placeholder="Enter thread title..." type="text" id="threadTitle" class="tableCell"/>
</div>
<textarea id="threadContent" placeholder="Enter thread content..."/></textarea>
<input type="submit" value="Post" id="submitThreadButton"/>
</form>
</div>
</div>
<script>
$( document ).ready(function() {
$.getJSON("_categories", function(response) {
$.each(response, function(i, category) {
$('select').append("<option value='" + category.id + "'>" + category.title + "</option>");
});
$('select option[value="'+ parseGet("id") +'"]').attr("selected",true);
});
});
$("#submitThreadButton").click(function(e) {
e.preventDefault();
$.getJSON("_postthread",
{
title : $('form #threadTitle').val(),
content : $('form #threadContent').val(),
category : $('select').val(),
},
function(result) {
if (result["errorCode"] == 0)
window.location.replace(result["destination"]);
else {
showErrorMessage(result["errorCode"]);
}
}
);
});
</script>

View File

@ -0,0 +1,92 @@
<div class="infoBox" style="display:none;">
<div class="header">
<i style="" class="fa fa-user-plus" aria-hidden="true"></i>
Register
</div>
<div class="alertBox">
</div>
<form id="registration" method="get">
<input id="username" name="username" type="text"/>
<input id="hiddenUsername" type="text" name="hiddenUsername" style="display: none;"/>
<label for="username">Username</label>
<input id="password" name="password" type="password"/>
<label for="password">Password</label>
<input id="passwordRepeat" name="passwordRepeat" type="password"/>
<label for="passwordRepeat">Verify Password</label>
<input id="email" name="email" type="text"/>
<label for="email">Email</label>
<input id="registerButton" value="Register" type="submit"/>
</form>
</div>
<script>
$( document ).ready(function() {
checkPrivilege();
});
function validateInput()
{
var password = $('form #password');
var repeatPassword = $('form #passwordRepeat');
var username = $('form #username');
var email = $('form #email');
if (password.val().length < 5) {
showErrorMessage("Passwords must be at least 5 characters!");
return false;
}
if (password.val() != repeatPassword.val()) {
showErrorMessage("Passwords must match!");
return false;
}
if (username.val().length < 3) {
showErrorMessage("Username must contain at least 3 characters!");
return false;
}
if (email.val().length < 3) {
showErrorMessage("Invalid email address!");
return false;
}
return true;
}
$("#registerButton").click(function(e) {
e.preventDefault();
if (validateInput())
$.getJSON("_register",
{
username : $('form #username').val(),
password : $('form #password').val(),
hiddenUsername : $('form #hiddenUsername').val(),
passwordRepeat : $('form #passwordRepeat').val(),
email : $('form #email').val()
},
function(result) {
if (result["errorCode"] == 0)
window.location.replace(result["destination"]);
else {
showErrorMessage(result["errorCode"]);
}
}
);
});
$('input[type="text"], input[type="password"]').click(function() { $('.alertBox').slideUp("fast"); });
$( document ).ready(function() {
$.getJSON("_userinfo", function(result) {
if (result["matchedName"] != "null")
$('#username, #hiddenUsername').val(result["matchedUsername"]);
});
});
</script>

View File

@ -0,0 +1,126 @@
<div id="threadContainer">
<div id="textNav"><a class="themeBlue" href="home">Home</a> &raquo; </div>
<hr/>
<div class="threadStart table" style="width: 100%;">
<div class="userInfo tableCell">
<div class="userAvatar">
<i class="fa fa-user-secret" aria-hidden="true" style="font-size: 8em;"></i>
</div>
<a class="userProfileLink" href=""><span class="userTitle">_</span></a><br/>
<span style="font-size: 9pt;" class="timePosted">_</span>
</div>
<div class="threadInfo tableCell">
<div class="threadTitle" style="float: left;">_</div>
<div style="float: right;" id="replyThreadCaption">
<i class="fa fa-reply" aria-hidden="true"></i>
Reply
</div>
<div style="clear: both;"></div>
<div class="threadContent">_</div>
</div>
</div>
</div>
<div id="postReplyContainer" style="display: none;">
<hr/>
<div id="postReplyClose">
<i class="fa fa-times" aria-hidden="true"></i>
</div>
<div id="replyContentContainer">
<div class="alertBox">
</div>
<textarea placeholder="Reply content..." id="replyContentBox"></textarea>
<div id="submitReplyButton">
<i class="fa fa-reply" aria-hidden="true"></i>
</div>
</div>
</div>
<script>
$( document ).on("actionEventLoad", function() {
$.getJSON("_thread?id=" + parseGet('id'), function(Response) {
if (Response.errorCode != null)
{
alert('error!');
}
$('#textNav').append('<a class="themeBlue" href="category?id=' + Response.Thread.threadCategory.id + '">' + Response.Thread.threadCategory.title + '</a> &raquo; ' + decodeURIComponent(Response.Thread.title));
$('.threadStart .userTitle').html(Response.Thread.author.username);
$('.threadStart .timePosted').html(getDate(Response.Thread.creationDate));
$('.threadStart .threadTitle').html(decodeURIComponent(Response.Thread.title));
$('.threadStart a.userProfileLink').attr("href", "user?id=" + Response.Thread.author.id);
$('.threadStart .threadContent').html(decodeURIComponent(Response.Thread.content));
if (Response.Thread.author.avatarURL != "")
$('.threadStart .userAvatar').html("").attr("style", "background-image:url('" + Response.Thread.author.avatarURL + "');'");
$('#replyThreadButton').attr("href", "postthread?threadid=" + Response.Thread.id);
$.each(Response.Replies, function(i, eachReply) {
var cat = "<div class='threadStart table' style='width: 100%;'> \
<div class='userInfo tableCell'>";
if (eachReply.author.avatarURL == "")
cat += "<div class='userAvatar'><i class='fa fa-user-secret' aria-hidden='true' style='font-size: 8em;'></i>";
else
cat += "<div class='userAvatar' style=\"background-image:url('" + eachReply.author.avatarURL + "');\">";
cat +=
"</div> \
<a class='userProfileLink' href='user?id="+ eachReply.author.id +"'><span class='userTitle'>" + getColorForLevel(eachReply.author.ranking.name, eachReply.author.username) + "</span></a><br/> \
<span style='font-size: 9pt;' class='timePosted'>" + checkJustNow(eachReply.lastModificationString) + "</span> \
</div> \
<div class='threadInfo tableCell'> \
<i style=\"" + shouldHideAction(eachReply.author) + "\" replyid='" + eachReply.id + "' class=\"fa fa-times actionHover actionDelete\" aria-hidden=\"true\"></i> \
<div class='threadContent'>" + decodeURIComponent(eachReply.content) + "</div> \
</div> \
</div>";
$("#threadContainer").append(cat);
});
});
});
$('#replyThreadCaption').click(function(e) {
e.preventDefault();
$('#postReplyContainer').slideDown('fast');
});
$('#postReplyClose').click(function(e) {
$(this).parent().slideUp('fast');
});
$("#submitReplyButton").click(function(e) {
e.preventDefault();
$.getJSON("_postthread",
{
content : $('#replyContentBox').val(),
title : "Reply",
threadid : parseGet("id")
},
function(result) {
if (result["errorCode"] == 0)
window.location.replace(result["destination"]);
else {
showErrorMessage(result["errorCode"]);
}
});
});
$('#content').on('click', '.actionDelete', function(e) {
$.getJSON("_editthread",
{
replyid : $(this).attr("replyid"),
delete : true
},
function(response) {
if (response.success)
window.location.replace(response.destination);
});
});
</script>

View File

@ -0,0 +1,56 @@
<div id="userCover" style="display:none;">
</div>
<div id="userInfoBox">
<div class="table" style="width: 100%;">
<div class="tableCell" style="vertical-align:middle; width: 70%;">
<div class="userInfoField table">
<i class="fa fa-user tableCell" aria-hidden="true"></i> <span class="tableCell" id="userCreated">_</span>
</div>
<div class="userInfoField table">
<i class="fa fa-clock-o tableCell" aria-hidden="true"></i> <span class="tableCell" id="userLogon">_</span>
</div>
<div class="userInfoField table">
<i class="fa fa-comment tableCell" aria-hidden="true"></i> <span class="tableCell" id="userPostCount">_</span>
</div>
<div class="userInfoField table">
<i class="fa fa-envelope-o tableCell" aria-hidden="true"></i> <span class="tableCell" id="userEmail"><a href="#" class="themeBlue">_</a></span>
</div>
<div class="userInfoField table">
<i class="fa fa-users tableCell" aria-hidden="true"></i> <span class="tableCell" id="userRank">_</span>
</div>
</div>
<div class="tableCell" style="vertical-align:middle;">
<div id="userAvatar" class="">
<i class="fa fa-user-secret" aria-hidden="true" style="font-size: 19em; margin-top: -56px;"></i>
</div>
</div>
</div>
<hr style="width: calc(100% + 2em); margin-bottom: -1em; margin-left: -1em;"/>
</div>
<script>
$.getJSON("_userinfo?id=" + parseGet('id'), function(user) {
if (user == null)
return false;
$('#userCover').html(user.username);
var creationDate = new Date(user.creationDate);
$('#userCreated').html("Joined " + (creationDate.getMonth() + 1) + '-' + creationDate.getDate() + '-' + creationDate.getFullYear());
$('#userLogon').html("Last seen " + checkJustNow(user.lastLoginString));
$('#userPostCount').html(user.posts + " Posts");
$('#userEmail a').html(user.email);
$('#userEmail a').attr("href", "mailto:" + user.email);
$('#userAvatar').html('');
$('#userAvatar').attr("style", "background-image:url('" + user.avatarURL + "');'");
$('#userRank').html(user.ranking.name);
$('#userCover').slideDown('fast');
});
</script>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="CryptSharp" version="1.2.0.1" targetFramework="net40" />
<package id="MarkdownDeep.NET" version="1.5" targetFramework="net40" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net40" requireReinstallation="true" />
</packages>

Binary file not shown.

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary
{
@ -18,7 +19,7 @@ namespace SharedLibrary
}
//Execute the command
abstract public void Execute(Event E);
abstract public Task ExecuteAsync(Event E);
public String Name { get; private set; }
public String Description { get; private set; }

View File

@ -2,24 +2,37 @@
using System.Collections.Generic;
using System.Text;
using SharedLibrary;
using SharedLibrary.Network;
using System.Threading.Tasks;
namespace IW4MAdmin
namespace SharedLibrary.Commands
{
class Quit : Command
{
public Quit(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override async Task ExecuteAsync(Event E)
{
E.Owner.Manager.Stop();
}
}
class Owner : Command
{
public Owner(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Owner.clientDB.getOwner() == null)
{
E.Origin.setLevel(Player.Permission.Owner);
E.Origin.Tell("Congratulations, you have claimed ownership of this server!");
await E.Origin.Tell("Congratulations, you have claimed ownership of this server!");
E.Owner.owner = E.Origin;
E.Owner.clientDB.updatePlayer(E.Origin);
}
else
E.Origin.Tell("This server already has an owner!");
await E.Origin.Tell("This server already has an owner!");
}
}
@ -27,15 +40,13 @@ namespace IW4MAdmin
{
public Warn(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.lastOffense = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Target.lastOffense = E.Data.RemoveWords(1);
if (E.Origin.Level <= E.Target.Level)
E.Origin.Tell("You cannot warn " + E.Target.Name);
await E.Origin.Tell("You cannot warn " + E.Target.Name);
else
{
E.Target.Warn(E.Target.lastOffense, E.Origin);
}
await E.Target.Warn(E.Target.lastOffense, E.Origin);
}
}
@ -43,12 +54,12 @@ namespace IW4MAdmin
{
public WarnClear(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.lastOffense = String.Empty;
E.Target.Warnings = 0;
String Message = String.Format("All warning cleared for {0}", E.Target.Name);
E.Owner.Broadcast(Message);
await E.Owner.Broadcast(Message);
}
}
@ -56,13 +67,13 @@ namespace IW4MAdmin
{
public Kick(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.lastOffense = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Target.lastOffense = SharedLibrary.Utilities.RemoveWords(E.Data, 1);
if (E.Origin.Level > E.Target.Level)
E.Target.Kick(E.Target.lastOffense, E.Origin);
await E.Target.Kick(E.Target.lastOffense, E.Origin);
else
E.Origin.Tell("You cannot kick " + E.Target.Name);
await E.Origin.Tell("You cannot kick " + E.Target.Name);
}
}
@ -70,9 +81,9 @@ namespace IW4MAdmin
{
public Say(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Owner.Broadcast("^1" + E.Origin.Name + " - ^6" + E.Data + "^7");
await E.Owner.Broadcast("^1" + E.Origin.Name + " - ^6" + E.Data + "^7");
}
}
@ -80,14 +91,14 @@ namespace IW4MAdmin
{
public TempBan(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.lastOffense = SharedLibrary.Utilities.removeWords(E.Data, 1);
String Message = "^1Player Temporarily Banned: ^5" + E.Target.lastOffense + "^7 (1 hour)";
E.Target.lastOffense = SharedLibrary.Utilities.RemoveWords(E.Data, 1);
String Message = E.Target.lastOffense;
if (E.Origin.Level > E.Target.Level)
E.Target.tempBan(Message, E.Origin);
await E.Target.TempBan(Message, E.Origin);
else
E.Origin.Tell("You cannot temp ban " + E.Target.Name);
await E.Origin.Tell("You cannot temp ban " + E.Target.Name);
}
}
@ -95,22 +106,22 @@ namespace IW4MAdmin
{
public SBan(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.lastOffense = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Target.lastOffense = SharedLibrary.Utilities.RemoveWords(E.Data, 1);
E.Target.lastEvent = E; // needs to be fixed
String Message;
if (E.Owner.Website == null)
Message = "^1Player Banned: ^5" + E.Target.lastOffense;
else
Message = "^1Player Banned: ^5" + E.Target.lastOffense + "^7 (appeal at" + E.Owner.Website + ")";
Message = "^1Player Banned: ^5" + E.Target.lastOffense;
if (E.Origin.Level > E.Target.Level)
{
E.Target.Ban(Message, E.Origin);
E.Origin.Tell(String.Format("Sucessfully banned ^5{0} ^7({1})", E.Target.Name, E.Target.npID));
await E.Target.Ban(Message, E.Origin);
await E.Origin.Tell(String.Format("Sucessfully banned ^5{0} ^7({1})", E.Target.Name, E.Target.npID));
}
else
E.Origin.Tell("You cannot ban " + E.Target.Name);
await E.Origin.Tell("You cannot ban " + E.Target.Name);
}
}
@ -118,12 +129,10 @@ namespace IW4MAdmin
{
public Unban(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Owner.Unban(E.Data.Trim(), E.Target))
E.Origin.Tell("Successfully unbanned " + E.Target.Name);
else
E.Origin.Tell("Unable to find a ban for that GUID");
await E.Owner.Unban(E.Data.Trim(), E.Target);
await E.Origin.Tell($"Successfully unbanned {E.Target.Name}::{E.Target.npID}");
}
}
@ -131,10 +140,10 @@ namespace IW4MAdmin
{
public WhoAmI(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.Name, E.Origin.clientID, E.Origin.npID, E.Origin.databaseID, SharedLibrary.Utilities.levelToColor(E.Origin.Level), E.Origin.IP);
E.Origin.Tell(You);
await E.Origin.Tell(You);
}
}
@ -142,28 +151,31 @@ namespace IW4MAdmin
{
public List(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
StringBuilder playerList = new StringBuilder();
lock (E.Owner.getPlayers())
int count = 0;
for (int i = 0; i < E.Owner.Players.Count; i++)
{
int count = 0;
foreach (Player P in E.Owner.getPlayers())
var P = E.Owner.Players[i];
if (P == null)
continue;
if (P.Masked)
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.levelToColor(Player.Permission.User), P.clientID, P.Name, SharedLibrary.Utilities.getSpaces(Player.Permission.SeniorAdmin.ToString().Length - Player.Permission.User.ToString().Length));
else
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", Utilities.levelToColor(P.Level), P.clientID, P.Name, SharedLibrary.Utilities.getSpaces(Player.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
if (count == 2 || E.Owner.getPlayers().Count == 1)
{
if (P == null)
continue;
playerList.AppendFormat("[^3{0}^7]{3}[^3{1}^7] {2}", SharedLibrary.Utilities.levelToColor(P.Level), P.clientID, P.Name, SharedLibrary.Utilities.getSpaces(Player.Permission.SeniorAdmin.ToString().Length - P.Level.ToString().Length));
if (count == 2)
{
E.Origin.Tell(playerList.ToString());
count = 0;
playerList = new StringBuilder();
continue;
}
count++;
await E.Origin.Tell(playerList.ToString());
count = 0;
playerList = new StringBuilder();
continue;
}
count++;
}
}
}
@ -172,48 +184,48 @@ namespace IW4MAdmin
{
public Help(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
String cmd = E.Data.Trim();
if (cmd.Length > 2)
{
bool found = false;
foreach (Command C in E.Owner.getCommands())
foreach (Command C in E.Owner.Manager.GetCommands())
{
if (C.Name.Contains(cmd) || C.Name == cmd)
{
E.Origin.Tell(" [^3" + C.Name + "^7] " + C.Description);
await E.Origin.Tell(" [^3" + C.Name + "^7] " + C.Description);
found = true;
}
}
if (!found)
E.Origin.Tell("Could not find that command");
await E.Origin.Tell("Could not find that command");
}
else
{
int count = 0;
StringBuilder helpResponse = new StringBuilder();
List<Command> test = E.Owner.getCommands();
List<Command> CommandList = E.Owner.Manager.GetCommands();
foreach (Command C in test)
foreach (Command C in CommandList)
{
if (E.Origin.Level >= C.Permission)
{
helpResponse.Append(" [^3" + C.Name + "^7] ");
if (count >= 4)
{
E.Origin.Tell(helpResponse.ToString());
await E.Origin.Tell(helpResponse.ToString());
helpResponse = new StringBuilder();
count = 0;
}
count++;
}
}
E.Origin.Tell(helpResponse.ToString());
E.Origin.Tell("Type !help <cmd> to get command usage example");
await E.Origin.Tell(helpResponse.ToString());
await E.Origin.Tell("Type !help <cmd> to get command usage example");
}
}
}
@ -222,22 +234,23 @@ namespace IW4MAdmin
{
public FastRestart(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Owner.Broadcast("Performing fast restart in 5 seconds...");
E.Owner.fastRestart(5);
await E.Owner.Broadcast("Performing fast restart...");
await Task.Delay(3000);
await E.Owner.ExecuteCommandAsync("fast_restart");
}
}
class MapRotate : Command
{
public MapRotate(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Owner.Broadcast("Performing map rotate in 5 seconds...");
E.Owner.mapRotate(5);
await E.Owner.Broadcast("Performing map rotate...");
await Task.Delay(3000);
await E.Owner.ExecuteCommandAsync("map_rotate");
}
}
@ -245,27 +258,41 @@ namespace IW4MAdmin
{
public SetLevel(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Target == E.Origin)
{
E.Origin.Tell("You can't set your own level, silly.");
await E.Origin.Tell("You can't set your own level, silly.");
return;
}
Player.Permission newPerm = SharedLibrary.Utilities.matchPermission(SharedLibrary.Utilities.removeWords(E.Data, 1));
Player.Permission newPerm = Utilities.matchPermission(Utilities.RemoveWords(E.Data, 1));
if (newPerm > Player.Permission.Banned)
{
E.Target.setLevel(newPerm);
E.Target.Tell("Congratulations! You have been promoted to ^3" + newPerm);
E.Origin.Tell(E.Target.Name + " was successfully promoted!");
// prevent saving of old permissions on disconnect
foreach (var server in E.Owner.Manager.GetServers())
{
foreach (var player in server.getPlayers())
{
if (player != null && player.npID == E.Target.npID)
{
player.setLevel(newPerm);
await E.Target.Tell("Congratulations! You have been promoted to ^3" + newPerm);
}
}
}
await E.Target.Tell("Congratulations! You have been promoted to ^3" + newPerm);
await E.Origin.Tell(E.Target.Name + " was successfully promoted!");
//NEEED TO MOVE
E.Owner.clientDB.updatePlayer(E.Target);
}
else
E.Origin.Tell("Invalid group specified.");
await E.Origin.Tell("Invalid group specified.");
}
}
@ -273,9 +300,9 @@ namespace IW4MAdmin
{
public Usage(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Origin.Tell("IW4M Admin is using " + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB");
await E.Origin.Tell("IW4M Admin is using " + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB");
}
}
@ -283,10 +310,10 @@ namespace IW4MAdmin
{
public Uptime(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
TimeSpan uptime = DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime;
E.Origin.Tell(String.Format("IW4M Admin has been up for {0} days, {1} hours, and {2} minutes", uptime.Days, uptime.Hours, uptime.Minutes));
await E.Origin.Tell(String.Format("IW4M Admin has been up for {0} days, {1} hours, and {2} minutes", uptime.Days, uptime.Hours, uptime.Minutes));
}
}
@ -294,19 +321,13 @@ namespace IW4MAdmin
{
public Admins(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
List<Player> activePlayers = E.Owner.getPlayers();
lock (activePlayers)
{
foreach (Player P in E.Owner.getPlayers())
{
if (P != null && P.Level > Player.Permission.Flagged && !P.Masked)
{
E.Origin.Tell(String.Format("[^3{0}^7] {1}", SharedLibrary.Utilities.levelToColor(P.Level), P.Name));
}
}
}
foreach (Player P in E.Owner.getPlayers())
if (P != null && P.Level > Player.Permission.Flagged && !P.Masked)
await E.Origin.Tell(String.Format("[^3{0}^7] {1}", Utilities.levelToColor(P.Level), P.Name));
}
}
@ -314,23 +335,23 @@ namespace IW4MAdmin
{
public MapCMD(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
string newMap = E.Data.Trim().ToLower();
foreach (Map m in E.Owner.maps)
{
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
{
E.Owner.Broadcast("Changing to map ^2" + m.Alias);
SharedLibrary.Utilities.Wait(3);
E.Owner.Map(m.Name);
await E.Owner.Broadcast("Changing to map ^2" + m.Alias);
await Task.Delay(5000);
await E.Owner.LoadMap(m.Name);
return;
}
}
E.Owner.Broadcast("Attempting to change to unknown map ^1" + newMap);
SharedLibrary.Utilities.Wait(3);
E.Owner.Map(newMap);
await E.Owner.Broadcast("Attempting to change to unknown map ^1" + newMap);
await Task.Delay(5000);
await E.Owner.LoadMap(newMap);
}
}
@ -338,20 +359,20 @@ namespace IW4MAdmin
{
public Find(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
var db_players = E.Owner.clientDB.findPlayers(E.Data.Trim());
if (db_players == null)
{
E.Origin.Tell("No players found");
await E.Origin.Tell("No players found");
return;
}
foreach (Player P in db_players)
{
String mesg = String.Format("[^3{0}^7] [^3@{1}^7] - [{2}^7] - {3} | last seen {4} ago", P.Name, P.databaseID, SharedLibrary.Utilities.levelToColor(P.Level), P.IP, P.getLastConnection());
E.Origin.Tell(mesg);
await E.Origin.Tell(mesg);
}
}
}
@ -360,13 +381,13 @@ namespace IW4MAdmin
{
public FindAll(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Data = E.Data.Trim();
if (E.Data.Length < 4)
{
E.Origin.Tell("You must enter at least 4 letters");
await E.Origin.Tell("You must enter at least 4 letters");
return;
}
@ -375,7 +396,7 @@ namespace IW4MAdmin
if (db_aliases == null)
{
E.Origin.Tell("No players found");
await E.Origin.Tell("No players found");
return;
}
@ -397,7 +418,7 @@ namespace IW4MAdmin
if (Current != null)
{
String mesg = String.Format("^1{0} ^7now goes by ^5{1}^7 [^3{2}^7]", lookingFor, Current.Name, Current.databaseID);
E.Origin.Tell(mesg);
await E.Origin.Tell(mesg);
}
}
}
@ -407,14 +428,14 @@ namespace IW4MAdmin
{
public Rules(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Owner.rules.Count < 1)
E.Origin.Tell("This server has not set any rules.");
await E.Origin.Tell("This server has not set any rules.");
else
{
foreach (String r in E.Owner.rules)
E.Origin.Tell("- " + r);
await E.Origin.Tell("- " + r);
}
}
}
@ -423,12 +444,11 @@ namespace IW4MAdmin
{
public PrivateMessage(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Data = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Target.Alert();
E.Target.Tell("^1" + E.Origin.Name + " ^3[PM]^7 - " + E.Data);
E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.Name, E.Data));
E.Data = Utilities.RemoveWords(E.Data, 1);
await E.Target.Tell("^1" + E.Origin.Name + " ^3[PM]^7 - " + E.Data);
await E.Origin.Tell(String.Format("To ^3{0} ^7-> {1}", E.Target.Name, E.Data));
}
}
@ -436,12 +456,12 @@ namespace IW4MAdmin
{
public Reload(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
{
public override async Task ExecuteAsync(Event E)
{
if (E.Owner.Reload())
E.Origin.Tell("Sucessfully reloaded configs!");
await E.Origin.Tell("Sucessfully reloaded configs!");
else
E.Origin.Tell("Unable to reload configs :(");
await E.Origin.Tell("Unable to reload configs :(");
}
}
@ -449,9 +469,9 @@ namespace IW4MAdmin
{
public Balance(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Origin.currentServer.executeCommand(String.Format("admin_lastevent {0};{1}", "balance", E.Origin.npID)); //Let gsc do the magic
await E.Owner.ExecuteCommandAsync(String.Format("admin_lastevent {0};{1}", "balance", E.Origin.npID)); //Let gsc do the magic
}
}
@ -459,9 +479,9 @@ namespace IW4MAdmin
{
public GoTo(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Origin.currentServer.executeCommand(String.Format("admin_lastevent {0};{1};{2};{3}", "goto", E.Origin.npID, E.Target.Name, E.Data)); //Let gsc do the magic
await E.Owner.ExecuteCommandAsync(String.Format("admin_lastevent {0};{1};{2};{3}", "goto", E.Origin.npID, E.Target.Name, E.Data)); //Let gsc do the magic
}
}
@ -469,24 +489,24 @@ namespace IW4MAdmin
{
public Flag(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Target.Level >= E.Origin.Level)
{
E.Origin.Tell("You cannot flag " + E.Target.Name);
await E.Origin.Tell("You cannot flag " + E.Target.Name);
return;
}
if (E.Target.Level == Player.Permission.Flagged)
{
E.Target.setLevel(Player.Permission.User);
E.Origin.Tell("You have ^5unflagged ^7" + E.Target.Name);
await E.Origin.Tell("You have ^5unflagged ^7" + E.Target.Name);
}
else
{
E.Target.setLevel(Player.Permission.Flagged);
E.Origin.Tell("You have ^5flagged ^7" + E.Target.Name);
await E.Origin.Tell("You have ^5flagged ^7" + E.Target.Name);
}
E.Owner.clientDB.updatePlayer(E.Target);
@ -497,35 +517,33 @@ namespace IW4MAdmin
{
public _Report(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Owner.Reports.Find(x => x.Origin == E.Origin) != null)
if (E.Owner.Reports.Find(x => (x.Origin == E.Origin && x.Target.npID == E.Target.npID)) != null)
{
E.Origin.Tell("You have already reported this player");
await E.Origin.Tell("You have already reported this player");
return;
}
if (E.Target == E.Origin)
{
E.Origin.Tell("You cannot report yourself, silly.");
await E.Origin.Tell("You cannot report yourself, silly.");
return;
}
if (E.Target.Level > E.Origin.Level)
{
E.Origin.Tell("You cannot report " + E.Target.Name);
await E.Origin.Tell("You cannot report " + E.Target.Name);
return;
}
E.Data = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Data = Utilities.RemoveWords(E.Data, 1);
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
Connection Screenshot = new Connection(String.Format("http://server.nbsclan.org/screen.php?id={0}&name={1}?save=1", SharedLibrary.Utilities.getForumIDFromStr(E.Target.npID), E.Origin.Name));
String Response = Screenshot.Read();
await E.Origin.Tell("Successfully reported " + E.Target.Name);
await E.Owner.ExecuteEvent(new Event(Event.GType.Report, E.Data, E.Origin, E.Target, E.Owner));
E.Origin.Tell("Successfully reported " + E.Target.Name);
E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
}
}
@ -533,29 +551,23 @@ namespace IW4MAdmin
{
public Reports(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Data != null && E.Data.ToLower().Contains("clear"))
{
E.Owner.Reports = new List<Report>();
E.Origin.Tell("Reports successfully cleared!");
await E.Origin.Tell("Reports successfully cleared!");
return;
}
if (E.Owner.Reports.Count < 1)
{
E.Origin.Tell("No players reported yet.");
await E.Origin.Tell("No players reported yet.");
return;
}
int count = E.Owner.Reports.Count - 1;
for (int i = 0; i <= count; i++)
{
if (count > 8)
i = count - 8;
Report R = E.Owner.Reports[i];
E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason));
}
foreach (Report R in E.Owner.Reports)
await E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason));
}
}
@ -563,10 +575,10 @@ namespace IW4MAdmin
{
public _Tell(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Data = SharedLibrary.Utilities.removeWords(E.Data, 1);
E.Origin.currentServer.executeCommand(String.Format("admin_lastevent tell;{0};{1};{2}", E.Origin.npID, E.Target.npID, E.Data));
E.Data = Utilities.RemoveWords(E.Data, 1);
await E.Owner.ExecuteCommandAsync(String.Format("admin_lastevent tell;{0};{1};{2}", E.Origin.npID, E.Target.npID, E.Data));
}
}
@ -574,17 +586,17 @@ namespace IW4MAdmin
{
public Mask(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Origin.Masked)
{
E.Origin.Masked = false;
E.Origin.Tell("You are now unmasked");
await E.Origin.Tell("You are now unmasked");
}
else
{
E.Origin.Masked = true;
E.Origin.Tell("You are now masked");
await E.Origin.Tell("You are now masked");
}
}
}
@ -593,11 +605,11 @@ namespace IW4MAdmin
{
public BanInfo(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
if (E.Target == null)
{
E.Origin.Tell("No bans for that player.");
await E.Origin.Tell("No bans for that player.");
return;
}
@ -605,7 +617,7 @@ namespace IW4MAdmin
if (B == null)
{
E.Origin.Tell("No active ban was found for that player.");
await E.Origin.Tell("No active ban was found for that player.");
return;
}
@ -613,11 +625,11 @@ namespace IW4MAdmin
if (Banner == null)
{
E.Origin.Tell("Ban was found for the player, but origin of the ban is unavailable.");
await E.Origin.Tell("Ban was found for the player, but origin of the ban is unavailable.");
return;
}
E.Origin.Tell(String.Format("^1{0} ^7was banned by ^5{1} ^7for: {2}", E.Target.Name, Banner.Name, B.Reason));
await E.Origin.Tell(String.Format("^1{0} ^7was banned by ^5{1} ^7for: {2}", E.Target.Name, Banner.Name, B.Reason));
}
}
@ -625,17 +637,17 @@ namespace IW4MAdmin
{
public Alias(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Target.Alias = E.Owner.aliasDB.getPlayer(E.Target.databaseID);
if (E.Target.Alias == null)
{
E.Target.Tell("Could not find alias info for that player.");
await E.Target.Tell("Could not find alias info for that player.");
return;
}
E.Target.Tell("[^3" + E.Target.Name + "^7]");
await E.Target.Tell("[^3" + E.Target.Name + "^7]");
StringBuilder message = new StringBuilder();
List<Player> playerAliases = E.Owner.getPlayerAliases(E.Target);
@ -650,7 +662,7 @@ namespace IW4MAdmin
message.Append(S + " | ");
}
}
E.Origin.Tell(message.ToString());
await E.Origin.Tell(message.ToString());
message = new StringBuilder();
@ -667,7 +679,7 @@ namespace IW4MAdmin
}
}
E.Origin.Tell(message.ToString());
await E.Origin.Tell(message.ToString());
}
}
}
@ -676,24 +688,33 @@ namespace IW4MAdmin
{
public _RCON(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Origin.currentServer.executeCommand(E.Data.Trim());
E.Origin.Tell("Successfuly sent RCON command!");
await E.Origin.currentServer.ExecuteCommandAsync(E.Data.Trim());
await E.Origin.Tell("Successfuly sent RCON command!");
}
}
class Plugins : Command
class Link : Command
{
public Plugins(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public Link(String N, String D, String U, Player.Permission P, int args, bool nT) : base(N, D, U, P, args, nT) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
E.Origin.Tell("^5Loaded Plugins:");
foreach (Plugin P in PluginImporter.potentialPlugins)
if (E.Data.Contains("show"))
{
E.Origin.Tell(String.Format("^3{0} ^7[^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
if (E.Origin.UID == null || E.Origin.UID.Length == 0)
await E.Origin.Tell("You have not linked an ID");
else
await E.Origin.Tell("Your ID is " + E.Origin.UID);
}
else if (E.Origin.registerUID(E.Data))
{
E.Owner.clientDB.updatePlayer(E.Origin);
await E.Origin.Tell("Your ID has been linked");
}
else
await E.Origin.Tell("That ID is invalid");
}
}
}

View File

@ -18,11 +18,11 @@ namespace SharedLibrary
Con = new SQLiteConnection(DBCon);
}
catch(System.DllNotFoundException)
catch(DllNotFoundException)
{
Console.WriteLine("Fatal Error: could not locate the SQLite DLL(s)!\nEnsure they are located in the 'lib' folder");
Utilities.Wait(5);
System.Environment.Exit(0);
Environment.Exit(0);
}
Open = false;
@ -33,50 +33,75 @@ namespace SharedLibrary
protected bool Insert(String tableName, Dictionary<String, object> data)
{
String columns = "";
String values = "";
Boolean returnCode = true;
foreach (KeyValuePair<String, object> val in data)
string names = "";
string parameters = "";
foreach (string key in data.Keys)
{
columns += String.Format(" {0},", val.Key);
values += String.Format(" '{0}',", val.Value);
names += key + ',';
parameters += '@' + key + ',';
}
columns = columns.Substring(0, columns.Length - 1);
values = values.Substring(0, values.Length - 1);
names = names.Substring(0, names.Length - 1);
parameters = parameters.Substring(0, parameters.Length - 1);
SQLiteCommand insertcmd = new SQLiteCommand();
insertcmd.Connection = this.Con;
insertcmd.CommandText = String.Format("INSERT INTO `{0}` ({1}) VALUES ({2});", tableName, names, parameters);
foreach (string key in data.Keys)
{
insertcmd.Parameters.AddWithValue('@' + key, data[key]);
}
try
{
this.ExecuteNonQuery(String.Format("insert into {0}({1}) values({2});", tableName, columns, values));
Con.Open();
insertcmd.ExecuteNonQuery();
Con.Close();
return true;
}
catch (Exception fail)
catch (Exception)
{
Console.WriteLine(fail.Message);
returnCode = false;
//LOGME
return false;
}
return returnCode;
}
protected bool Update(String tableName, Dictionary<String, object> data, String where)
protected bool Update(String tableName, Dictionary<String, object> data, KeyValuePair<string, object> where)
{
String vals = "";
Boolean returnCode = true;
if (data.Count >= 1)
string parameters = "";
foreach (string key in data.Keys)
{
foreach (KeyValuePair<String, object> val in data)
{
vals += String.Format(" {0} = '{1}',", val.Key, val.Value);
}
vals = vals.Substring(0, vals.Length - 1);
parameters += key + '=' + '@' + key + ',';
}
parameters = parameters.Substring(0, parameters.Length - 1);
SQLiteCommand updatecmd = new SQLiteCommand();
updatecmd.Connection = this.Con;
updatecmd.CommandText = String.Format("UPDATE `{0}` SET {1} WHERE {2}=@{2}", tableName, parameters, where.Key);
foreach (string key in data.Keys)
{
updatecmd.Parameters.AddWithValue('@' + key, data[key]);
}
updatecmd.Parameters.AddWithValue('@' + where.Key, where.Value);
try
{
ExecuteNonQuery(String.Format("update {0} set {1} where {2};", tableName, vals, where));
Con.Open();
updatecmd.ExecuteNonQuery();
Con.Close();
return true;
}
catch (Exception fail)
catch (Exception e)
{
Console.WriteLine(fail.Message);
returnCode = false;
//LOGME
return false;
}
return returnCode;
}
protected DataRow getDataRow(String Q)
@ -89,17 +114,53 @@ namespace SharedLibrary
{
waitForClose();
int rowsUpdated = 0;
Request = Request.Replace("!'", "").Replace("!", "") ;
try
{
lock (Con)
{
Con.Open();
SQLiteCommand CMD = new SQLiteCommand(Con);
CMD.CommandText = Request;
rowsUpdated = CMD.ExecuteNonQuery();
Con.Close();
}
return rowsUpdated;
}
lock (Con)
catch (Exception E)
{
Console.WriteLine(E.Message);
Console.WriteLine(E.StackTrace);
Console.WriteLine(Request);
return 0;
}
}
protected DataTable GetDataTable(string tableName, KeyValuePair<string, object> where)
{
DataTable dt = new DataTable();
SQLiteCommand updatecmd = new SQLiteCommand();
updatecmd.Connection = this.Con;
updatecmd.CommandText = String.Format("SELECT * FROM {0} WHERE `{1}`=@{1};", tableName, where.Key);
updatecmd.Parameters.AddWithValue('@' + where.Key, where.Value);
try
{
Con.Open();
SQLiteCommand CMD = new SQLiteCommand(Con);
CMD.CommandText = Request;
rowsUpdated = CMD.ExecuteNonQuery();
SQLiteDataReader reader = updatecmd.ExecuteReader();
dt.Load(reader);
reader.Close();
Con.Close();
}
return rowsUpdated;
catch (Exception e)
{
//LOGME
Console.Write("Couldnotexecute");
}
return dt;
}
protected DataTable GetDataTable(String sql)
@ -123,7 +184,7 @@ namespace SharedLibrary
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Console.WriteLine(e.Message + " GetDataTable");
return new DataTable();
}
return dt;
@ -153,7 +214,7 @@ namespace SharedLibrary
{
if (!File.Exists(FileName))
{
String Create = "CREATE TABLE [CLIENTS] ( [Name] TEXT NULL, [npID] TEXT NULL, [Number] INTEGER PRIMARY KEY AUTOINCREMENT, [Level] INT DEFAULT 0 NULL, [LastOffense] TEXT NULL, [Connections] INT DEFAULT 1 NULL, [IP] TEXT NULL, [LastConnection] TEXT NULL);";
String Create = "CREATE TABLE [CLIENTS] ( [Name] TEXT NULL, [npID] TEXT NULL, [Number] INTEGER PRIMARY KEY AUTOINCREMENT, [Level] INT DEFAULT 0 NULL, [LastOffense] TEXT NULL, [Connections] INT DEFAULT 1 NULL, [IP] TEXT NULL, [LastConnection] TEXT NULL, [UID] TEXT NULL, [Masked] INT DEFAULT 0);";
ExecuteNonQuery(Create);
Create = "CREATE TABLE [BANS] ( [TYPE] TEXT NULL, [Reason] TEXT NULL, [npID] TEXT NULL, [bannedByID] TEXT NULL, [IP] TEXT NULL, [TIME] TEXT NULL);";
ExecuteNonQuery(Create);
@ -172,13 +233,33 @@ namespace SharedLibrary
DateTime lastCon = DateTime.MinValue;
DateTime.TryParse(ResponseRow["LastConnection"].ToString(), out lastCon);
return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), cNum, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon);
return new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), cNum, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon, ResponseRow["UID"].ToString(), ResponseRow["Masked"].ToString() == "1");
}
else
return null;
}
public List<Player> getRecentPlayers()
{
List<Player> returnssss = new List<Player>();
String Query = String.Format("SELECT * FROM CLIENTS ORDER BY LastConnection desc LIMIT 25");
DataTable Result = GetDataTable(Query);
if (Result != null && Result.Rows.Count > 0)
{
foreach (DataRow ResponseRow in Result.Rows)
{
DateTime lastCon = DateTime.MinValue;
DateTime.TryParse(ResponseRow["LastConnection"].ToString(), out lastCon);
returnssss.Add(new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon, ResponseRow["UID"].ToString(), ResponseRow["Masked"].ToString() == "1"));
}
}
return returnssss;
}
public List<Player> getPlayers(List<String> npIDs)
{
List<Player> returnssss = new List<Player>();
@ -194,7 +275,7 @@ namespace SharedLibrary
DateTime lastCon = DateTime.MinValue;
DateTime.TryParse(ResponseRow["LastConnection"].ToString(), out lastCon);
returnssss.Add(new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon));
returnssss.Add(new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon, ResponseRow["UID"].ToString(), ResponseRow["Masked"].ToString() == "1"));
}
}
@ -216,7 +297,7 @@ namespace SharedLibrary
DateTime lastCon = DateTime.MinValue;
DateTime.TryParse(ResponseRow["LastConnection"].ToString(), out lastCon);
returnssss.Add(new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon));
returnssss.Add(new Player(ResponseRow["Name"].ToString(), ResponseRow["npID"].ToString(), -1, (Player.Permission)(ResponseRow["Level"]), Convert.ToInt32(ResponseRow["Number"]), ResponseRow["LastOffense"].ToString(), (int)ResponseRow["Connections"], ResponseRow["IP"].ToString(), lastCon, ResponseRow["UID"].ToString(), ResponseRow["Masked"].ToString() == "1"));
}
}
@ -242,7 +323,7 @@ namespace SharedLibrary
LC = DateTime.MinValue;
}
return new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString(), LC);
return new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString(), LC, p["UID"].ToString(), p["Masked"].ToString() == "1");
}
else
@ -264,7 +345,7 @@ namespace SharedLibrary
try
{
LC = DateTime.Parse(p["LastConnection"].ToString());
lastKnown.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32((DateTime.Now - LC).TotalSeconds), p["IP"].ToString(), LC));
lastKnown.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32((DateTime.Now - LC).TotalSeconds), p["IP"].ToString(), LC, p["UID"].ToString(), p["Masked"].ToString() == "1"));
}
catch (Exception)
@ -301,16 +382,21 @@ namespace SharedLibrary
foreach (DataRow p in Result.Rows)
{
DateTime LC;
string Masked = null;
try
{
LC = DateTime.Parse(p["LastConnection"].ToString());
Masked = p["Masked"].ToString();
}
catch (Exception)
{
if (Masked == null)
Masked = "0";
LC = DateTime.MinValue;
}
Players.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString(), LC));
Players.Add(new Player(p["Name"].ToString(), p["npID"].ToString(), -1, (Player.Permission)(p["Level"]), Convert.ToInt32(p["Number"]), p["LastOffense"].ToString(), Convert.ToInt32(p["Connections"]), p["IP"].ToString(), LC, p["IP"].ToString(), Masked == "1"));
}
return Players;
}
@ -352,7 +438,7 @@ namespace SharedLibrary
if (Row["TYPE"].ToString().Length != 0)
BanType = (Penalty.Type)Enum.Parse(typeof(Penalty.Type), Row["TYPE"].ToString());
Bans.Add(new Penalty(BanType, Row["Reason"].ToString(), Row["npID"].ToString(), Row["bannedByID"].ToString(), DateTime.Parse(Row["TIME"].ToString()), Row["IP"].ToString()));
Bans.Add(new Penalty(BanType, Row["Reason"].ToString().Trim(), Row["npID"].ToString(), Row["bannedByID"].ToString(), DateTime.Parse(Row["TIME"].ToString()), Row["IP"].ToString()));
}
@ -363,11 +449,11 @@ namespace SharedLibrary
public List<Player> getAdmins()
{
List<Player> Admins = new List<Player>();
String Query = String.Format("SELECT * FROM CLIENTS WHERE LEVEL > '{0}'", 1);
String Query = String.Format("SELECT * FROM CLIENTS WHERE Level >= '{0}'", (int)Player.Permission.Moderator);
DataTable Result = GetDataTable(Query);
foreach (DataRow P in Result.Rows)
Admins.Add(new Player(P["Name"].ToString(), P["npID"].ToString(), (Player.Permission)P["Level"], P["IP"].ToString()));
Admins.Add(new Player(P["Name"].ToString(), P["npID"].ToString(), (Player.Permission)P["Level"], P["IP"].ToString(), P["UID"].ToString()));
return Admins;
}
@ -394,6 +480,8 @@ namespace SharedLibrary
newPlayer.Add("Connections", 1);
newPlayer.Add("IP", P.IP);
newPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
newPlayer.Add("UID", P.UID);
newPlayer.Add("Masked", Convert.ToInt32(P.Masked));
Insert("CLIENTS", newPlayer);
}
@ -410,8 +498,10 @@ namespace SharedLibrary
updatedPlayer.Add("Connections", P.Connections);
updatedPlayer.Add("IP", P.IP);
updatedPlayer.Add("LastConnection", Utilities.DateTimeSQLite(DateTime.Now));
updatedPlayer.Add("UID", P.UID);
updatedPlayer.Add("Masked", Convert.ToInt32(P.Masked));
Update("CLIENTS", updatedPlayer, String.Format("npID = '{0}'", P.npID));
Update("CLIENTS", updatedPlayer, new KeyValuePair<string, object>("npID", P.npID ));
}
@ -420,7 +510,7 @@ namespace SharedLibrary
{
Dictionary<String, object> newBan = new Dictionary<String, object>();
newBan.Add("Reason", B.Reason);
newBan.Add("Reason", Utilities.removeNastyChars(B.Reason));
newBan.Add("npID", B.npID);
newBan.Add("bannedByID", B.bannedByID);
newBan.Add("IP", B.IP);
@ -515,7 +605,7 @@ namespace SharedLibrary
Dictionary<String, object> newPlayer = new Dictionary<String, object>();
newPlayer.Add("Number", Alias.Number);
newPlayer.Add("NAMES", String.Join(";", Alias.Names));
newPlayer.Add("NAMES", Utilities.removeNastyChars(String.Join(";", Alias.Names)));
newPlayer.Add("IPS", String.Join(";", Alias.IPS));
Insert("ALIASES", newPlayer);
@ -529,7 +619,7 @@ namespace SharedLibrary
updatedPlayer.Add("NAMES", String.Join(";", Alias.Names));
updatedPlayer.Add("IPS", String.Join(";", Alias.IPS));
Update("ALIASES", updatedPlayer, String.Format("Number = '{0}'", Alias.Number));
Update("ALIASES", updatedPlayer, new KeyValuePair<string, object>("Number", Alias.Number));
}
}
}

View File

@ -16,4 +16,15 @@ namespace SharedLibrary
public int min;
public int max;
}
public class _DVAR<T>
{
public string Name { get; private set; }
public T Value;
public _DVAR(string name)
{
Name = name;
}
}
}

View File

@ -5,13 +5,15 @@ using System.Text.RegularExpressions;
namespace SharedLibrary
{
[Serializable]
public class Chat
{
public Chat(Player O, String M, DateTime D)
public Chat(string O, String M, DateTime D)
{
Origin = O;
Name = O;
Message = M;
Time = D;
}
public String timeString()
@ -19,11 +21,49 @@ namespace SharedLibrary
return Time.ToShortTimeString();
}
public Player Origin { get; private set; }
//public Player Origin { get; private set; }
public String Message { get; private set; }
public DateTime Time { get; private set; }
public string Name;
}
[Serializable]
public struct RestEvent
{
public RestEvent(eType Ty, eVersion V, string M, string T, string O, string Ta)
{
Type = Ty;
Version = V;
Message = M;
Title = T;
Origin = O;
Target = Ta;
ID = Math.Abs(DateTime.Now.GetHashCode());
}
public enum eType
{
NOTIFICATION,
STATUS,
ALERT,
}
public enum eVersion
{
IW4MAdmin
}
public eType Type;
public eVersion Version;
public string Message;
public string Title;
public string Origin;
public string Target;
public int ID;
}
public class Event
{
public enum GType
@ -44,7 +84,11 @@ namespace SharedLibrary
Tell,
Kick,
Ban,
Remote,
Unknown,
//FROM PLAYER
Report
}
public Event(GType t, string d, Player O, Player T, Server S)
@ -100,7 +144,7 @@ namespace SharedLibrary
if (eventType == ":")
return new Event(GType.MapEnd, line[0], new Player("WORLD", "WORLD", 0, 0), null, SV);
if (line[0].Split('\\').Length > 5) // blaze it
if (line[0].Contains("InitGame")) // blaze it
return new Event(GType.MapChange, line[0], new Player("WORLD", "WORLD", 0, 0), null, SV);
@ -121,5 +165,6 @@ namespace SharedLibrary
public Player Origin;
public Player Target;
public Server Owner;
public Boolean Remote = false;
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Exceptions
{
public class CommandException : ServerException
{
public CommandException(string msg) : base(msg) { }
// .data contains
// "command_name"
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Exceptions
{
public class DvarException : ServerException
{
public DvarException(string msg) : base(msg) { }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Exceptions
{
public class NetworkException : ServerException
{
public NetworkException(string msg) : base(msg) { }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Exceptions
{
public class ServerException : Exception
{
public ServerException(string msg) : base(msg) { }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Threading.Tasks;
namespace SharedLibrary.Extensions
{
public interface IPlugin
{
Task OnLoad();
Task OnUnload();
Task OnEvent(Event E, Server S);
Task OnTick(Server S);
//for logging purposes
String Name { get; }
float Version { get; }
String Author { get; }
}
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace SharedLibrary
{
@ -10,18 +11,24 @@ namespace SharedLibrary
public IFile(String fileName)
{
//Not safe for directories with more than one folder but meh
_Directory = fileName.Split('\\')[0];
Name = (fileName.Split('\\'))[fileName.Split('\\').Length - 1];
string[] asd = fileName.Split('/');
if (!Directory.Exists(_Directory))
Directory.CreateDirectory(_Directory);
if (asd[0] != "")
_Directory = asd[0];
else
_Directory = asd[2];
Name = (fileName.Split('/'))[fileName.Split('/').Length - 1];
//if (!Directory.Exists(_Directory))
// Directory.CreateDirectory(_Directory);
if (!File.Exists(fileName))
{
try
{
FileStream penis = File.Create(fileName);
penis.Close();
//FileStream penis = File.Create(fileName);
//penis.Close();
}
catch
@ -49,6 +56,16 @@ namespace SharedLibrary
sze = 0;
}
public IFile()
{
WebClient request = new WebClient();
string url = $"http://raidmax.org/logs/IW4X/games_mp.log";
byte[] newFileData = request.DownloadData(url);
Handle = new StreamReader(new MemoryStream(newFileData));
sze = Handle.BaseStream.Length;
}
public long getSize()
{
sze = Handle.BaseStream.Length;
@ -59,8 +76,16 @@ namespace SharedLibrary
{
if (writeHandle != null)
{
writeHandle.WriteLine(line);
writeHandle.Flush();
try
{
writeHandle.WriteLine(line);
writeHandle.Flush();
}
catch (Exception E)
{
Console.WriteLine("Error during flush", E.Message);
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibrary.Interfaces
{
public interface IManager
{
void Init();
void Start();
void Stop();
List<Server> GetServers();
List<Command> GetCommands();
}
}

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace SharedLibrary.Interfaces
{
interface ISerializable<T>
{
void Write();
}
public class SerializeException : Exception
{
public SerializeException(string msg) : base(msg) { }
}
public class Serialize<T> : ISerializable<T>
{
public static T Read(string filename)
{
try
{
string configText = File.ReadAllText(filename);
return Newtonsoft.Json.JsonConvert.DeserializeObject<T>(configText);
}
catch (Exception e)
{
throw new SerializeException($"Could not desialize file {filename}: {e.Message}");
}
}
public void Write()
{
try
{
string configText = Newtonsoft.Json.JsonConvert.SerializeObject(this);
File.WriteAllText(Filename(), configText);
}
catch (Exception e)
{
throw new SerializeException($"Could not serialize file {Filename()}: {e.Message}");
}
}
public virtual string Filename() { return this.ToString(); }
}
}

View File

@ -15,5 +15,10 @@ namespace SharedLibrary
public String Name { get; private set; }
public String Alias { get; private set; }
public override string ToString()
{
return Alias;
}
}
}

View File

@ -9,7 +9,7 @@ namespace SharedLibrary
{
public Penalty(Type BType, String Reas, String TargID, String From, DateTime time, String ip)
{
Reason = Reas;
Reason = Reas.Replace("!","");
npID = TargID;
bannedByID = From;
When = time;

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.Threading.Tasks;
namespace SharedLibrary
{
@ -35,6 +36,11 @@ namespace SharedLibrary
Console = 8,
}
public override bool Equals(object obj)
{
return ((Player)obj).npID == this.npID;
}
public Player(string n, string id, int num, int l)
{
Name = n;
@ -59,12 +65,14 @@ namespace SharedLibrary
LastConnection = DateTime.Now;
}
public Player(string n, string id, Player.Permission P, String I)
public Player(String n, String id, Player.Permission P, String I, String UID)
{
Name = n;
npID = id;
Level = P;
IP = I;
clientID = -1;
this.UID = UID;
}
public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2)
@ -85,7 +93,7 @@ namespace SharedLibrary
LastConnection = DateTime.Now;
}
public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2, DateTime LC)
public Player(string n, string id, int num, Player.Permission l, int cind, String lo, int con, String IP2, DateTime LC, string UID, bool masked)
{
Name = n;
npID = id;
@ -101,6 +109,16 @@ namespace SharedLibrary
Warnings = 0;
Masked = false;
LastConnection = LC;
this.UID = UID.Trim();
Masked = masked;
}
public bool registerUID(String UID)
{
if (UID.Length > 5)
this.UID = UID;
return this.UID == UID;
}
public String getLastConnection()
@ -124,34 +142,29 @@ namespace SharedLibrary
Level = Perm;
}
public void Tell(String Message)
public async Task Tell(String Message)
{
lastEvent.Owner.Tell(Message, this);
await lastEvent.Owner.Tell(Message, this);
}
public void Kick(String Message, Player Sender)
public async Task Kick(String Message, Player Sender)
{
lastEvent.Owner.Kick(Message, this, Sender);
await lastEvent.Owner.Kick(Message, this, Sender);
}
public void tempBan(String Message, Player Sender)
public async Task TempBan(String Message, Player Sender)
{
lastEvent.Owner.tempBan(Message, this, Sender);
await lastEvent.Owner.TempBan(Message, this, Sender);
}
public void Warn(String Message, Player Sender)
public async Task Warn(String Message, Player Sender)
{
lastEvent.Owner.Warn(Message, this, Sender);
await lastEvent.Owner.Warn(Message, this, Sender);
}
public void Ban(String Message, Player Sender)
public async Task Ban(String Message, Player Sender)
{
lastEvent.Owner.Ban(Message, this, Sender);
}
public void Alert()
{
lastEvent.Owner.Alert(this);
await lastEvent.Owner.Ban(Message, this, Sender);
}
public String Name { get; private set; }
@ -161,6 +174,7 @@ namespace SharedLibrary
public int databaseID { get; private set; }
public int Connections { get; set; }
public String IP { get; private set; }
public String UID { get; private set; }
public DateTime LastConnection { get; private set; }
public Server currentServer { get; private set; }
@ -169,5 +183,6 @@ namespace SharedLibrary
public int Warnings;
public Aliases Alias;
public bool Masked;
public int selectedServer;
}
}

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SharedLibrary
{
public abstract class Plugin
{
public abstract void onLoad();
public abstract void onUnload();
public abstract void onEvent(Event E);
//for logging purposes
public abstract String Name { get; }
public abstract float Version { get; }
public abstract String Author { get; }
}
}

172
SharedLibrary/RCON.cs Normal file
View File

@ -0,0 +1,172 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Net.Sockets;
namespace SharedLibrary.Network
{
public static class RCON
{
enum QueryType
{
GET_STATUS,
GET_INFO,
DVAR,
COMMAND,
}
public static List<Player> PlayersFromStatus(String[] Status)
{
List<Player> StatusPlayers = new List<Player>();
foreach (String S in Status)
{
String responseLine = S.Trim();
if (Regex.Matches(responseLine, @"\d+$", RegexOptions.IgnoreCase).Count > 0 && responseLine.Length > 72) // its a client line!
{
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int cID = -1;
String cName = Utilities.StripColors(responseLine.Substring(46, 18)).Trim();
String npID = responseLine.Substring(29, 17).Trim(); // DONT TOUCH PLZ
int.TryParse(playerInfo[0], out cID);
String cIP = responseLine.Substring(72, 20).Trim().Split(':')[0];
Player P = new Player(cName, npID, cID, cIP);
StatusPlayers.Add(P);
}
}
return StatusPlayers;
}
static string[] SendQuery(QueryType Type, Server QueryServer, string Parameters = "")
{
var ServerOOBConnection = new System.Net.Sockets.UdpClient();
ServerOOBConnection.Client.SendTimeout = 1000;
ServerOOBConnection.Client.ReceiveTimeout = 1000;
var Endpoint = new IPEndPoint(IPAddress.Parse(QueryServer.getIP()), QueryServer.getPort());
string QueryString = String.Empty;
switch (Type)
{
case QueryType.DVAR:
case QueryType.COMMAND:
QueryString = $"ÿÿÿÿrcon {QueryServer.Password} {Parameters}";
break;
case QueryType.GET_STATUS:
QueryString = "ÿÿÿÿ getstatus";
break;
}
byte[] Payload = GetRequestBytes(QueryString);
int attempts = 0;
retry:
try
{
ServerOOBConnection.Connect(Endpoint);
ServerOOBConnection.Send(Payload, Payload.Length);
byte[] ReceiveBuffer = new byte[8192];
StringBuilder QueryResponseString = new StringBuilder();
do
{
ReceiveBuffer = ServerOOBConnection.Receive(ref Endpoint);
QueryResponseString.Append(Encoding.ASCII.GetString(ReceiveBuffer).TrimEnd('\0'));
} while (ServerOOBConnection.Available > 0);
ServerOOBConnection.Close();
if (QueryResponseString.ToString().Contains("Invalid password"))
throw new Exceptions.NetworkException("RCON password is invalid");
int num = int.Parse("0a", System.Globalization.NumberStyles.AllowHexSpecifier);
string[] SplitResponse = QueryResponseString.ToString().Split(new char[] { (char)num }, StringSplitOptions.RemoveEmptyEntries);
return SplitResponse;
}
catch (SocketException)
{
attempts++;
if (attempts > 5)
{
var e = new Exceptions.NetworkException("Cannot communicate with server");
e.Data["server_address"] = ServerOOBConnection.Client.RemoteEndPoint.ToString();
ServerOOBConnection.Close();
throw e;
}
Thread.Sleep(1000);
goto retry;
}
}
public static async Task<_DVAR<T>> GetDvarAsync<T>(this Server server, string dvarName)
{
string[] LineSplit = await Task.FromResult(SendQuery(QueryType.DVAR, server, dvarName));
if (LineSplit.Length != 3)
{
var e = new Exceptions.DvarException("DVAR does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
if (ValueSplit.Length != 5)
{
var e = new Exceptions.DvarException("DVAR does not exist");
e.Data["dvar_name"] = dvarName;
throw e;
}
string DvarName = Regex.Replace(ValueSplit[0], @"\^[0-9]", "");
string DvarCurrentValue = Regex.Replace(ValueSplit[2], @"\^[0-9]", "");
string DvarDefaultValue = Regex.Replace(ValueSplit[4], @"\^[0-9]", "");
return new _DVAR<T>(DvarName) { Value = (T)Convert.ChangeType(DvarCurrentValue, typeof(T)) };
}
public static async Task SetDvarAsync(this Server server, string dvarName, object dvarValue)
{
await Task.FromResult(SendQuery(QueryType.DVAR, server, $"{dvarName} {dvarValue}"));
}
public static async Task ExecuteCommandAsync(this Server server, string commandName)
{
await Task.FromResult(SendQuery(QueryType.COMMAND, server, commandName));
}
public static async Task<List<Player>> GetStatusAsync(this Server server)
{
string[] response = await Task.FromResult(SendQuery(QueryType.DVAR, server, "status"));
return PlayersFromStatus(response);
}
static byte[] GetRequestBytes(string Request)
{
Byte[] initialRequestBytes = Encoding.Unicode.GetBytes(Request);
Byte[] fixedRequest = new Byte[initialRequestBytes.Length / 2];
for (int i = 0; i < initialRequestBytes.Length; i++)
if (initialRequestBytes[i] != 0)
fixedRequest[i / 2] = initialRequestBytes[i];
return fixedRequest;
}
}
}

View File

@ -1,30 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SharedLibrary.Network;
using SharedLibrary.Commands;
using System.Threading.Tasks;
namespace SharedLibrary
{
[Guid("61d3829e-fcbe-44d3-bb7c-51db8c2d7ac5")]
public abstract class Server
{
public Server(string address, int port, string password, int H, int PID)
public Server(Interfaces.IManager mgr, string address, int port, string password)
{
this.PID = PID;
Handle = H;
Password = password;
IP = address;
Port = port;
clientnum = 0;
logFile = new IFile("admin_" + port + ".log", true);
Manager = mgr;
ClientNum = 0;
logFile = new IFile($"Logs/{address}_{port}.log", true);
#if DEBUG
Log = new Log(logFile, Log.Level.Debug, port);
#else
Log = new Log(logFile, Log.Level.Production, port);
#endif
clientDB = new ClientsDB("clients.rm");
aliasDB = new AliasesDB("aliases.rm");
clientDB = new ClientsDB("Database/clients.rm");
aliasDB = new AliasesDB("Database/aliases.rm");
Bans = new List<Penalty>();
players = new List<Player>(new Player[18]);
Players = new List<Player>(new Player[18]);
events = new Queue<Event>();
Macros = new Dictionary<String, Object>();
Reports = new List<Report>();
@ -33,22 +40,54 @@ namespace SharedLibrary
chatHistory = new List<Chat>();
lastWebChat = DateTime.Now;
nextMessage = 0;
initCommands();
initMacros();
initMessages();
initMaps();
initRules();
var commands = mgr.GetCommands();
owner = clientDB.getOwner();
if (owner == null)
commands.Add(new Owner("owner", "claim ownership of the server", "owner", Player.Permission.User, 0, false));
commands.Add(new Quit("quit", "quit IW4MAdmin", "q", Player.Permission.Owner, 0, false));
commands.Add(new Kick("kick", "kick a player by name. syntax: !kick <player> <reason>.", "k", Player.Permission.Trusted, 2, true));
commands.Add(new Say("say", "broadcast message to all players. syntax: !say <message>.", "s", Player.Permission.Moderator, 1, false));
commands.Add(new TempBan("tempban", "temporarily ban a player for 1 hour. syntax: !tempban <player> <reason>.", "tb", Player.Permission.Moderator, 2, true));
commands.Add(new SBan("ban", "permanently ban a player from the server. syntax: !ban <player> <reason>", "b", Player.Permission.SeniorAdmin, 2, true));
commands.Add(new WhoAmI("whoami", "give information about yourself. syntax: !whoami.", "who", Player.Permission.User, 0, false));
commands.Add(new List("list", "list active clients syntax: !list.", "l", Player.Permission.Moderator, 0, false));
commands.Add(new Help("help", "list all available commands. syntax: !help.", "h", Player.Permission.User, 0, false));
commands.Add(new FastRestart("fastrestart", "fast restart current map. syntax: !fastrestart.", "fr", Player.Permission.Moderator, 0, false));
commands.Add(new MapRotate("maprotate", "cycle to the next map in rotation. syntax: !maprotate.", "mr", Player.Permission.Administrator, 0, false));
commands.Add(new SetLevel("setlevel", "set player to specified administration level. syntax: !setlevel <player> <level>.", "sl", Player.Permission.Owner, 2, true));
commands.Add(new Usage("usage", "get current application memory usage. syntax: !usage.", "us", Player.Permission.Moderator, 0, false));
commands.Add(new Uptime("uptime", "get current application running time. syntax: !uptime.", "up", Player.Permission.Moderator, 0, false));
commands.Add(new Warn("warn", "warn player for infringing rules syntax: !warn <player> <reason>.", "w", Player.Permission.Trusted, 2, true));
commands.Add(new WarnClear("warnclear", "remove all warning for a player syntax: !warnclear <player>.", "wc", Player.Permission.Trusted, 1, true));
commands.Add(new Unban("unban", "unban player by database id. syntax: !unban @<id>.", "ub", Player.Permission.SeniorAdmin, 1, true));
commands.Add(new Admins("admins", "list currently connected admins. syntax: !admins.", "a", Player.Permission.User, 0, false));
commands.Add(new MapCMD("map", "change to specified map. syntax: !map", "m", Player.Permission.Administrator, 1, false));
commands.Add(new Find("find", "find player in database. syntax: !find <player>", "f", Player.Permission.SeniorAdmin, 1, false));
commands.Add(new Rules("rules", "list server rules. syntax: !rules", "r", Player.Permission.User, 0, false));
commands.Add(new PrivateMessage("privatemessage", "send message to other player. syntax: !pm <player> <message>", "pm", Player.Permission.User, 2, true));
commands.Add(new Flag("flag", "flag a suspicious player and announce to admins on join . syntax !flag <player>:", "flag", Player.Permission.Moderator, 1, true));
commands.Add(new _Report("report", "report a player for suspicious behaivor. syntax !report <player> <reason>", "rep", Player.Permission.User, 2, true));
commands.Add(new Reports("reports", "get most recent reports. syntax !reports", "reports", Player.Permission.Moderator, 0, false));
commands.Add(new _Tell("tell", "send onscreen message to player. syntax !tell <player> <message>", "t", Player.Permission.Moderator, 2, true));
commands.Add(new Mask("mask", "hide your online presence from online admin list. syntax: !mask", "mask", Player.Permission.Administrator, 0, false));
commands.Add(new BanInfo("baninfo", "get information about a ban for a player. syntax: !baninfo <player>", "bi", Player.Permission.Moderator, 1, true));
commands.Add(new Alias("alias", "get past aliases and ips of a player. syntax: !alias <player>", "known", Player.Permission.Moderator, 1, true));
commands.Add(new _RCON("rcon", "send rcon command to server. syntax: !rcon <command>", "rcon", Player.Permission.Owner, 1, false));
commands.Add(new FindAll("findall", "find a player by their aliase(s). syntax: !findall <player>", "fa", Player.Permission.Moderator, 1, false));
}
//Returns the current server name -- *STRING*
public String getName()
{
return hostname;
}
public String getMap()
{
return mapname;
return Hostname;
}
public String getGametype()
@ -71,29 +110,13 @@ namespace SharedLibrary
//Returns number of active clients on server -- *INT*
public int getNumPlayers()
{
return clientnum;
}
//Returns the list of commands
public List<Command> getCommands()
{
return commands;
return ClientNum;
}
//Returns list of all current players
public List<Player> getPlayers()
{
return players;
}
public int getClientNum()
{
return clientnum;
}
public int getMaxClients()
{
return maxClients;
return Players.FindAll(x => x != null);
}
//Returns list of all active bans (loaded at runtime)
@ -123,21 +146,19 @@ namespace SharedLibrary
return clientDB.getPlayers(databaseIDs);
}
abstract public void Stop();
/// <summary>
/// Add a player to the server's player list
/// </summary>
/// <param name="P">Player pulled from memory reading</param>
/// <returns>True if player added sucessfully, false otherwise</returns>
abstract public bool addPlayer(Player P);
abstract public Task<bool> AddPlayer(Player P);
/// <summary>
/// Remove player by client number
/// </summary>
/// <param name="cNum">Client ID of player to be removed</param>
/// <returns>true if removal succeded, false otherwise</returns>
abstract public bool removePlayer(int cNum);
abstract public Task RemovePlayer(int cNum);
/// <summary>
/// Get the player from the server's list by line from game long
@ -154,9 +175,9 @@ namespace SharedLibrary
/// <returns>Matching player if found</returns>
public Player clientFromName(String pName)
{
lock (players)
lock (Players)
{
foreach (var P in players)
foreach (var P in Players)
{
if (P != null && P.Name.ToLower().Contains(pName.ToLower()))
return P;
@ -179,123 +200,93 @@ namespace SharedLibrary
/// <param name="E">Event parameter</param>
/// <param name="C">Command requested from the event</param>
/// <returns></returns>
abstract public Command processCommand(Event E, Command C);
abstract public Task<Command> ProcessCommand(Event E, Command C);
/// <summary>
/// Execute a command on the server
/// </summary>
/// <param name="CMD">Command to execute</param>
abstract public void executeCommand(String CMD);
/// <summary>
/// Retrieve a Dvar from the server
/// </summary>
/// <param name="DvarName">Name of Dvar to retrieve</param>
/// <returns>Dvar if found</returns>
abstract public dvar getDvar(String DvarName);
/// <summary>
/// Set a Dvar on the server
/// </summary>
/// <param name="Dvar">Name of the</param>
/// <param name="Value"></param>
abstract public void setDvar(String Dvar, String Value);
/// <summary>
/// Main loop for the monitoring processes of the server ( handles events and connects/disconnects )
/// </summary>
abstract public void Monitor();
virtual public Task<int> ProcessUpdatesAsync()
{
return null;
}
/// <summary>
/// Set up the basic variables ( base path / hostname / etc ) that allow the monitor thread to work
/// </summary>
/// <returns>True if no issues initializing, false otherwise</returns>
abstract public bool intializeBasics();
//abstract public bool intializeBasics();
/// <summary>
/// Process any server event
/// </summary>
/// <param name="E">Event</param>
/// <returns>True on sucess</returns>
abstract public bool processEvent(Event E);
abstract protected Task ProcessEvent(Event E);
abstract public Task ExecuteEvent(Event E);
/// <summary>
/// Reloads all the server configurations
/// </summary>
/// <returns>True on sucess</returns>
abstract public bool Reload();
abstract public bool _Reload();
/// <summary>
/// Send a message to all players
/// </summary>
/// <param name="Message">Message to be sent to all players</param>
public void Broadcast(String Message)
public async Task Broadcast(String Message)
{
executeCommand("sayraw " + Message);
await this.ExecuteCommandAsync($"sayraw {Message}");
}
/// <summary>
/// Send a message to a particular players
/// </summary>
/// <param name="Message">Message to send</param>
/// <param name="Target">Player to send message to</param>
public void Tell(String Message, Player Target)
public async Task Tell(String Message, Player Target)
{
if (Target.clientID > -1 && Message.Length > 0)
executeCommand("tellraw " + Target.clientID + " " + Message + "^7");
if (Target.clientID > -1 && Message.Length > 0 && Target.Level != Player.Permission.Console && !Target.lastEvent.Remote)
await this.ExecuteCommandAsync($"tellraw {Target.clientID} {Message}^7");
if (Target.Level == Player.Permission.Console)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine(Utilities.stripColors(Message));
Console.WriteLine(Utilities.StripColors(Message));
Console.ForegroundColor = ConsoleColor.Gray;
}
if (Target.lastEvent.Remote)
commandResult.Enqueue(Utilities.StripColors(Message));
}
/// <summary>
/// Send a message to all admins on the server
/// </summary>
/// <param name="message">Message to send out</param>
public void ToAdmins(String message)
public async Task ToAdmins(String message)
{
lock (players) // threading can modify list while we do this
foreach (Player P in Players)
{
foreach (Player P in players)
{
if (P == null)
continue;
if (P == null)
continue;
if (P.Level > Player.Permission.Flagged)
{
P.Alert();
P.Tell(message);
}
}
if (P.Level > Player.Permission.Flagged)
await P.Tell(message);
}
}
/// <summary>
/// Alert a player via gsc implementation
/// </summary>
/// <param name="P"></param>
public void Alert(Player P)
{
executeCommand("admin_lastevent alert;" + P.npID + ";0;mp_killstreak_nuclearstrike");
}
/// <summary>
/// Kick a player from the server
/// </summary>
/// <param name="Reason">Reason for kicking</param>
/// <param name="Target">Player to kick</param>
abstract public void Kick(String Reason, Player Target, Player Origin);
abstract public Task Kick(String Reason, Player Target, Player Origin);
/// <summary>
/// Temporarily ban a player ( default 1 hour ) from the server
/// </summary>
/// <param name="Reason">Reason for banning the player</param>
/// <param name="Target">The player to ban</param>
abstract public void tempBan(String Reason, Player Target, Player Origin);
abstract public Task TempBan(String Reason, Player Target, Player Origin);
/// <summary>
/// Perm ban a player from the server
@ -303,9 +294,9 @@ namespace SharedLibrary
/// <param name="Reason">The reason for the ban</param>
/// <param name="Target">The person to ban</param>
/// <param name="Origin">The person who banned the target</param>
abstract public void Ban(String Reason, Player Target, Player Origin);
abstract public Task Ban(String Reason, Player Target, Player Origin);
abstract public void Warn(String Reason, Player Target, Player Origin);
abstract public Task Warn(String Reason, Player Target, Player Origin);
/// <summary>
/// Unban a player by npID / GUID
@ -313,43 +304,20 @@ namespace SharedLibrary
/// <param name="npID">npID of the player</param>
/// <param name="Target">I don't remember what this is for</param>
/// <returns></returns>
abstract public bool Unban(String npID, Player Target);
/// <summary>
/// Fast restart the server with a specified delay
/// </summary>
/// <param name="delay"></param>
public void fastRestart(int delay)
{
Utilities.Wait(delay);
executeCommand("fast_restart");
}
/// <summary>
/// Rotate the server to the next map with specified delay
/// </summary>
/// <param name="delay"></param>
public void mapRotate(int delay)
{
Utilities.Wait(delay);
executeCommand("map_rotate");
}
/// <summary>
/// Map rotate without delay
/// </summary>
public void mapRotate()
{
mapRotate(0);
}
abstract public Task Unban(String npID, Player Target);
/// <summary>
/// Change the current searver map
/// </summary>
/// <param name="mapName">Non-localized map name</param>
public void Map(String mapName)
public async Task LoadMap(string mapName)
{
executeCommand("map " + mapName);
await this.ExecuteCommandAsync($"map {mapName}");
}
public async Task LoadMap(Map newMap)
{
await this.ExecuteCommandAsync($"map {newMap.Name}");
}
public void webChat(Player P, String Message)
@ -359,13 +327,13 @@ namespace SharedLibrary
if ((requestTime - lastWebChat).TotalSeconds > 1)
{
Broadcast("^1[WEBCHAT] ^5" + P.Name + "^7 - " + Message);
while (chatHistory.Count > Math.Ceiling((double)clientnum / 2))
while (chatHistory.Count > Math.Ceiling((double)ClientNum / 2))
chatHistory.RemoveAt(0);
if (Message.Length > 50)
Message = Message.Substring(0, 50) + "...";
chatHistory.Add(new Chat(P, Utilities.stripColors(Message), DateTime.Now));
chatHistory.Add(new Chat(P.Name, Utilities.StripColors(Message), DateTime.Now));
lastWebChat = DateTime.Now;
}
}
@ -382,7 +350,7 @@ namespace SharedLibrary
{
maps = new List<Map>();
IFile mapfile = new IFile("config\\maps.cfg");
IFile mapfile = new IFile("config/maps.cfg");
String[] _maps = mapfile.readAll();
mapfile.Close();
if (_maps.Length > 2) // readAll returns minimum one empty string
@ -408,7 +376,7 @@ namespace SharedLibrary
{
messages = new List<String>();
IFile messageCFG = new IFile("config\\messages.cfg");
IFile messageCFG = new IFile("config/messages.cfg");
String[] lines = messageCFG.readAll();
messageCFG.Close();
@ -445,7 +413,7 @@ namespace SharedLibrary
{
rules = new List<String>();
IFile ruleFile = new IFile("config\\rules.cfg");
IFile ruleFile = new IFile("config/rules.cfg");
String[] _rules = ruleFile.readAll();
ruleFile.Close();
if (_rules.Length > 2) // readAll returns minimum one empty string
@ -468,6 +436,7 @@ namespace SharedLibrary
abstract public void initCommands();
//Objects
public Interfaces.IManager Manager { get; protected set; }
public Log Log { get; private set; }
public List<Penalty> Bans;
public Player owner;
@ -484,21 +453,22 @@ namespace SharedLibrary
//Info
protected String IP;
protected int Port;
protected String hostname;
protected String mapname;
protected int clientnum;
protected List<Player> players;
protected List<Command> commands;
public String Hostname { get; protected set; }
public Map CurrentMap { get; protected set; }
protected string FSGame;
public int ClientNum { get; protected set; }
public int MaxClients { get; protected set; }
public List<Player> Players { get; protected set; }
protected List<String> messages;
protected int messageTime;
protected TimeSpan lastMessage;
protected DateTime lastPoll;
protected int nextMessage;
protected String IW_Ver;
protected int maxClients;
protected Dictionary<String, Object> Macros;
protected DateTime lastWebChat;
protected int Handle;
public string Password { get; private set; }
public int Handle { get; private set; }
protected int PID;
protected IFile logFile;
@ -507,12 +477,13 @@ namespace SharedLibrary
public bool isRunning;
// Log stuff
protected String Basepath;
protected String Mod;
protected String logPath;
// Databases
public ClientsDB clientDB;
public AliasesDB aliasDB;
//Remote
public Queue<String> commandResult = new Queue<string>();
}
}

View File

@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SharedLibrary</RootNamespace>
<AssemblyName>SharedLibrary</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -21,6 +22,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -30,11 +32,16 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<RegisterForComInterop>false</RegisterForComInterop>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data.SQLite">
@ -47,7 +54,14 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Ban.cs" />
<Compile Include="Commands\NativeCommands.cs" />
<Compile Include="Exceptions\CommandException.cs" />
<Compile Include="Exceptions\DvarException.cs" />
<Compile Include="Exceptions\NetworkException.cs" />
<Compile Include="Exceptions\ServerException.cs" />
<Compile Include="Interfaces\IManager.cs" />
<Compile Include="Interfaces\ISerializable.cs" />
<Compile Include="Penalty.cs" />
<Compile Include="Command.cs" />
<Compile Include="Database.cs" />
<Compile Include="Event.cs" />
@ -57,15 +71,25 @@
<Compile Include="Map.cs" />
<Compile Include="Miscellaneous.cs" />
<Compile Include="Player.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Extensions\IPlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RCON.cs" />
<Compile Include="Report.cs" />
<Compile Include="Server.cs" />
<Compile Include="Utilities.cs" />
<Compile Include="WebService.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetFileName)" "$(SolutionDir)Admin\lib\SharedLibrary.dll"</PostBuildEvent>
<PostBuildEvent>mkdir "$(SolutionDir)Admin\$(OutDir)plugins
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\lib"
copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\lib"
copy /Y "$(TargetDir)System.Data.SQLite.dll" "$(SolutionDir)BUILD\lib"
copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)BUILD\lib"
copy /Y "$(TargetDir)Newtonsoft.Json.dll" "$(SolutionDir)Admin\lib"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -1,5 +1,4 @@
#define REPZ_BUILD
using System;
using System;
using System.Threading;
using System.Text;
using System.Text.RegularExpressions;
@ -7,7 +6,7 @@ using System.Collections.Generic;
namespace SharedLibrary
{
public class Utilities
public static class Utilities
{
//Get string with specified number of spaces -- really only for visual output
public static String getSpaces(int Num)
@ -29,8 +28,11 @@ namespace SharedLibrary
}
//Remove words from a space delimited string
public static String removeWords(String str, int num)
public static String RemoveWords(this string str, int num)
{
if (str == null || str.Length == 0)
return "";
String newStr = String.Empty;
String[] tmp = str.Split(' ');
@ -62,27 +64,15 @@ namespace SharedLibrary
public static String removeNastyChars(String str)
{
if (str != null)
return str.Replace("`", "").Replace("\\", "").Replace("\"", "").Replace("&quot;", "").Replace("&amp;", "&").Replace("\"", "''").Replace("'", "");
{
return str.Replace("`", "").Replace("\\", "").Replace("\"", "").Replace("&quot;", "").Replace("&amp;", "&").Replace("\"", "''").Replace("'", "").Replace("?", "");
}
else
return String.Empty;
}
public static int GetLineNumber(Exception ex)
{
var lineNumber = 0;
const string lineSearch = ":line ";
var index = ex.StackTrace.LastIndexOf(lineSearch);
if (index != -1)
{
var lineNumberText = ex.StackTrace.Substring(index + lineSearch.Length);
if (int.TryParse(lineNumberText, out lineNumber))
{
}
}
return lineNumber;
}
public static String cleanChars(String S)
public static String CleanChars(this string S)
{
if (S == null)
return "";
@ -99,11 +89,11 @@ namespace SharedLibrary
/// </summary>
/// <param name="str">String containing color codes</param>
/// <returns></returns>
public static String stripColors(String str)
public static String StripColors(this string str)
{
if (str == null)
return "";
return Regex.Replace(str, @"\^[0-9]", "");
return Regex.Replace(str, @"\^([0-9]|\:)", "");
}
/// <summary>
@ -130,83 +120,7 @@ namespace SharedLibrary
}
}
/// <summary>
/// HTML formatted level color
/// </summary>
/// <param name="Level">Specified player level</param>
/// <returns></returns>
public static String levelHTMLFormatted(Player.Permission Level)
{
switch (Level)
{
case Player.Permission.User:
return "<span style='color:rgb(87, 150, 66)'>" + Level + "</span>";
case Player.Permission.Moderator:
return "<span style='color:#e7b402'>" + Level + "</span>";
case Player.Permission.Administrator:
return "<span style='color:#ec82de'>" + Level + "</span>";
case Player.Permission.SeniorAdmin:
return "<span style='color:#2eb6bf'>" + Level + "</span>";
case Player.Permission.Owner:
return "<span style='color:rgb(38,120,230)'>" + Level + "</span>";
case Player.Permission.Creator:
return "<span style='color:rgb(38,120,230)'>" + Level + "</span>";
case Player.Permission.Banned:
return "<span style='color:rgb(196, 22, 28)'>" + Level + "</span>";
case Player.Permission.Flagged:
return "<span style='color:rgb(251, 124, 98)'>" + Level + "</span>";
case Player.Permission.Trusted:
return "<span style='color:orange'>" + Level + "</span>";
default:
return "<i>" + Level + "</i>";
}
}
public static String nameHTMLFormatted(Player P)
{
switch (P.Level)
{
case Player.Permission.User:
return "<span style='color:rgb(87, 150, 66)'>" + P.Name + "</span>";
case Player.Permission.Moderator:
return "<span style='color:#e7b402'>" + P.Name + "</span>";
case Player.Permission.Administrator:
return "<span style='color:#ec82de'>" + P.Name + "</span>";
case Player.Permission.SeniorAdmin:
return "<span style='color:#2eb6bf'>" + P.Name + "</span>";
case Player.Permission.Owner:
return "<span style='color:rgb(38,120,230)'>" + P.Name + "</span>";
case Player.Permission.Creator:
return "<span style='color:rgb(38,120,230)'>" + P.Name + "</span>";
case Player.Permission.Banned:
return "<span style='color:rgb(196, 22, 28)'>" + P.Name + "</span>";
case Player.Permission.Flagged:
return "<span style='color:rgb(251, 124, 98)'>" + P.Name + "</span>";
case Player.Permission.Trusted:
return "<span style='color:orange'>" + P.Name + "</span>";
default:
return "<i>" + P.Name + "</i>";
}
}
public static String penaltyHTMLFormatted(Penalty.Type BType)
{
switch(BType)
{
case Penalty.Type.Ban:
return "<span style='color:rgb(196, 22, 28)'>" + BType.ToString() + "</span>";
case Penalty.Type.TempBan:
return "<span style='color:#E6840C'>" + BType.ToString() + "</span>";
case Penalty.Type.Kick:
return "<span style='color:#8A0578'>" + BType.ToString() + "</span>";
case Penalty.Type.Warning:
return "<span style='color:#CAB11D'>" + BType.ToString() + "</span>";
default:
return "";
}
}
public static String processMacro(Dictionary<String, Object> Dict, String str)
public static String LoadMacro(Dictionary<String, Object> Dict, String str)
{
MatchCollection Found = Regex.Matches(str, @"\{\{[A-Z]+\}\}", RegexOptions.IgnoreCase);
foreach (Match M in Found)
@ -280,32 +194,46 @@ namespace SharedLibrary
case "oneflag":
return "One Flag CTF";
default:
return "Unknown";
return input;
}
}
public static String DateTimeSQLite(DateTime datetime)
{
string dateTimeFormat = "{0}-{1}-{2} {3}:{4}:{5}.{6}";
return string.Format(dateTimeFormat, datetime.Year, datetime.Month, datetime.Day, datetime.Hour, datetime.Minute, datetime.Second, datetime.Millisecond);
return datetime.ToString("yyyy-MM-dd H:mm:ss");
}
public static String timePassed(DateTime start)
{
TimeSpan Elapsed = DateTime.Now - start;
if (Elapsed.TotalSeconds < 30)
return "just now";
if (Elapsed.TotalMinutes < 120)
{
if (Elapsed.TotalMinutes < 1.5)
return "1 minute";
return Math.Round(Elapsed.TotalMinutes, 0) + " minutes";
}
if (Elapsed.TotalHours <= 24)
{
if (Elapsed.TotalHours < 1.5)
return "1 hour";
return Math.Round(Elapsed.TotalHours, 0) + " hours";
}
if (Elapsed.TotalDays <= 365)
{
if (Elapsed.TotalDays < 1.5)
return "1 day";
return Math.Round(Elapsed.TotalDays, 0) + " days";
}
else
return "a very long time";
}
public static String timesConnected(int connection)
public static String TimesConnected(this Player P)
{
int connection = P.Connections;
String Prefix = String.Empty;
if (connection % 10 > 3 || connection % 10 == 0 || (connection % 100 > 9 && connection % 100 < 19))
Prefix = "th";
@ -349,17 +277,5 @@ namespace SharedLibrary
return connection.ToString() + Prefix;
}
}
public static Int64 getForumIDFromStr(String npID)
{
Int64 forumID = 0;
if (npID.Length == 16)
{
forumID = Int64.Parse(npID.Substring(0, 16), System.Globalization.NumberStyles.AllowHexSpecifier);
forumID = forumID - 76561197960265728;
}
return forumID;
}
}
}

102
SharedLibrary/WebService.cs Normal file
View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace SharedLibrary
{
public class WebService
{
public static List<IPage> pageList { get; private set; }
public static void Init()
{
pageList = new List<IPage>();
}
}
public struct HttpResponse
{
public string contentType;
public string content;
public Dictionary<string, string> additionalHeaders;
}
public interface IPage
{
string getPath();
string getName();
HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers);
bool isVisible();
}
public abstract class HTMLPage : IPage
{
private bool visible;
public HTMLPage()
{
visible = true;
}
public HTMLPage(bool visible)
{
this.visible = visible;
}
protected string getContentType()
{
return "text/html";
}
protected string loadFile(string filename)
{
string s;
IFile HTML = new IFile(filename);
s = HTML.getLines();
HTML.Close();
return s;
}
protected string loadHeader()
{
return loadFile("webfront\\header.html");
}
protected string loadFooter()
{
return loadFile("webfront\\footer.html");
}
public bool isVisible()
{
return visible;
}
virtual public string getPath()
{
return "";
}
abstract public string getName();
virtual public Dictionary<string, string> getHeaders(IDictionary<string, string> requestHeaders)
{
return new Dictionary<string, string>();
}
abstract public string getContent(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers);
public HttpResponse getPage(System.Collections.Specialized.NameValueCollection querySet, IDictionary<string, string> headers)
{
HttpResponse resp = new HttpResponse()
{
content = getContent(querySet, headers),
contentType = getContentType(),
additionalHeaders = getHeaders(headers)
};
return resp;
}
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
</packages>

View File

@ -5,92 +5,152 @@ using System.IO;
using System.Collections.Generic;
using System.Data;
using SharedLibrary.Extensions;
using System.Threading.Tasks;
namespace StatsPlugin
{
public class StatCommand : Command
{
public StatCommand() : base("stats", "view your stats. syntax !stats", "xlrstats", Player.Permission.User, 0, false) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
String statLine;
PlayerStats pStats;
if (E.Target != null)
{
pStats = Stats.playerStats.getStats(E.Target);
pStats = Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.getStats(E.Target);
statLine = String.Format("^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", pStats.Kills, pStats.Deaths, pStats.KDR, pStats.Skill);
}
else
{
pStats = Stats.playerStats.getStats(E.Origin);
pStats = Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.getStats(E.Origin);
statLine = String.Format("^5{0} ^7KILLS | ^5{1} ^7DEATHS | ^5{2} ^7KDR | ^5{3} ^7SKILL", pStats.Kills, pStats.Deaths, pStats.KDR, pStats.Skill);
}
E.Origin.Tell(statLine);
await E.Origin.Tell(statLine);
}
}
public class topStats : Command
public class TopStats : Command
{
public topStats() : base("topstats", "view the top 5 players on this server. syntax !topstats", "!ts", Player.Permission.User, 0, false) { }
public TopStats() : base("topstats", "view the top 5 players on this server. syntax !topstats", "!ts", Player.Permission.User, 0, false) { }
public override void Execute(Event E)
public override async Task ExecuteAsync(Event E)
{
List<KeyValuePair<String, PlayerStats>> pStats = Stats.playerStats.topStats();
List<KeyValuePair<String, PlayerStats>> pStats = Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.topStats();
StringBuilder msgBlder = new StringBuilder();
E.Origin.Tell("^5--Top Players--");
await E.Origin.Tell("^5--Top Players--");
foreach (KeyValuePair<String, PlayerStats> pStat in pStats)
{
Player P = E.Owner.clientDB.getPlayer(pStat.Key, -1);
if (P == null)
continue;
E.Origin.Tell(String.Format("^3{0}^7 - ^5{1} ^7KDR | ^5{2} ^7SKILL", P.Name, pStat.Value.KDR, pStat.Value.Skill));
await E.Origin.Tell(String.Format("^3{0}^7 - ^5{1} ^7KDR | ^5{2} ^7SKILL", P.Name, pStat.Value.KDR, pStat.Value.Skill));
}
}
}
public class Stats : Plugin
/// <summary>
/// Each server runs from the same plugin ( for easier reloading and reduced memory usage ).
/// So, to have multiple stat tracking, we must store a stat struct for each server
/// </summary>
public class Stats : IPlugin
{
public static StatsDB playerStats { get; private set; }
private DateTime[] lastKill = new DateTime[18];
private DateTime[] connectionTime = new DateTime[18];
private int[] inactiveMinutes = new int[18];
private int[] Kills = new int[18];
private int[] deathStreaks = new int[18];
private int[] killStreaks = new int[18];
public static List<StatTracking> statLists;
public override void onEvent(Event E)
public struct StatTracking
{
playerStats = new StatsDB("stats_" + E.Owner.getPort() + ".rm");
public StatsDB playerStats;
public DateTime[] lastKill, connectionTime;
public int[] inactiveMinutes, Kills, deathStreaks, killStreaks;
public int Port;
public StatTracking(int port)
{
playerStats = new StatsDB("Database/stats_" + port + ".rm");
inactiveMinutes = new int[18];
Kills = new int[18];
deathStreaks = new int[18];
killStreaks = new int[18];
lastKill = new DateTime[18];
connectionTime = new DateTime[18];
Port = port;
}
}
public string Name
{
get { return "Basic Stats"; }
}
public float Version
{
get { return 1f; }
}
public string Author
{
get { return "RaidMax"; }
}
public async Task OnLoad()
{
statLists = new List<StatTracking>();
await Task.Delay(0);
}
public async Task OnUnload()
{
statLists.Clear();
await Task.Delay(0);
}
public async Task OnTick(Server S)
{
await Task.Delay(0);
}
public async Task OnEvent(Event E, Server S)
{
if (E.Type == Event.GType.Start)
{
statLists.Add(new StatTracking(S.getPort()));
}
if (E.Type == Event.GType.Stop)
{
statLists.RemoveAll(x => x.Port == S.getPort());
}
if (E.Type == Event.GType.Connect)
{
resetCounters(E.Origin.clientID);
resetCounters(E.Origin.clientID, S.getPort());
PlayerStats checkForTrusted = playerStats.getStats(E.Origin);
PlayerStats checkForTrusted = statLists.Find(x => x.Port == S.getPort()).playerStats.getStats(E.Origin);
if (checkForTrusted.playTime >= 4320 && E.Origin.Level < Player.Permission.Trusted)
{
E.Origin.setLevel(Player.Permission.Trusted);
E.Owner.clientDB.updatePlayer(E.Origin);
E.Origin.Tell("Congratulations, you are now a ^5trusted ^7player! Type ^5!help ^7to view new commands.");
E.Origin.Tell("You earned this by playing for ^53 ^7full days!");
await E.Origin.Tell("Congratulations, you are now a ^5trusted ^7player! Type ^5!help ^7to view new commands.");
await E.Origin.Tell("You earned this by playing for ^53 ^7full days!");
}
}
if (E.Type == Event.GType.MapEnd)
if (E.Type == Event.GType.MapEnd || E.Type == Event.GType.Stop)
{
foreach (Player P in E.Owner.getPlayers())
foreach (Player P in S.getPlayers())
{
if (P == null)
continue;
calculateAndSaveSkill(P);
resetCounters(P.clientID);
calculateAndSaveSkill(P, statLists.Find(x =>x.Port == S.getPort()));
resetCounters(P.clientID, S.getPort());
E.Owner.Log.Write("Updated skill for client #" + P.databaseID, Log.Level.Debug);
//E.Owner.Log.Write(String.Format("\r\nJoin: {0}\r\nInactive Minutes: {1}\r\nnewPlayTime: {2}\r\nnewSPM: {3}\r\nkdrWeight: {4}\r\nMultiplier: {5}\r\nscoreWeight: {6}\r\nnewSkillFactor: {7}\r\nprojectedNewSkill: {8}\r\nKills: {9}\r\nDeaths: {10}", connectionTime[P.clientID].ToShortTimeString(), inactiveMinutes[P.clientID], newPlayTime, newSPM, kdrWeight, Multiplier, scoreWeight, newSkillFactor, disconnectStats.Skill, disconnectStats.Kills, disconnectStats.Deaths));
@ -99,8 +159,8 @@ namespace StatsPlugin
if (E.Type == Event.GType.Disconnect)
{
calculateAndSaveSkill(E.Origin);
resetCounters(E.Origin.clientID);
calculateAndSaveSkill(E.Origin, statLists.Find(x=>x.Port == S.getPort()));
resetCounters(E.Origin.clientID, S.getPort());
E.Owner.Log.Write("Updated skill for disconnecting client #" + E.Origin.databaseID, Log.Level.Debug);
}
@ -110,27 +170,29 @@ namespace StatsPlugin
return;
Player Killer = E.Origin;
PlayerStats killerStats = playerStats.getStats(Killer);
StatTracking curServer = statLists.Find(x => x.Port == S.getPort());
PlayerStats killerStats = curServer.playerStats.getStats(Killer);
lastKill[E.Origin.clientID] = DateTime.Now;
Kills[E.Origin.clientID]++;
if ((lastKill[E.Origin.clientID] - DateTime.Now).TotalSeconds > 60)
inactiveMinutes[E.Origin.clientID]++;
curServer.lastKill[E.Origin.clientID] = DateTime.Now;
curServer.Kills[E.Origin.clientID]++;
if ((curServer.lastKill[E.Origin.clientID] - DateTime.Now).TotalSeconds > 60)
curServer.inactiveMinutes[E.Origin.clientID]++;
killerStats.Kills++;
if (killerStats.Deaths == 0)
killerStats.KDR = killerStats.Kills;
else
killerStats.KDR = killerStats.Kills / killerStats.Deaths;
killerStats.KDR = Math.Round((double)killerStats.Kills / (double)killerStats.Deaths, 2);
playerStats.updateStats(Killer, killerStats);
curServer.playerStats.updateStats(Killer, killerStats);
killStreaks[Killer.clientID] += 1;
deathStreaks[Killer.clientID] = 0;
curServer.killStreaks[Killer.clientID] += 1;
curServer.deathStreaks[Killer.clientID] = 0;
Killer.Tell(messageOnStreak(killStreaks[Killer.clientID], deathStreaks[Killer.clientID]));
await Killer.Tell(messageOnStreak(curServer.killStreaks[Killer.clientID], curServer.deathStreaks[Killer.clientID]));
}
if (E.Type == Event.GType.Death)
@ -139,38 +201,39 @@ namespace StatsPlugin
return;
Player Victim = E.Origin;
PlayerStats victimStats = playerStats.getStats(Victim);
StatTracking curServer = statLists.Find(x => x.Port == S.getPort());
PlayerStats victimStats = curServer.playerStats.getStats(Victim);
victimStats.Deaths++;
victimStats.KDR = victimStats.Kills / victimStats.Deaths;
victimStats.KDR = Math.Round((double)victimStats.Kills / (double)victimStats.Deaths, 2);
playerStats.updateStats(Victim, victimStats);
curServer.playerStats.updateStats(Victim, victimStats);
deathStreaks[Victim.clientID] += 1;
killStreaks[Victim.clientID] = 0;
curServer.deathStreaks[Victim.clientID] += 1;
curServer.killStreaks[Victim.clientID] = 0;
Victim.Tell(messageOnStreak(killStreaks[Victim.clientID], deathStreaks[Victim.clientID]));
await Victim.Tell(messageOnStreak(curServer.killStreaks[Victim.clientID], curServer.deathStreaks[Victim.clientID]));
}
}
private void calculateAndSaveSkill(Player P)
private void calculateAndSaveSkill(Player P, StatTracking curServer)
{
if (P == null)
return;
PlayerStats disconnectStats = playerStats.getStats(P);
if (Kills[P.clientID] == 0)
PlayerStats disconnectStats = curServer.playerStats.getStats(P);
if (curServer.Kills[P.clientID] == 0)
return;
else if (lastKill[P.clientID] > connectionTime[P.clientID])
inactiveMinutes[P.clientID] += (int)(DateTime.Now - lastKill[P.clientID]).TotalMinutes;
else if (curServer.lastKill[P.clientID] > curServer.connectionTime[P.clientID])
curServer.inactiveMinutes[P.clientID] += (int)(DateTime.Now - curServer.lastKill[P.clientID]).TotalMinutes;
int newPlayTime = (int)(DateTime.Now - connectionTime[P.clientID]).TotalMinutes - inactiveMinutes[P.clientID];
int newPlayTime = (int)(DateTime.Now - curServer.connectionTime[P.clientID]).TotalMinutes - curServer.inactiveMinutes[P.clientID];
if (newPlayTime < 2)
return;
double newSPM = Kills[P.clientID] * 50 / newPlayTime;
double newSPM = curServer.Kills[P.clientID] * 50 / Math.Max(1, newPlayTime);
double kdrWeight = Math.Round(Math.Pow(disconnectStats.KDR, 2 / Math.E), 3);
double Multiplier;
@ -190,34 +253,18 @@ namespace StatsPlugin
disconnectStats.Skill = disconnectStats.scorePerMinute * kdrWeight / 10;
disconnectStats.playTime += newPlayTime;
playerStats.updateStats(P, disconnectStats);
curServer.playerStats.updateStats(P, disconnectStats);
}
private void resetCounters(int cID)
{
Kills[cID] = 0;
connectionTime[cID] = DateTime.Now;
inactiveMinutes[cID] = 0;
deathStreaks[cID] = 0;
killStreaks[cID] = 0;
}
public override void onLoad()
{
for (int i = 0; i < 18; i++)
{
Kills[i] = 0;
connectionTime[i] = DateTime.Now;
inactiveMinutes[i] = 0;
deathStreaks[i] = 0;
killStreaks[i] = 0;
}
}
public override void onUnload()
private void resetCounters(int cID, int serverPort)
{
StatTracking selectedPlayers = statLists.Find(x => x.Port == serverPort);
selectedPlayers.Kills[cID] = 0;
selectedPlayers.connectionTime[cID] = DateTime.Now;
selectedPlayers.inactiveMinutes[cID] = 0;
selectedPlayers.deathStreaks[cID] = 0;
selectedPlayers.killStreaks[cID] = 0;
}
private String messageOnStreak(int killStreak, int deathStreak)
@ -245,30 +292,6 @@ namespace StatsPlugin
return Message;
}
public override string Name
{
get
{
return "Basic Stats";
}
}
public override float Version
{
get
{
return 0.3f;
}
}
public override string Author
{
get
{
return "RaidMax";
}
}
}
public class StatsDB : Database
@ -332,15 +355,15 @@ namespace StatsPlugin
updatedPlayer.Add("DEATHS", S.Deaths);
updatedPlayer.Add("KDR", Math.Round(S.KDR, 2));
updatedPlayer.Add("SKILL", Math.Round(S.Skill, 1));
updatedPlayer.Add("SPM", Math.Round(S.scorePerMinute, 0));
updatedPlayer.Add("SPM", Math.Round(S.scorePerMinute, 1));
updatedPlayer.Add("PLAYTIME", S.playTime);
Update("STATS", updatedPlayer, String.Format("npID = '{0}'", P.npID));
Update("STATS", updatedPlayer, new KeyValuePair<string, object>("npID", P.npID));
}
public List<KeyValuePair<String, PlayerStats>> topStats()
{
String Query = String.Format("SELECT * FROM STATS WHERE KDR < '{0}' AND KILLS > '{1}' AND PLAYTIME > '{2}' ORDER BY SKILL DESC LIMIT '{3}'", 10, 150, 60, 5);
String Query = String.Format("SELECT * FROM STATS WHERE SKILL > 0 AND KDR < '{0}' AND KILLS > '{1}' AND PLAYTIME > '{2}' ORDER BY SKILL DESC LIMIT '{3}'", 10, 150, 60, 5);
DataTable Result = GetDataTable(Query);
List<KeyValuePair<String, PlayerStats>> pStats = new List<KeyValuePair<String, PlayerStats>>();

View File

@ -9,8 +9,9 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>StatsPlugin</RootNamespace>
<AssemblyName>StatsPlugin</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -20,6 +21,7 @@
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
@ -28,12 +30,9 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="SharedLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Admin\lib\SharedLibrary.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
@ -43,12 +42,19 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="Plugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)Admin\plugins\SimpleStatsPlugin.dll"</PostBuildEvent>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

307
Votemap Plugin/Plugin.cs Normal file
View File

@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using SharedLibrary;
using SharedLibrary.Network;
using SharedLibrary.Extensions;
using System.Threading.Tasks;
namespace Votemap_Plugin
{
/// <summary>
/// Allow clients to vote for the next map at the end of a round
/// Map choices are defined in the server
/// </summary>
public class VoteMap : Command
{
public VoteMap() : base("vote", "vote for the next map. syntax !v <mapname>", "v", Player.Permission.User, 1, false) { }
/// <summary>
/// Properties of Event E
/// Owner: The server the event came from
/// Origin: The player generating the event
/// Target: Optional target the player specified
/// Data: Chat message which triggered the command event
/// </summary>
/// <param name="E">This is the `say` event that comes from the server</param>
public override async Task ExecuteAsync(Event E)
{
var voting = Vote.getServerVotes(E.Owner.getPort());
// we only want to allow a vote during a vote session
if (voting.voteInSession)
{
if (voting.hasVoted(E.Origin.npID))
await E.Origin.Tell("You have already voted. Use ^5!vc ^7to ^5cancel ^7your vote");
else
{
string mapSearch = E.Data.ToLower().Trim();
// probably not the most optimized way to match the map.. but nothing is time critical here
Map votedMap = E.Owner.maps.Find(m => (m.Alias.ToLower().Contains(mapSearch) || m.Name.Contains(mapSearch)));
if (votedMap == null)
await E.Origin.Tell("^1" + E.Data + " is not a recognized map");
else
{
voting.castVote(E.Origin.npID, votedMap);
await E.Origin.Tell("You voted for ^5" + votedMap.Alias);
}
}
}
else
await E.Origin.Tell("There is no vote in session");
}
}
public class VoteCancel : Command
{
public VoteCancel() : base("votecancel", "cancel your vote for the next map. syntax !vc", "vc", Player.Permission.User, 0, false) { }
public override async Task ExecuteAsync(Event E)
{
var voting = Vote.getServerVotes(E.Owner.getPort());
if (voting.voteInSession)
{
if (voting.hasVoted(E.Origin.npID))
{
voting.cancelVote(E.Origin.npID);
await E.Origin.Tell("Vote cancelled");
}
else
{
await E.Origin.Tell("You have no vote to cancel");
}
}
else
await E.Origin.Tell("There is no vote in session");
}
}
public class Vote : IPlugin
{
public class VoteData
{
public string guid;
public Map map;
}
public class MapResult
{
public Map map;
public int voteNum;
}
public class ServerVoting
{
public int serverID
{
get; private set;
}
public bool voteInSession;
public bool matchEnded;
public bool votePassed;
public bool waitForLoad;
public DateTime voteTimeStart;
public DateTime loadStartTime;
public List<VoteData> voteList
{
get; private set;
}
public ServerVoting(int id)
{
serverID = id;
voteInSession = false;
votePassed = false;
matchEnded = false;
waitForLoad = true;
voteList = new List<VoteData>();
}
public int getTotalVotes()
{
return voteList.Count;
}
public bool hasVoted(string guid)
{
return voteList.Exists(x => (x.guid == guid));
}
public void castVote(string guid, Map map)
{
var vote = new VoteData();
vote.guid = guid;
vote.map = map;
voteList.Add(vote);
}
public void cancelVote(string guid)
{
voteList.RemoveAll(x => (x.guid == guid));
}
public MapResult getTopMap()
{
List<MapResult> results = new List<MapResult>();
MapResult result = new MapResult();
result.map = new Map("Remain", "Remain");
result.voteNum = 0;
foreach (var vote in voteList)
{
if (!results.Exists(x => (x.map.Name == vote.map.Name)))
{
MapResult newResult = new MapResult();
newResult.map = vote.map;
newResult.voteNum = 1;
results.Add(newResult);
}
else
{
var map = results.Find(x => x.map.Name == vote.map.Name);
map.voteNum += 1;
}
}
foreach (var map in results)
if (map.voteNum > result.voteNum)
result = map;
return result;
}
}
private static List<ServerVoting> serverVotingList;
public static int minVotes = 3;
public string Author
{
get
{
return "RaidMax";
}
}
public float Version
{
get
{
return 1.0f;
}
}
public string Name
{
get
{
return "Votemap Plugin";
}
}
public async Task OnLoad()
{
serverVotingList = new List<ServerVoting>();
}
public async Task OnUnload()
{
serverVotingList.Clear();
}
/// <summary>
/// The server monitor thread calls this about every 1 second
/// This is not high-precision, but will run 1 time per second
/// </summary>
/// <param name="S"></param>
public async Task OnTick(Server S)
{
var serverVotes = getServerVotes(S.getPort());
if (serverVotes != null)
{
if ((DateTime.Now - serverVotes.loadStartTime).TotalSeconds < 30 /* || S.getPlayers().Count < 3*/)
return;
else
serverVotes.waitForLoad = false;
// dvar that is set & updated by the game script...
serverVotes.matchEnded = (await S.GetDvarAsync<int>("scr_gameended")).Value == 1;
/*
Console.WriteLine("===========================");
Console.WriteLine("Match ended->" + serverVotes.matchEnded);
Console.WriteLine("Vote in session->" + serverVotes.voteInSession);
Console.WriteLine("Vote passed->" + serverVotes.votePassed);*/
if (!serverVotes.voteInSession && serverVotes.matchEnded && serverVotes.voteTimeStart == DateTime.MinValue)
{
await S.Broadcast("Voting has started for the ^5next map");
await S.Broadcast("Type ^5!v <map> ^7to vote for the nextmap!");
serverVotes.voteInSession = true;
serverVotes.voteTimeStart = DateTime.Now;
return;
}
if (!serverVotes.voteInSession && serverVotes.votePassed && (DateTime.Now - serverVotes.voteTimeStart).TotalSeconds > 30)
{
await S.ExecuteCommandAsync("map " + serverVotes.getTopMap().map.Name);
serverVotes.votePassed = false;
return;
}
if (serverVotes.voteInSession)
{
if ((DateTime.Now - serverVotes.voteTimeStart).TotalSeconds > 25)
{
serverVotes.voteInSession = false;
MapResult m = serverVotes.getTopMap();
await S.Broadcast("Voting has ended!");
if (m.voteNum < minVotes && S.getPlayers().Count > 4)
await S.Broadcast("Vote map failed. At least ^5" + minVotes + " ^7people must choose the same map");
else
{
await S.Broadcast(String.Format("Next map is ^5{0} ^7- [^2{1}/{2}^7] votes", m.map.Alias, m.voteNum, serverVotes.getTotalVotes()));
serverVotes.votePassed = true;
}
}
}
}
}
public async Task OnEvent(Event E, Server S)
{
if (E.Type == Event.GType.Start)
{
serverVotingList.Add(new ServerVoting(S.getPort()));
}
if (E.Type == Event.GType.Stop)
{
serverVotingList.RemoveAll(x => x.serverID == S.getPort());
}
if (E.Type == Event.GType.MapEnd || E.Type == Event.GType.MapChange)
{
var serverVotes = getServerVotes(S.getPort());
serverVotes.voteList.Clear();
serverVotes.voteTimeStart = DateTime.MinValue;
serverVotes.loadStartTime = DateTime.Now;
serverVotes.waitForLoad = true;
}
}
public static ServerVoting getServerVotes(int serverID)
{
return serverVotingList.Find(x => (x.serverID == serverID));
}
}
}

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{428D8EB9-ECA3-4A66-AA59-3A944378C33F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Votemap_Plugin</RootNamespace>
<AssemblyName>VotemapPlugin</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\SharedLibrary\SharedLibrary.csproj">
<Project>{d51eeceb-438a-47da-870f-7d7b41bc24d6}</Project>
<Name>SharedLibrary</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="Plugin.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -1,620 +0,0 @@
using System;
using System.Collections.Generic;
using SharedLibrary;
using System.Text;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections.Specialized;
namespace Webfront_Plugin
{
class Framework
{
private List<Server> activeServers;
public Framework()
{
activeServers = new List<Server>();
}
public void addServer(Server S)
{
activeServers.Add(S);
}
public void removeServer(Server S)
{
if (S != null && activeServers.Contains(S))
{
S.Stop();
activeServers.Remove(S);
}
}
public List<Server> getServers()
{
return activeServers;
}
private String processTemplate(String Input, String Param)
{
try
{
Server requestedServer = null;
int requestPageNum = 0;
int ID = 0;
String Query = "";
if (Param != null)
{
NameValueCollection querySet = System.Web.HttpUtility.ParseQueryString(Param);
if (querySet["server"] != null)
requestedServer = activeServers.Find(x => x.pID() == Int32.Parse(querySet["server"]));
else
requestedServer = activeServers.First();
if (querySet["page"] != null)
requestPageNum = Int32.Parse(querySet["page"]);
if (querySet["id"] != null)
ID = Int32.Parse(querySet["id"]);
if (querySet["query"] != null)
Query = querySet["query"];
}
String Pattern = @"\{\{.+\}\}";
Regex Search = new Regex(Pattern, RegexOptions.IgnoreCase);
MatchCollection Matches = Search.Matches(Input);
foreach (Match match in Matches)
{
Input = processReplacements(Input, match.Value, requestPageNum, ID, Query, requestedServer);
}
return Input;
}
catch (Exception E)
{
Page Error = new error();
return Error.Load().Replace("{{ERROR}}", E.Message);
}
}
private String parsePagination(int totalItems, int itemsPerPage, int currentPage, String Page)
{
StringBuilder output = new StringBuilder();
output.Append("<div id=pages>");
if (currentPage > 0)
output.AppendFormat("<a href=/{0}?page={1}>PREV</a>", Page, currentPage - 1);
double totalPages = Math.Ceiling(((float)totalItems / itemsPerPage));
output.Append("<span id=pagination>" + (currentPage + 1) + "/" + totalPages + "</span>");
if ((currentPage + 1) < totalPages)
output.AppendFormat("<a href=/{0}?page={1}>NEXT</a>", Page, currentPage + 1);
output.Append("</div>");
return output.ToString();
}
private String processReplacements(String Input, String Macro, int curPage, int ID, String Query, params Server[] Servers)
{
bool Authenticated = false;
bool UserPrivelege = false;
if (Servers[0] != null && Manager.lastIP != null)
{
Player User = Servers[0].clientDB.getPlayer(Manager.lastIP.ToString());
if (User != null && User.Level > Player.Permission.Flagged)
Authenticated = true;
if (User != null && User.Level == Player.Permission.User)
UserPrivelege = true;
}
if (Macro.Length < 5)
return "";
String Looking = Macro.Substring(2, Macro.Length - 4);
if (Looking == "SERVERS")
{
int cycleFix = 0;
StringBuilder buffer = new StringBuilder();
foreach (Server S in activeServers)
{
StringBuilder players = new StringBuilder();
if (S.getClientNum() > 0)
{
int count = 0;
double currentPlayers = S.statusPlayers.Count;
players.Append("<table cellpadding='0' cellspacing='0' class='players'>");
foreach (Player P in S.getPlayers())
{
if (P == null)
continue;
if (count % 2 == 0)
{
switch (cycleFix)
{
case 0:
players.Append("<tr class='row-grey'>");
cycleFix = 1;
break;
case 1:
players.Append("<tr class='row-white'>");
cycleFix = 0;
break;
}
}
players.AppendFormat("<td><a href='/player?id={0}'>{1}</a></td>", P.databaseID, SharedLibrary.Utilities.nameHTMLFormatted(P));
if (count % 2 != 0)
{
players.Append("</tr>");
}
count++;
}
players.Append("</table>");
}
buffer.AppendFormat(@"<table cellpadding=0 cellspacing=0 class=server>
<tr>
<th class=server_title><span>{0}</span></th>
<th class=server_map><span>{1}</span></th>
<th class=server_players><span>{2}</span></th>
<th class=server_gametype><span>{3}</span></th>
<th><a href=/bans>Penalties</a></th>
<th><a class='history' href='/graph?server={4}'>History</a></th>
</tr>
</table>
{5}",
S.getName(), S.getMap(), S.getClientNum() + "/" + S.getMaxClients(), SharedLibrary.Utilities.gametypeLocalized(S.getGametype()), S.pID(), players.ToString());
if (S.getClientNum() > 0)
{
buffer.AppendFormat("<div class='chatHistory' id='chatHistory_{0}'></div><script type='text/javascript'>$( document ).ready(function() {{ setInterval({1}loadChatMessages({0}, '#chatHistory_{0}'){1}, 2500); }});</script><div class='null' style='clear:both;'></div>", S.pID(), '\"');
if (UserPrivelege || Authenticated)
buffer.AppendFormat("<form class='chatOutFormat' action={1}javascript:chatRequest({0}, 'chatEntry_{0}'){1}><input class='chatFormat_text' type='text' placeholder='Enter a message...' id='chatEntry_{0}'/><input class='chatFormat_submit' type='submit'/></form>", S.pID(), '\"');
}
buffer.Append("<hr/>");
}
return Input.Replace(Macro, buffer.ToString());
}
if(Looking == "CHAT")
{
StringBuilder chatMessages = new StringBuilder();
chatMessages.Append("<table id='table_chatHistory'>");
if (Servers.Length > 0 && Servers[0] != null)
{
foreach (Chat Message in Servers[0].chatHistory)
chatMessages.AppendFormat("<tr><td class='chat_name' style='text-align: left;'>{0}</td><td class='chat_message'>{1}</td><td class='chat_time' style='text-align: right;'>{2}</td></tr>", SharedLibrary.Utilities.nameHTMLFormatted(Message.Origin), Message.Message, Message.timeString());
}
chatMessages.Append("</table>");
return chatMessages.ToString();
}
if (Looking == "PLAYER")
{
StringBuilder buffer = new StringBuilder();
Server S = activeServers[0];
buffer.Append("<table class='player_info'><tr><th>Name</th><th>Aliases</th><th>IP</th><th>Rating</th><th>Level</th><th>Connections</th><th>Last Seen</th><th>Profile</th>");
List<Player> matchingPlayers = new List<Player>();
if (ID > 0)
matchingPlayers.Add(S.clientDB.getPlayer(ID));
else if (Query.Length > 2)
{
matchingPlayers = S.clientDB.findPlayers(Query);
if (matchingPlayers == null)
matchingPlayers = new List<Player>();
List<int> matchedDatabaseIDs = new List<int>();
foreach (Aliases matchingAlias in S.aliasDB.findPlayers(Query))
matchedDatabaseIDs.Add(matchingAlias.Number);
foreach (Player matchingP in S.clientDB.getPlayers(matchedDatabaseIDs))
{
if (matchingPlayers.Find(x => x.databaseID == matchingP.databaseID) == null)
matchingPlayers.Add(matchingP);
}
}
if (matchingPlayers == null)
buffer.Append("</table>");
else
{
foreach (Player Player in matchingPlayers)
{
if (Player == null)
continue;
buffer.Append("<tr>");
StringBuilder Names = new StringBuilder();
List<String> nameAlias = new List<String>();
List<String> IPAlias = new List<String>();
StringBuilder IPs = new StringBuilder();
if (Authenticated)
{
List<Aliases> allAlliases = S.getAliases(Player);
foreach (Aliases A in allAlliases)
{
foreach (String Name in A.Names.Distinct())
nameAlias.Add(Name);
foreach (String IP in A.IPS.Distinct())
IPAlias.Add(IP);
}
Names.Append("<a href='#' class='pseudoLinkAlias'>Show Aliases</a>");
Names.Append("<div class='playerAlias'>");
foreach (String Name in nameAlias.Distinct())
Names.AppendFormat("<span>{0}</span><br/>", Utilities.stripColors(Name));
Names.Append("</div>");
IPs.Append("<a href='#' class='pseudoLinkIP'>Show IPs</a>");
IPs.Append("<div class='playerIPs'>");
foreach (String IP in IPAlias)
IPs.AppendFormat("<span>{0}</span><br/>", IP);
IPs.Append("</div>");
}
if (!Authenticated)
{
Names.Append("Hidden");
IPs.Append("Hidden");
}
Int64 forumID = 0;
if (Player.npID.Length == 16)
{
forumID = Int64.Parse(Player.npID.Substring(0, 16), System.Globalization.NumberStyles.AllowHexSpecifier);
forumID = forumID - 76561197960265728;
}
String Screenshot = String.Empty;
//if (logged)
Screenshot = String.Format("<a href='http://server.nbsclan.org/screen.php?id={0}&name={1}' target='_blank'><div style='background-image:url(http://server.nbsclan.org/shutter.png); width: 20px; height: 20px;float: right; position:relative; right: 21%; background-size: contain;'></div></a>", forumID, Player.Name);
buffer.AppendFormat("<td><a style='float: left;' href='{9}'>{0}</a>{10}</td><td>{1}</td><td>{2}</td><td>{3}</td><td>{4}</td><td>{5}</td><td>{6} ago</td><td><a href='https://repziw4.de/forum/memberlist.php?mode=viewprofile&u={7}'>{8}</a></td>", Player.Name, Names, IPs, 0, SharedLibrary.Utilities.levelHTMLFormatted(Player.Level), Player.Connections, Player.getLastConnection(), forumID, Player.Name, "/player?id=" + Player.databaseID, Screenshot);
buffer.Append("</tr>");
}
buffer.Append("</table>");
return Input.Replace(Macro, buffer.ToString());
}
}
if (Looking == "BANS")
{
StringBuilder buffer = new StringBuilder();
Server S = activeServers[0];
buffer.Append("<table cellspacing=0 class=bans>");
int limitPerPage = 30;
int Pagination = curPage;
int totalBans = S.Bans.Count;
int range;
int start = Pagination * limitPerPage;
int cycleFix = 0;
if (totalBans <= limitPerPage)
range = totalBans;
else if ((totalBans - start) < limitPerPage)
range = (totalBans - start);
else
range = limitPerPage;
List<Penalty> Bans = new List<Penalty>();
if (totalBans > 0)
Bans = S.Bans.OrderByDescending(x => x.When).ToList().GetRange(start, range);
if (Bans.Count == 0)
buffer.Append("<span style='font-size: 16pt;'>No bans yet.</span>");
else
{
buffer.Append("<h1 style=margin-top: 0;>{{TIME}}</h1><hr /><tr><th>Name</th><th>Type</th><th style=text-align:left;>Offense</th><th style=text-align:left;>Penalty By</th><th style='width: 175px; text-align:right;padding-right: 80px;'>Time</th></tr>");
if (Bans[0] != null)
buffer = buffer.Replace("{{TIME}}", "From " + SharedLibrary.Utilities.timePassed(Bans[0].When) + " ago" + " &mdash; " + totalBans + " total");
List<String> npIDs = new List<string>();
foreach (Penalty B in Bans)
npIDs.Add(B.npID);
List<Player> bannedPlayers = S.clientDB.getPlayers(npIDs);
for (int i = 0; i < Bans.Count; i++)
{
if (Bans[i] == null)
continue;
Player P = bannedPlayers.Where(x => x.npID == Bans[i].npID).First();
Player B;
if (P.npID == Bans[i].bannedByID || Bans[i].bannedByID == "")
B = new Player("IW4MAdmin", "", 0, SharedLibrary.Player.Permission.Banned, 0, "", 0, "");
else
B = S.clientDB.getPlayer(Bans[i].bannedByID, -1);
if (P == null)
P = new Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (B == null)
B = new Player("Unknown", "n/a", 0, 0, 0, "Unknown", 0, "");
if (P.lastOffense == String.Empty)
P.lastOffense = "Evade";
if (P != null && B != null)
{
String Prefix;
if (cycleFix % 2 == 0)
Prefix = "class=row-grey";
else
Prefix = "class=row-white";
String Link = "/player?id=" + P.databaseID;
buffer.AppendFormat("<tr {4}><td><a href='{5}'>{0}</a></td><td style='width: 150px; border-left: 3px solid #bbb; text-align:left;'>{6}</td><td style='border-left: 3px solid #bbb; text-align:left;'>{1}</td><td style='border-left: 3px solid #bbb;text-align:left;'>{2}</td><td style='width: 175px; text-align:right;'>{3}</td></tr></div>", P.Name, Bans[i].Reason.Substring(0, Math.Min(70, Bans[i].Reason.Length)), SharedLibrary.Utilities.nameHTMLFormatted(B), Bans[i].getWhen(), Prefix, Link, Utilities.penaltyHTMLFormatted(Bans[i].BType));
cycleFix++;
}
}
}
buffer.Append("</table><hr/>");
if (totalBans > limitPerPage)
buffer.Append(parsePagination(totalBans, limitPerPage, Pagination, "bans"));
return Input.Replace(Macro, buffer.ToString());
}
if (Looking == "GRAPH")
{
StringBuilder buffer = new StringBuilder();
buffer.Append("<script type='text/javascript' src='//www.google.com/jsapi'></script><div id='chart_div'></div>");
buffer.Append("<script> var players = [");
int count = 1;
List<PlayerHistory> run = Servers[0].playerHistory.ToList();
foreach (PlayerHistory i in run) //need to reverse for proper timeline
{
buffer.AppendFormat("[new Date({0}, {1}, {2}, {3}, {4}), {5}]", i.When.Year, i.When.Month - 1, i.When.Day, i.When.Hour, i.When.Minute, i.Players);
if (count < run.Count)
buffer.Append(",\n");
count++;
}
buffer.Append("];\n");
buffer.Append("</script>");
return Input.Replace(Macro, buffer.ToString());
}
if (Looking == "TITLE")
return Input.Replace(Macro, "IW4MAdmin by RaidMax");
if (Looking == "VERSION")
return Input.Replace(Macro, "1.1");
if (Looking == "PUBBANS" || Looking == "PUBBANSR")
{
String pubBans = "=========================================\r\nIW4MAdmin Public Banlist\r\n=========================================\r\n";
foreach (Penalty P in activeServers[0].Bans.OrderByDescending(x => x.When).ToList())
{
if (P.BType == Penalty.Type.Ban)
pubBans += String.Format("{0};{1};{2};{3}\r\n",P.npID, P.IP, P.Reason.Trim(), P.When);
if (Looking == "PUBBANSR")
pubBans += "<br/>";
}
return Input.Replace(Macro, pubBans);
}
return "PLACEHOLDER";
}
public String processRequest(Kayak.Http.HttpRequestHead request)
{
Page requestedPage = new notfound();
Page Header = new header();
Page Footer = new footer();
if (request.Path == "/")
requestedPage = new main();
else
{
string p = request.Path.ToLower().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries)[0];
switch (p)
{
case "bans":
requestedPage = new bans();
break;
case "player":
requestedPage = new player();
break;
case "graph":
requestedPage = new graph();
return processTemplate(requestedPage.Load(), request.QueryString);
case "chat":
requestedPage = new chat();
return processTemplate(requestedPage.Load(), request.QueryString);
case "error":
requestedPage = new error();
break;
case "pubbans":
return processTemplate("{{PUBBANS}}", null);
case "pubbansr":
return processTemplate("{{PUBBANSR}}", null);
default:
requestedPage = new notfound();
break;
}
}
return processTemplate(Header.Load(), null) + processTemplate(requestedPage.Load(), request.QueryString) + processTemplate(Footer.Load(), null);
}
}
abstract class Page
{
public abstract String Load();
public abstract String Name { get; }
protected String loadHTML()
{
IFile HTML = new IFile("webfront\\" + this.Name + ".html");
String Contents = HTML.getLines();
HTML.Close();
return Contents;
}
}
class notfound : Page
{
public override String Name
{
get { return "notfound"; }
}
public override String Load()
{
return loadHTML();
}
}
class main : Page
{
public override String Name
{
get { return "main"; }
}
public override String Load()
{
return loadHTML();
}
}
class bans : Page
{
public override String Name
{
get { return "bans"; }
}
public override String Load()
{
return loadHTML();
}
}
class header : Page
{
public override String Name
{
get { return "header"; }
}
public override String Load()
{
return loadHTML();
}
}
class footer : Page
{
public override String Name
{
get { return "footer"; }
}
public override String Load()
{
return loadHTML();
}
}
class player : Page
{
public override String Name
{
get { return "player"; }
}
public override String Load()
{
return loadHTML();
}
}
class graph : Page
{
public override String Name
{
get { return "graph"; }
}
public override String Load()
{
return loadHTML();
}
}
class chat : Page
{
public override String Name
{
get { return "chat"; }
}
public override String Load()
{
return "{{CHAT}}";
}
}
class error : Page
{
public override String Name
{
get { return "error"; }
}
public override String Load()
{
return loadHTML();
}
}
}

View File

@ -1,59 +0,0 @@
using System;
using SharedLibrary;
using System.Threading;
using System.Collections.Generic;
namespace Webfront_Plugin
{
public class Webfront : Plugin
{
private static Thread webManagerThread;
public override void onEvent(Event E)
{
if (E.Type == Event.GType.Start)
{
Manager.webFront.removeServer(Manager.webFront.getServers().Find(x => x.getPort() == E.Owner.getPort()));
Manager.webFront.addServer(E.Owner);
E.Owner.Log.Write("Webfront now listening", Log.Level.Production);
}
if (E.Type == Event.GType.Stop)
{
Manager.webFront.removeServer(E.Owner);
E.Owner.Log.Write("Webfront has lost access to server", Log.Level.Production);
}
}
public override void onLoad()
{
webManagerThread = new Thread(new ThreadStart(Manager.Init));
webManagerThread.Name = "Webfront";
webManagerThread.Start();
}
public override void onUnload()
{
Manager.webScheduler.Stop();
webManagerThread.Join();
}
public override String Name
{
get { return "Webfront"; }
}
public override float Version
{
get { return 0.1f; }
}
public override string Author
{
get
{
return "RaidMax";
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More