mirror of
https://github.com/RaidMax/IW4M-Admin.git
synced 2025-06-11 15:52:25 -05:00
Moved plugins to a seperate folder
Uncapped the search result limit for !find
This commit is contained in:
81
Plugins/EventAPI/EventAPI.csproj
Normal file
81
Plugins/EventAPI/EventAPI.csproj
Normal file
@ -0,0 +1,81 @@
|
||||
<?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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</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=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" />
|
||||
</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>
|
182
Plugins/EventAPI/Plugin.cs
Normal file
182
Plugins/EventAPI/Plugin.cs
Normal file
@ -0,0 +1,182 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using SharedLibrary;
|
||||
using SharedLibrary.Interfaces;
|
||||
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.GetPlayersAsList().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 OnLoadAsync()
|
||||
{
|
||||
apiEvents = new Queue<RestEvent>();
|
||||
flaggedMessagesText = new List<string>();
|
||||
activeServers = new List<Server>();
|
||||
WebService.pageList.Add(new EventsJSON());
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
apiEvents.Clear();
|
||||
activeServers.Clear();
|
||||
}
|
||||
|
||||
public async Task OnTickAsync(Server S)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
{
|
||||
if (E.Type == Event.GType.Start)
|
||||
{
|
||||
activeServers.Add(S);
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Stop)
|
||||
{
|
||||
// fixme: this will be bad once FTP is working and there can be multiple servers on the same port.
|
||||
activeServers.RemoveAll(s => s.getPort() == S.getPort());
|
||||
}
|
||||
|
||||
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
Plugins/EventAPI/packages.config
Normal file
4
Plugins/EventAPI/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
</packages>
|
54
Plugins/FastRestart/FastRestartPlugin.csproj
Normal file
54
Plugins/FastRestart/FastRestartPlugin.csproj
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" 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>{1479DE87-ACB5-4046-81C8-A0BA5041227D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>FastRestartPlugin</RootNamespace>
|
||||
<AssemblyName>FastRestartPlugin</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</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>
|
||||
</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>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>copy /Y "$(TargetDir)$(TargetName).dll" "$(SolutionDir)BUILD\plugins\"</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
</Project>
|
105
Plugins/FastRestart/Plugin.cs
Normal file
105
Plugins/FastRestart/Plugin.cs
Normal file
@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SharedLibrary;
|
||||
using SharedLibrary.Interfaces;
|
||||
using SharedLibrary.Network;
|
||||
|
||||
namespace Plugin
|
||||
{
|
||||
public class FastRestartConfig : Serialize<FastRestartConfig>
|
||||
{
|
||||
public bool Enabled;
|
||||
}
|
||||
|
||||
public class CEnableFastRestart : Command
|
||||
{
|
||||
public CEnableFastRestart() : base("frenable", "enable fast restarting at the end of a map. syntax: !fre", "fre", Player.Permission.SeniorAdmin, 0, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
FastRestartPlugin.Config = new FastRestartConfig() { Enabled = true };
|
||||
Serialize<FastRestartConfig>.Write($"config/fastrestartconfig_{E.Owner}.cfg", FastRestartPlugin.Config);
|
||||
await E.Origin.Tell("Fast restarting is now enabled for this server");
|
||||
}
|
||||
}
|
||||
|
||||
public class CDisableFastRestart : Command
|
||||
{
|
||||
public CDisableFastRestart() : base("fredisable", "disable fast restarting at the end of a map. syntax: !frd", "frd", Player.Permission.SeniorAdmin, 0, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
FastRestartPlugin.Config = new FastRestartConfig() { Enabled = false };
|
||||
Serialize<FastRestartConfig>.Write($"config/fastrestartconfig_{E.Owner}.cfg", FastRestartPlugin.Config);
|
||||
await E.Origin.Tell("Fast restarting is now disabled for this server");
|
||||
}
|
||||
}
|
||||
|
||||
public class FastRestartPlugin : IPlugin
|
||||
{
|
||||
bool MatchEnded;
|
||||
DateTime MatchEndTime;
|
||||
public static FastRestartConfig Config;
|
||||
|
||||
public string Name { get { return "Fast Restarter"; } }
|
||||
|
||||
public float Version { get { return 1.0f; } }
|
||||
|
||||
public string Author { get { return "RaidMax"; } }
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
{
|
||||
if (E.Type == Event.GType.Start)
|
||||
{
|
||||
try
|
||||
{
|
||||
await S.GetDvarAsync<int>("scr_intermission_time");
|
||||
Config = Serialize<FastRestartConfig>.Read($"config/fastrestartconfig_{E.Owner}.cfg");
|
||||
}
|
||||
|
||||
catch (SharedLibrary.Exceptions.DvarException)
|
||||
{
|
||||
await S.ExecuteCommandAsync("set scr_intermission_time 20");
|
||||
}
|
||||
|
||||
catch (SharedLibrary.Exceptions.SerializeException)
|
||||
{
|
||||
Config = new FastRestartConfig() { Enabled = false };
|
||||
Serialize<FastRestartConfig>.Write($"config/fastrestartconfig_{E.Owner}.cfg", Config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Task OnLoadAsync()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task OnTickAsync(Server S)
|
||||
{
|
||||
if (!Config.Enabled)
|
||||
return;
|
||||
|
||||
MatchEnded = (await S.GetDvarAsync<int>("scr_gameended")).Value == 1;
|
||||
|
||||
if (MatchEnded && MatchEndTime == DateTime.MinValue)
|
||||
MatchEndTime = DateTime.Now;
|
||||
|
||||
// I'm pretty sure the timelength from game ended to scoreboard popup is 2000ms
|
||||
if (MatchEnded && (DateTime.Now - MatchEndTime).TotalSeconds >= ((await S.GetDvarAsync<int>("scr_intermission_time")).Value - 2))
|
||||
{
|
||||
await S.ExecuteCommandAsync("fast_restart");
|
||||
MatchEndTime = DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
public Task OnUnloadAsync()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
36
Plugins/FastRestart/Properties/AssemblyInfo.cs
Normal file
36
Plugins/FastRestart/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("FastRestartPlugin")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("FastRestartPlugin")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2017")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1479de87-acb5-4046-81c8-a0ba5041227d")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
43
Plugins/MessageBoard/Encryption.cs
Normal file
43
Plugins/MessageBoard/Encryption.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
9
Plugins/MessageBoard/Events.cs
Normal file
9
Plugins/MessageBoard/Events.cs
Normal 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);
|
||||
}
|
33
Plugins/MessageBoard/Exceptions.cs
Normal file
33
Plugins/MessageBoard/Exceptions.cs
Normal 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) { }
|
||||
}
|
||||
}
|
||||
|
1376
Plugins/MessageBoard/Forum.cs
Normal file
1376
Plugins/MessageBoard/Forum.cs
Normal file
File diff suppressed because it is too large
Load Diff
12
Plugins/MessageBoard/Identifiable.cs
Normal file
12
Plugins/MessageBoard/Identifiable.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MessageBoard
|
||||
{
|
||||
interface Identifiable
|
||||
{
|
||||
int getID();
|
||||
}
|
||||
}
|
145
Plugins/MessageBoard/MessageboardPlugin.csproj
Normal file
145
Plugins/MessageBoard/MessageboardPlugin.csproj
Normal file
@ -0,0 +1,145 @@
|
||||
<?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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<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="CodeKicker.BBCode, Version=5.0.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CodeKicker.BBCode.5.0.0.0\lib\net35\CodeKicker.BBCode.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="CryptSharp">
|
||||
<HintPath>..\packages\CryptSharp.1.2.0.1\lib\net35\CryptSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="DNA.Text, Version=2.1.1.10633, Culture=neutral, PublicKeyToken=f393b6ce92960100, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\DNA.Text.2.1.1.10633\lib\DNA.Text.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<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.Data" />
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>..\Admin\lib\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Security" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.XML" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<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>
|
||||
<Content Include="forum\usercp.html">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<WCFMetadata Include="Service References\" />
|
||||
</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>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)DNA.Text.dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\lib\DNA.Text.dll"
|
||||
copy /Y "$(TargetDir)CryptSharp.dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\lib\CryptSharp.dll"
|
||||
|
||||
copy /Y "$(TargetDir)CodeKicker.BBcode.dll" "$(SolutionDir)Admin\bin\$(ConfigurationName)\lib\CodeKicker.BBcode.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>
|
62
Plugins/MessageBoard/Plugin.cs
Normal file
62
Plugins/MessageBoard/Plugin.cs
Normal file
@ -0,0 +1,62 @@
|
||||
using System;
|
||||
using SharedLibrary;
|
||||
using SharedLibrary.Interfaces;
|
||||
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 OnLoadAsync()
|
||||
{
|
||||
forum = new Forum.Manager();
|
||||
forum.Start();
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
forum.Stop();
|
||||
}
|
||||
|
||||
public async Task OnTickAsync(Server S)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
{
|
||||
if (E.Type == Event.GType.Start)
|
||||
{
|
||||
if (stupidServer == null)
|
||||
stupidServer = S;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
61
Plugins/MessageBoard/Rank.cs
Normal file
61
Plugins/MessageBoard/Rank.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
22
Plugins/MessageBoard/Session.cs
Normal file
22
Plugins/MessageBoard/Session.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
623
Plugins/MessageBoard/Storage.cs
Normal file
623
Plugins/MessageBoard/Storage.cs
Normal file
@ -0,0 +1,623 @@
|
||||
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 IF NOT EXISTS [USERS] (
|
||||
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
[ranking] INTEGER DEFAULT 0,
|
||||
[username] TEXT COLLATE NOCASE 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 IF NOT EXISTS [SESSIONS] (
|
||||
[sessionid] TEXT NOT NULL,
|
||||
[sessionuserid] INTEGER NOT NULL,
|
||||
FOREIGN KEY(sessionuserid) REFERENCES USERS(id)
|
||||
);";
|
||||
|
||||
string createRankingTable = @"CREATE TABLE IF NOT EXISTS [RANKS] (
|
||||
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
[name] TEXT UNIQUE NOT NULL,
|
||||
[equivalentrank] INTEGER DEFAULT 0
|
||||
);";
|
||||
|
||||
string createCategoryTable = @"CREATE TABLE IF NOT EXISTS [CATEGORIES] (
|
||||
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
[title] TEXT NOT NULL,
|
||||
[description] TEXT NOT NULL,
|
||||
[permissions] BLOB
|
||||
);";
|
||||
|
||||
string createThreadTable = @"CREATE TABLE IF NOT EXISTS [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 IF NOT EXISTS [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)
|
||||
);";
|
||||
|
||||
string createUserProfileTable = @"CREATE TABLE IF NOT EXISTS [PROFILES] (
|
||||
[id] INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
[userid] INTEGER NOT NULL,
|
||||
[showemail] INTEGER DEFAULT 1,
|
||||
[bannercolor] TEXT DEFAULT '#ff6633',
|
||||
[birthday] TEXT,
|
||||
[showage] INTEGER DEFAULT 0
|
||||
);";
|
||||
|
||||
|
||||
ExecuteNonQuery(createClientTable);
|
||||
ExecuteNonQuery(createSessionTable);
|
||||
ExecuteNonQuery(createRankingTable);
|
||||
ExecuteNonQuery(createCategoryTable);
|
||||
ExecuteNonQuery(createThreadTable);
|
||||
ExecuteNonQuery(createReplyTable);
|
||||
ExecuteNonQuery(createUserProfileTable);
|
||||
|
||||
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 void removeSession(string sessionID)
|
||||
{
|
||||
ExecuteNonQuery(String.Format("DELETE FROM SESSIONS WHERE sessionid = '{0}'", 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
|
||||
|
||||
#region PROFILES
|
||||
private ProfileSettings getProfileFromDataTable(DataTable Result)
|
||||
{
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
{
|
||||
DataRow ResponseRow = Result.Rows[0];
|
||||
int id = Convert.ToInt32(ResponseRow["id"].ToString());
|
||||
int userID = Convert.ToInt32(ResponseRow["userid"].ToString());
|
||||
bool showEmail = Convert.ToBoolean(Convert.ToInt32(ResponseRow["showemail"].ToString()));
|
||||
string bannerColor = ResponseRow["bannercolor"].ToString();
|
||||
DateTime birthday = DateTime.Parse(ResponseRow["birthday"].ToString());
|
||||
bool showAge = Convert.ToBoolean(Convert.ToInt32(ResponseRow["showage"].ToString()));
|
||||
|
||||
ProfileSettings foundProfile = new ProfileSettings(id, userID, showEmail, bannerColor, birthday, showAge);
|
||||
return foundProfile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Dictionary<string, object> getDataTableFromProfile(ProfileSettings addedSettings)
|
||||
{
|
||||
Dictionary<String, object> newSettings = new Dictionary<String, object>();
|
||||
|
||||
if(addedSettings.id > 0)
|
||||
newSettings.Add("id", addedSettings.id);
|
||||
newSettings.Add("userid", addedSettings.userid);
|
||||
newSettings.Add("showemail", Convert.ToInt32(addedSettings.showEmail));
|
||||
newSettings.Add("bannercolor", addedSettings.bannerColor);
|
||||
newSettings.Add("birthday", SharedLibrary.Utilities.DateTimeSQLite(addedSettings.birthday));
|
||||
newSettings.Add("showage", Convert.ToInt32(addedSettings.showAge));
|
||||
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
public ProfileSettings getProfileSettings(int userid)
|
||||
{
|
||||
DataTable Result = GetDataTable("PROFILES", new KeyValuePair<string, object>("userid", userid));
|
||||
|
||||
return getProfileFromDataTable(Result);
|
||||
}
|
||||
|
||||
public bool addProfileSettings(ProfileSettings newProfile)
|
||||
{
|
||||
return Insert("PROFILES", getDataTableFromProfile(newProfile));
|
||||
}
|
||||
|
||||
public bool updateProfileSettings(ProfileSettings updatedProfile)
|
||||
{
|
||||
return Update("PROFILES", getDataTableFromProfile(updatedProfile), new KeyValuePair<string, object>("userid", updatedProfile.userid));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
152
Plugins/MessageBoard/Thread.cs
Normal file
152
Plugins/MessageBoard/Thread.cs
Normal file
@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
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 string formattedContent { 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.formattedContent = CodeKicker.BBCode.BBCode.ToHtml(this.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;
|
||||
}
|
||||
}
|
||||
}
|
168
Plugins/MessageBoard/User.cs
Normal file
168
Plugins/MessageBoard/User.cs
Normal file
@ -0,0 +1,168 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
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 void updateUsername(string username)
|
||||
{
|
||||
if (username != null && username.Length > 0)
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public void updateAvatar(string avatarURL)
|
||||
{
|
||||
this.avatarURL = avatarURL;
|
||||
}
|
||||
|
||||
public void updatePassword(string salt, string hash)
|
||||
{
|
||||
if (salt.Length > 0 && hash.Length > 0)
|
||||
{
|
||||
passwordHash = hash;
|
||||
passwordSalt = salt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ProfileSettings
|
||||
{
|
||||
public int id { get; private set; }
|
||||
public int userid { get; private set; }
|
||||
public bool showEmail { get; private set; }
|
||||
public string bannerColor;
|
||||
public DateTime birthday { get; private set; }
|
||||
public bool showAge { get; private set; }
|
||||
|
||||
public ProfileSettings(int userid)
|
||||
{
|
||||
id = 0;
|
||||
this.userid = userid;
|
||||
showEmail = true;
|
||||
showAge = true;
|
||||
}
|
||||
|
||||
public ProfileSettings(int id, int userid, bool showEmail, string bannerColor, DateTime birthday, bool showAge)
|
||||
{
|
||||
this.id = id;
|
||||
this.userid = userid;
|
||||
this.showEmail = showEmail;
|
||||
this.bannerColor = bannerColor;
|
||||
this.birthday = birthday;
|
||||
this.showAge = showAge;
|
||||
}
|
||||
|
||||
public void hideEmail(bool shouldHide)
|
||||
{
|
||||
if (shouldHide)
|
||||
showEmail = false;
|
||||
else
|
||||
showEmail = true;
|
||||
}
|
||||
|
||||
public void hideAge(bool shouldHide)
|
||||
{
|
||||
if (shouldHide)
|
||||
showAge = false;
|
||||
else
|
||||
showAge = true;
|
||||
}
|
||||
}
|
||||
|
||||
public struct UserInfo
|
||||
{
|
||||
public User user;
|
||||
public ProfileSettings profile;
|
||||
}
|
||||
}
|
15
Plugins/MessageBoard/app.config
Normal file
15
Plugins/MessageBoard/app.config
Normal file
@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.1" newVersion="4.0.0.1"/>
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral"/>
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
|
86
Plugins/MessageBoard/forum/category.html
Normal file
86
Plugins/MessageBoard/forum/category.html
Normal 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>-->
|
60
Plugins/MessageBoard/forum/home.html
Normal file
60
Plugins/MessageBoard/forum/home.html
Normal 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> — <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>
|
59
Plugins/MessageBoard/forum/login.html
Normal file
59
Plugins/MessageBoard/forum/login.html
Normal 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>
|
56
Plugins/MessageBoard/forum/postthread.html
Normal file
56
Plugins/MessageBoard/forum/postthread.html
Normal 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>
|
92
Plugins/MessageBoard/forum/register.html
Normal file
92
Plugins/MessageBoard/forum/register.html
Normal 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>
|
209
Plugins/MessageBoard/forum/thread.html
Normal file
209
Plugins/MessageBoard/forum/thread.html
Normal file
@ -0,0 +1,209 @@
|
||||
<div id="threadContainer">
|
||||
<div id="textNav"><a class="themeBlue" href="home">Home</a> » </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 style="float: right; display: none;" id="editThreadCaption">
|
||||
<i class="fa fa-pencil-square-o" aria-hidden="true"></i>
|
||||
Edit
|
||||
</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> » ' + 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.formattedContent));
|
||||
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);
|
||||
|
||||
if (user.user.id == Response.Thread.author.id)
|
||||
$('#editThreadCaption').css("display", "block");
|
||||
|
||||
$.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> \
|
||||
<i style=\"" + shouldHideAction(eachReply.author) + "\" replyid='" + eachReply.id + "' class=\"fa fa-pencil-square-o actionHover actionEdit\" aria-hidden=\"true\"></i> \
|
||||
<div class='threadContent'>" + decodeURIComponent(eachReply.formattedContent) + "</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();
|
||||
|
||||
if ($('#postReplyContainer').attr("editthread") == "true") {
|
||||
$.getJSON("_editthread",
|
||||
{
|
||||
content : $('#replyContentBox').val(),
|
||||
title : $('#postReplyContainer').attr("threadtitle"),
|
||||
id : parseGet("id"),
|
||||
update: true
|
||||
},
|
||||
|
||||
function(result) {
|
||||
if (result["errorCode"] == 0)
|
||||
window.location.replace(result["destination"]);
|
||||
else {
|
||||
showErrorMessage(result["errorCode"]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if ($('#postReplyContainer').attr("editreply") != "true") {
|
||||
$.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"]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
||||
$.getJSON("_editthread",
|
||||
{
|
||||
content : $('#replyContentBox').val(),
|
||||
title : "Reply",
|
||||
replyid : $('#postReplyContainer').attr("replyid"),
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
$('#content').on('click', '.actionEdit', function (e) {
|
||||
$('#postReplyContainer').slideDown('fast');
|
||||
var id = $(this).attr("replyID")
|
||||
$.getJSON("_thread?replyid=" + id, function(Response) {
|
||||
|
||||
$('#postReplyContainer').find("textarea").html(Response.content);
|
||||
$('#postReplyContainer').attr("editReply", true);
|
||||
$('#postReplyContainer').attr("replyid", id);
|
||||
|
||||
});
|
||||
|
||||
/*$.getJSON("_editthread",
|
||||
{
|
||||
replyid: $(this).attr("replyid"),
|
||||
delete: false
|
||||
},
|
||||
function (response) {
|
||||
if (response.success)
|
||||
window.location.replace(response.destination);
|
||||
});*/
|
||||
});
|
||||
|
||||
|
||||
$("#editThreadCaption").click(function(e) {
|
||||
$('#postReplyContainer').slideDown('fast');
|
||||
$.getJSON("_thread?id=" + parseGet("id"), function(Response) {
|
||||
|
||||
$('#postReplyContainer').find("textarea").html(Response.Thread.content);
|
||||
$('#postReplyContainer').attr("editthread", true);
|
||||
$('#postReplyContainer').attr("threadid", parseGet("id"));
|
||||
$('#postReplyContainer').attr("threadtitle", Response.Thread.title);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
</script>
|
62
Plugins/MessageBoard/forum/user.html
Normal file
62
Plugins/MessageBoard/forum/user.html
Normal file
@ -0,0 +1,62 @@
|
||||
<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>
|
||||
<div style="width: 100%; text-align: center; margin: 1em 0;"><a href="usercp">User Control Panel</a></div>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
$.getJSON("_userinfo?id=" + parseGet('id'), function (Response) {
|
||||
var user = Response.user;
|
||||
if (user == null)
|
||||
return false;
|
||||
$('#userCover').html(user.username);
|
||||
$('#userCover').css("background-color", Response.profile.bannerColor);
|
||||
var creationDate = new Date(user.creationDate);
|
||||
$('#userCreated').html("Joined " + (creationDate.getMonth() + 1) + '-' + creationDate.getDate() + '-' + creationDate.getFullYear());
|
||||
$('#userLogon').html("Last seen " + user.lastLoginString);
|
||||
$('#userPostCount').html(user.posts + " Posts");
|
||||
|
||||
if (Response.profile.showEmail) {
|
||||
$('#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>
|
64
Plugins/MessageBoard/forum/usercp.html
Normal file
64
Plugins/MessageBoard/forum/usercp.html
Normal file
@ -0,0 +1,64 @@
|
||||
<div class="alertBox">
|
||||
</div>
|
||||
<form id="userCP">
|
||||
<div class="table">
|
||||
<div class="tableCell">
|
||||
<span style="display:flex; margin: 0.25em;">Username</span>
|
||||
<input type="text" name="username" id="username" />
|
||||
</div>
|
||||
<div class="tableCell">
|
||||
<span style="display:flex; margin: 0.25em;">Banner Color</span>
|
||||
<input type="color" name="bannercolor" id="bannercolor" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="table">
|
||||
<div class="tableCell">
|
||||
<span style="display:flex; margin: 0.25em;">New Password</span>
|
||||
<input type="password" name="updatedpassword" id="updatedpassword"/>
|
||||
</div>
|
||||
<div class="table">
|
||||
<span style="display:flex; margin: 0.25em;">Repeat New Password</span>
|
||||
<input type="password" name="updatedpasswordrepeat" id="updatedpasswordrepeat" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="userAvatar" style="margin: 1em 0;">
|
||||
</div>
|
||||
<span style="display:flex; margin: 0.25em;">Avatar URL</span>
|
||||
<input style="width: 35%;" type="text" id="avatarurl" name="avatarurl" /><br />
|
||||
<input id="updateprofile" type="submit" name="submit" value="Update" />
|
||||
</form>
|
||||
|
||||
<script>
|
||||
$.getJSON("_userinfo", function(result) {
|
||||
$("#userAvatar").css('background-image', "".concat("url(", result.user['avatarURL'], ")"));
|
||||
$("#username").val(result.user.username);
|
||||
$("#bannercolor").val(result.profile.bannerColor);
|
||||
$("#avatarurl").val(result.user.avatarURL);
|
||||
|
||||
});
|
||||
$('#updateprofile').click(function (e) {
|
||||
|
||||
if (user.id == 0)
|
||||
return false;
|
||||
e.preventDefault();
|
||||
$.getJSON("_updateuser",
|
||||
{
|
||||
username : $('#username').val(),
|
||||
bannercolor : $('#bannercolor').val(),
|
||||
avatarurl : $('#avatarurl').val(),
|
||||
updatedpassword : $('#updatedpassword').val(),
|
||||
updatedpasswordrepeat: $('#updatedpasswordrepeat').val()
|
||||
},
|
||||
|
||||
function(result) {
|
||||
if (result["errorCode"] == 0)
|
||||
window.location.replace(result["destination"]);
|
||||
else {
|
||||
showErrorMessage(result["errorCode"]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
</script>
|
7
Plugins/MessageBoard/packages.config
Normal file
7
Plugins/MessageBoard/packages.config
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CodeKicker.BBCode" version="5.0.0.0" targetFramework="net40" />
|
||||
<package id="CryptSharp" version="1.2.0.1" targetFramework="net40" />
|
||||
<package id="DNA.Text" version="2.1.1.10633" targetFramework="net40" />
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net45" />
|
||||
</packages>
|
474
Plugins/SimpleStats/Plugin.cs
Normal file
474
Plugins/SimpleStats/Plugin.cs
Normal file
@ -0,0 +1,474 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SharedLibrary;
|
||||
|
||||
namespace StatsPlugin
|
||||
{
|
||||
public class CViewStats : Command
|
||||
{
|
||||
public CViewStats() : base("stats", "view your stats. syntax !stats", "xlrstats", Player.Permission.User, 0, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
String statLine;
|
||||
PlayerStats pStats;
|
||||
|
||||
if (E.Data.Length > 0 && E.Target == null)
|
||||
{
|
||||
await E.Origin.Tell("Cannot find the player you specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (E.Target != null)
|
||||
{
|
||||
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.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);
|
||||
}
|
||||
|
||||
if (E.Message.IsBroadcastCommand())
|
||||
{
|
||||
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
|
||||
await E.Owner.Broadcast($"Stats for ^5{name}^7");
|
||||
await E.Owner.Broadcast(statLine);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (E.Target != null)
|
||||
await E.Origin.Tell($"Stats for ^5{E.Target.Name}^7");
|
||||
await E.Origin.Tell(statLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class CViewTopStats : Command
|
||||
{
|
||||
public CViewTopStats() : base("topstats", "view the top 5 players on this server. syntax !topstats", "ts", Player.Permission.User, 0, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
List<KeyValuePair<String, PlayerStats>> pStats = Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.GetTopStats();
|
||||
StringBuilder msgBlder = new StringBuilder();
|
||||
|
||||
await E.Origin.Tell("^5--Top Players--");
|
||||
foreach (KeyValuePair<String, PlayerStats> pStat in pStats)
|
||||
{
|
||||
Player P = E.Owner.Manager.GetClientDatabase().GetPlayer(pStat.Key, -1);
|
||||
if (P == null)
|
||||
continue;
|
||||
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 CResetStats : Command
|
||||
{
|
||||
public CResetStats() : base("resetstats", "reset your stats to factory-new, !syntax !resetstats", "rs", Player.Permission.User, 0, false) { }
|
||||
|
||||
public override async Task ExecuteAsync(Event E)
|
||||
{
|
||||
var stats = Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.GetStats(E.Origin);
|
||||
stats.Deaths = 0;
|
||||
stats.Kills = 0;
|
||||
stats.scorePerMinute = 1.0;
|
||||
stats.Skill = 1;
|
||||
stats.KDR = 0.0;
|
||||
await Task.Run(() => { Stats.statLists.Find(x => x.Port == E.Owner.getPort()).playerStats.UpdateStats(E.Origin, stats); });
|
||||
await E.Origin.Tell("Your stats have been reset");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 : SharedLibrary.Interfaces.IPlugin
|
||||
{
|
||||
public static List<StatTracking> statLists;
|
||||
|
||||
public struct StatTracking
|
||||
{
|
||||
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 1.1f; }
|
||||
}
|
||||
|
||||
public string Author
|
||||
{
|
||||
get { return "RaidMax"; }
|
||||
}
|
||||
|
||||
public async Task OnLoadAsync()
|
||||
{
|
||||
statLists = new List<StatTracking>();
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
statLists.Clear();
|
||||
}
|
||||
|
||||
public async Task OnTickAsync(Server S)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
{
|
||||
if (E.Type == Event.GType.Start)
|
||||
{
|
||||
statLists.Add(new StatTracking(S.getPort()));
|
||||
if (statLists.Count == 1)
|
||||
{
|
||||
S.Manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", GetTotalKills));
|
||||
S.Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYTIME", GetTotalPlaytime));
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Stop)
|
||||
{
|
||||
statLists.RemoveAll(x => x.Port == S.getPort());
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Connect)
|
||||
{
|
||||
ResetCounters(E.Origin.ClientID, S.getPort());
|
||||
|
||||
PlayerStats checkForTrusted = statLists.Find(x => x.Port == S.getPort()).playerStats.GetStats(E.Origin);
|
||||
if (checkForTrusted.TotalPlayTime >= 4320 && E.Origin.Level < Player.Permission.Trusted)
|
||||
{
|
||||
E.Origin.setLevel(Player.Permission.Trusted);
|
||||
E.Owner.Manager.GetClientDatabase().UpdatePlayer(E.Origin);
|
||||
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 || E.Type == Event.GType.Stop)
|
||||
{
|
||||
foreach (Player P in S.GetPlayersAsList())
|
||||
{
|
||||
|
||||
if (P == null)
|
||||
continue;
|
||||
|
||||
CalculateAndSaveSkill(P, statLists.Find(x =>x.Port == S.getPort()));
|
||||
ResetCounters(P.ClientID, S.getPort());
|
||||
|
||||
E.Owner.Logger.WriteInfo("Updated skill for client #" + P.DatabaseID);
|
||||
//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));
|
||||
}
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Disconnect)
|
||||
{
|
||||
CalculateAndSaveSkill(E.Origin, statLists.Find(x=>x.Port == S.getPort()));
|
||||
ResetCounters(E.Origin.ClientID, S.getPort());
|
||||
E.Owner.Logger.WriteInfo("Updated skill for disconnecting client #" + E.Origin.DatabaseID);
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Kill)
|
||||
{
|
||||
if (E.Origin == E.Target || E.Origin == null)
|
||||
return;
|
||||
|
||||
Player Killer = E.Origin;
|
||||
StatTracking curServer = statLists.Find(x => x.Port == S.getPort());
|
||||
PlayerStats killerStats = curServer.playerStats.GetStats(Killer);
|
||||
|
||||
|
||||
curServer.lastKill[E.Origin.ClientID] = DateTime.Now;
|
||||
curServer.Kills[E.Origin.ClientID]++;
|
||||
|
||||
if ((DateTime.Now - curServer.lastKill[E.Origin.ClientID]).TotalSeconds > 120)
|
||||
curServer.inactiveMinutes[E.Origin.ClientID] += 2;
|
||||
|
||||
killerStats.Kills++;
|
||||
|
||||
killerStats.KDR = (killerStats.Deaths == 0) ? killerStats.Kills : killerStats.KDR = Math.Round((double)killerStats.Kills / (double)killerStats.Deaths, 2);
|
||||
|
||||
|
||||
curServer.playerStats.UpdateStats(Killer, killerStats);
|
||||
|
||||
curServer.killStreaks[Killer.ClientID] += 1;
|
||||
curServer.deathStreaks[Killer.ClientID] = 0;
|
||||
|
||||
await Killer.Tell(MessageOnStreak(curServer.killStreaks[Killer.ClientID], curServer.deathStreaks[Killer.ClientID]));
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Death)
|
||||
{
|
||||
if (E.Origin == E.Target || E.Origin == null)
|
||||
return;
|
||||
|
||||
Player Victim = E.Origin;
|
||||
StatTracking curServer = statLists.Find(x => x.Port == S.getPort());
|
||||
PlayerStats victimStats = curServer.playerStats.GetStats(Victim);
|
||||
|
||||
victimStats.Deaths++;
|
||||
victimStats.KDR = Math.Round(victimStats.Kills / (double)victimStats.Deaths, 2);
|
||||
|
||||
curServer.playerStats.UpdateStats(Victim, victimStats);
|
||||
|
||||
curServer.deathStreaks[Victim.ClientID] += 1;
|
||||
curServer.killStreaks[Victim.ClientID] = 0;
|
||||
|
||||
await Victim.Tell(MessageOnStreak(curServer.killStreaks[Victim.ClientID], curServer.deathStreaks[Victim.ClientID]));
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetTotalKills()
|
||||
{
|
||||
long Kills = 0;
|
||||
foreach (var S in statLists)
|
||||
Kills += S.playerStats.GetTotalServerKills();
|
||||
return Kills.ToString("#,##0");
|
||||
}
|
||||
|
||||
public static string GetTotalPlaytime()
|
||||
{
|
||||
long Playtime = 0;
|
||||
foreach (var S in statLists)
|
||||
Playtime += S.playerStats.GetTotalServerPlaytime();
|
||||
return Playtime.ToString("#,##0");
|
||||
}
|
||||
|
||||
private void CalculateAndSaveSkill(Player P, StatTracking curServer)
|
||||
{
|
||||
if (P == null)
|
||||
return;
|
||||
|
||||
PlayerStats DisconnectingPlayerStats = curServer.playerStats.GetStats(P);
|
||||
if (curServer.Kills[P.ClientID] == 0)
|
||||
return;
|
||||
|
||||
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 - curServer.connectionTime[P.ClientID]).TotalMinutes - curServer.inactiveMinutes[P.ClientID];
|
||||
|
||||
if (newPlayTime < 2)
|
||||
return;
|
||||
|
||||
// calculate the players Score Per Minute for the current session
|
||||
double SessionSPM = curServer.Kills[P.ClientID] * 100 / Math.Max(1, newPlayTime);
|
||||
// calculate how much the KDR should way
|
||||
// 0.81829 is a Eddie-Generated number that weights the KDR nicely
|
||||
double KDRWeight = Math.Round(Math.Pow(DisconnectingPlayerStats.KDR, 1.637 / Math.E), 3);
|
||||
double SPMWeightAgainstAverage;
|
||||
|
||||
// if no SPM, weight is 1 else the weight is the current sessions spm / lifetime average score per minute
|
||||
SPMWeightAgainstAverage = (DisconnectingPlayerStats.scorePerMinute == 1) ? 1 : SessionSPM / DisconnectingPlayerStats.scorePerMinute;
|
||||
|
||||
// calculate the weight of the new play time againmst lifetime playtime
|
||||
//
|
||||
double SPMAgainstPlayWeight = newPlayTime / Math.Min(600, DisconnectingPlayerStats.TotalPlayTime + newPlayTime);
|
||||
// calculate the new weight against average times the weight against play time
|
||||
double newSkillFactor = SPMWeightAgainstAverage * SPMAgainstPlayWeight * SessionSPM;
|
||||
|
||||
// if the weight is greater than 1, add, else subtract
|
||||
DisconnectingPlayerStats.scorePerMinute += (SPMWeightAgainstAverage >= 1) ? newSkillFactor : -newSkillFactor;
|
||||
|
||||
DisconnectingPlayerStats.Skill = DisconnectingPlayerStats.scorePerMinute * KDRWeight / 10;
|
||||
DisconnectingPlayerStats.TotalPlayTime += newPlayTime;
|
||||
|
||||
curServer.playerStats.UpdateStats(P, DisconnectingPlayerStats);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
String Message = "";
|
||||
switch (killStreak)
|
||||
{
|
||||
case 5:
|
||||
Message = "Great job! You're on a ^55 killstreak!";
|
||||
break;
|
||||
case 10:
|
||||
Message = "Amazing! ^510 kills ^7without dying!";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (deathStreak)
|
||||
{
|
||||
case 5:
|
||||
Message = "Pick it up soldier, you've died ^55 times ^7in a row...";
|
||||
break;
|
||||
case 10:
|
||||
Message = "Seriously? ^510 deaths ^7without getting a kill?";
|
||||
break;
|
||||
}
|
||||
|
||||
return Message;
|
||||
}
|
||||
}
|
||||
|
||||
public class StatsDB : Database
|
||||
{
|
||||
public StatsDB(String FN) : base(FN) { }
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
if (!File.Exists(FileName))
|
||||
{
|
||||
String Create = "CREATE TABLE [STATS] ( [npID] TEXT, [KILLS] INTEGER DEFAULT 0, [DEATHS] INTEGER DEFAULT 0, [KDR] REAL DEFAULT 0, [SKILL] REAL DEFAULT 0, [MEAN] REAL DEFAULT 0, [DEV] REAL DEFAULT 0, [SPM] REAL DEFAULT 0, [PLAYTIME] INTEGER DEFAULT 0);";
|
||||
ExecuteNonQuery(Create);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPlayer(Player P)
|
||||
{
|
||||
Dictionary<String, object> newPlayer = new Dictionary<String, object>
|
||||
{
|
||||
{ "npID", P.NetworkID },
|
||||
{ "KILLS", 0 },
|
||||
{ "DEATHS", 0 },
|
||||
{ "KDR", 0.0 },
|
||||
{ "SKILL", 1.0 },
|
||||
{ "SPM", 1.0 },
|
||||
{ "PLAYTIME", 1.0 }
|
||||
};
|
||||
Insert("STATS", newPlayer);
|
||||
}
|
||||
|
||||
public PlayerStats GetStats(Player P)
|
||||
{
|
||||
DataTable Result = GetDataTable("STATS", new KeyValuePair<string, object>("npID", P.NetworkID));
|
||||
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
{
|
||||
DataRow ResponseRow = Result.Rows[0];
|
||||
return new PlayerStats(
|
||||
Convert.ToInt32(ResponseRow["KILLS"]),
|
||||
Convert.ToInt32(ResponseRow["DEATHS"]),
|
||||
Convert.ToDouble(ResponseRow["KDR"]),
|
||||
Convert.ToDouble(ResponseRow["SKILL"]),
|
||||
Convert.ToDouble(ResponseRow["SPM"]),
|
||||
Convert.ToInt32(ResponseRow["PLAYTIME"])
|
||||
);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
AddPlayer(P);
|
||||
return GetStats(P);
|
||||
}
|
||||
}
|
||||
|
||||
public long GetTotalServerKills()
|
||||
{
|
||||
var Result = GetDataTable("SELECT SUM(KILLS) FROM STATS");
|
||||
return Result.Rows[0][0].GetType() == typeof(DBNull) ? 0 : Convert.ToInt64(Result.Rows[0][0]);
|
||||
}
|
||||
|
||||
public long GetTotalServerPlaytime()
|
||||
{
|
||||
var Result = GetDataTable("SELECT SUM(PLAYTIME) FROM STATS");
|
||||
return Result.Rows[0][0].GetType() == typeof(DBNull) ? 0 : Convert.ToInt64(Result.Rows[0][0]) / 60;
|
||||
}
|
||||
|
||||
public void UpdateStats(Player P, PlayerStats S)
|
||||
{
|
||||
Dictionary<String, object> updatedPlayer = new Dictionary<String, object>
|
||||
{
|
||||
{ "KILLS", S.Kills },
|
||||
{ "DEATHS", S.Deaths },
|
||||
{ "KDR", Math.Round(S.KDR, 2) },
|
||||
{ "SKILL", Math.Round(S.Skill, 2) },
|
||||
{ "SPM", Math.Round(S.scorePerMinute, 2) },
|
||||
{ "PLAYTIME", S.TotalPlayTime }
|
||||
};
|
||||
Update("STATS", updatedPlayer, new KeyValuePair<string, object>("npID", P.NetworkID));
|
||||
}
|
||||
|
||||
public List<KeyValuePair<String, PlayerStats>> GetTopStats()
|
||||
{
|
||||
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>>();
|
||||
|
||||
if (Result != null && Result.Rows.Count > 0)
|
||||
{
|
||||
foreach (DataRow ResponseRow in Result.Rows)
|
||||
{
|
||||
pStats.Add( new KeyValuePair<String, PlayerStats>(ResponseRow["npID"].ToString(),
|
||||
new PlayerStats(
|
||||
Convert.ToInt32(ResponseRow["KILLS"]),
|
||||
Convert.ToInt32(ResponseRow["DEATHS"]),
|
||||
Convert.ToDouble(ResponseRow["KDR"]),
|
||||
Convert.ToDouble(ResponseRow["SKILL"]),
|
||||
Convert.ToDouble(ResponseRow["SPM"]),
|
||||
Convert.ToInt32(ResponseRow["PLAYTIME"])
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
return pStats;
|
||||
}
|
||||
}
|
||||
|
||||
public struct PlayerStats
|
||||
{
|
||||
public PlayerStats(int K, int D, double DR, double S, double sc, int P)
|
||||
{
|
||||
Kills = K;
|
||||
Deaths = D;
|
||||
KDR = DR;
|
||||
Skill = S;
|
||||
scorePerMinute = sc;
|
||||
TotalPlayTime = P;
|
||||
}
|
||||
|
||||
public int Kills;
|
||||
public int Deaths;
|
||||
public double KDR;
|
||||
public double Skill;
|
||||
public double scorePerMinute;
|
||||
public int TotalPlayTime;
|
||||
}
|
||||
}
|
36
Plugins/SimpleStats/Properties/AssemblyInfo.cs
Normal file
36
Plugins/SimpleStats/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("SamplePlugin")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("SamplePlugin")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("1d848d36-bf25-4bc0-acdd-67db2d014d45")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
78
Plugins/SimpleStats/Stats Plugin.csproj
Normal file
78
Plugins/SimpleStats/Stats Plugin.csproj
Normal file
@ -0,0 +1,78 @@
|
||||
<?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>{4785AB75-66F3-4391-985D-63A5A049A0FA}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>StatsPlugin</RootNamespace>
|
||||
<AssemblyName>StatsPlugin</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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data.SQLite">
|
||||
<HintPath>..\Admin\lib\System.Data.SQLite.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<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)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>
|
309
Plugins/VoteMap/Plugin.cs
Normal file
309
Plugins/VoteMap/Plugin.cs
Normal file
@ -0,0 +1,309 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using SharedLibrary;
|
||||
using SharedLibrary.Network;
|
||||
using SharedLibrary.Interfaces;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
#if DEBUG
|
||||
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.NetworkID))
|
||||
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.NetworkID, 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.NetworkID))
|
||||
{
|
||||
voting.cancelVote(E.Origin.NetworkID);
|
||||
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 OnLoadAsync()
|
||||
{
|
||||
serverVotingList = new List<ServerVoting>();
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
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 OnTickAsync(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.GetPlayersAsList().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 OnEventAsync(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
73
Plugins/VoteMap/Votemap Plugin.csproj
Normal file
73
Plugins/VoteMap/Votemap Plugin.csproj
Normal 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>{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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</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>
|
156
Plugins/Welcome/CountryLookup.cs
Normal file
156
Plugins/Welcome/CountryLookup.cs
Normal file
@ -0,0 +1,156 @@
|
||||
/* CountryLookup.cs
|
||||
*
|
||||
* Copyright (C) 2008 MaxMind, Inc. All Rights Reserved.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Data;
|
||||
using System.Net;
|
||||
|
||||
|
||||
namespace CountryLookupProj
|
||||
{
|
||||
/// <summary>
|
||||
/// Summary description for CountryLookup.
|
||||
/// </summary>
|
||||
public class CountryLookup
|
||||
{
|
||||
private FileStream fileInput;
|
||||
private static long COUNTRY_BEGIN = 16776960;
|
||||
private static string[] countryCode =
|
||||
{ "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN","AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB","BD","BE","BF","BG","BH","BI","BJ","BM","BN","BO","BR","BS","BT","BV","BW","BY","BZ","CA","CC","CD","CF","CG","CH","CI","CK","CL","CM","CN","CO","CR","CU","CV","CX","CY","CZ","DE","DJ","DK","DM","DO","DZ",
|
||||
"EC","EE","EG","EH","ER","ES","ET","FI","FJ","FK","FM","FO","FR","FX","GA","GB","GD","GE","GF","GH","GI","GL","GM","GN","GP","GQ","GR","GS","GT","GU","GW","GY","HK","HM","HN","HR","HT","HU","ID","IE","IL","IN","IO","IQ","IR","IS","IT","JM","JO","JP","KE","KG","KH","KI","KM","KN","KP","KR","KW","KY","KZ",
|
||||
"LA","LB","LC","LI","LK","LR","LS","LT","LU","LV","LY","MA","MC","MD","MG","MH","MK","ML","MM","MN","MO","MP","MQ","MR","MS","MT","MU","MV","MW","MX","MY","MZ","NA","NC","NE","NF","NG","NI","NL","NO","NP","NR","NU","NZ","OM","PA","PE","PF","PG","PH","PK","PL","PM","PN","PR","PS","PT","PW","PY","QA",
|
||||
"RE","RO","RU","RW","SA","SB","SC","SD","SE","SG","SH","SI","SJ","SK","SL","SM","SN","SO","SR","ST","SV","SY","SZ","TC","TD","TF","TG","TH","TJ","TK","TM","TN","TO","TL","TR","TT","TV","TW","TZ","UA","UG","UM","US","UY","UZ","VA","VC","VE","VG","VI","VN","VU","WF","WS","YE","YT","RS","ZA","ZM","ME","ZW","A1","A2",
|
||||
"O1","AX","GG","IM","JE","BL","MF"
|
||||
};
|
||||
private static string[] countryName =
|
||||
{"An Unknown Country","Asia/Pacific Region","Europe","Andorra","United Arab Emirates","Afghanistan","Antigua and Barbuda","Anguilla","Albania","Armenia","Netherlands Antilles","Angola","Antarctica","Argentina","American Samoa","Austria","Australia","Aruba","Azerbaijan","Bosnia and Herzegovina","Barbados","Bangladesh","Belgium",
|
||||
"Burkina Faso","Bulgaria","Bahrain","Burundi","Benin","Bermuda","Brunei Darussalam","Bolivia","Brazil","Bahamas","Bhutan","Bouvet Island","Botswana","Belarus","Belize","Canada","Cocos (Keeling) Islands","Congo, The Democratic Republic of the","Central African Republic","Congo","Switzerland","Cote D'Ivoire",
|
||||
"Cook Islands","Chile","Cameroon","China","Colombia","Costa Rica","Cuba","Cape Verde","Christmas Island","Cyprus","Czech Republic","Germany","Djibouti","Denmark","Dominica","Dominican Republic","Algeria","Ecuador","Estonia","Egypt","Western Sahara","Eritrea","Spain","Ethiopia","Finland","Fiji","Falkland Islands (Malvinas)",
|
||||
"Micronesia, Federated States of","Faroe Islands","France","France, Metropolitan","Gabon","United Kingdom","Grenada","Georgia","French Guiana","Ghana","Gibraltar","Greenland","Gambia","Guinea","Guadeloupe","Equatorial Guinea","Greece","South Georgia and the South Sandwich Islands","Guatemala","Guam","Guinea-Bissau","Guyana",
|
||||
"Hong Kong","Heard Island and McDonald Islands","Honduras","Croatia","Haiti","Hungary","Indonesia","Ireland","Israel","India","British Indian Ocean Territory","Iraq","Iran, Islamic Republic of","Iceland","Italy","Jamaica","Jordan","Japan","Kenya","Kyrgyzstan","Cambodia","Kiribati","Comoros","Saint Kitts and Nevis",
|
||||
"Korea, Democratic People's Republic of","Korea, Republic of","Kuwait","Cayman Islands","Kazakstan","Lao People's Democratic Republic","Lebanon","Saint Lucia","Liechtenstein","Sri Lanka","Liberia","Lesotho","Lithuania","Luxembourg","Latvia","Libyan Arab Jamahiriya","Morocco","Monaco","Moldova, Republic of","Madagascar",
|
||||
"Marshall Islands","Macedonia","Mali","Myanmar","Mongolia","Macau","Northern Mariana Islands","Martinique","Mauritania","Montserrat","Malta","Mauritius","Maldives","Malawi","Mexico","Malaysia","Mozambique","Namibia","New Caledonia","Niger","Norfolk Island","Nigeria","Nicaragua","Netherlands",
|
||||
"Norway","Nepal","Nauru","Niue","New Zealand","Oman","Panama","Peru","French Polynesia","Papua New Guinea","Philippines","Pakistan","Poland","Saint Pierre and Miquelon","Pitcairn Islands","Puerto Rico","Palestinian Territory","Portugal","Palau","Paraguay","Qatar","Reunion","Romania","Russian Federation","Rwanda","Saudi Arabia",
|
||||
"Solomon Islands","Seychelles","Sudan","Sweden","Singapore","Saint Helena","Slovenia","Svalbard and Jan Mayen","Slovakia","Sierra Leone","San Marino","Senegal","Somalia","Suriname","Sao Tome and Principe","El Salvador","Syrian Arab Republic","Swaziland","Turks and Caicos Islands","Chad","French Southern Territories","Togo",
|
||||
"Thailand","Tajikistan","Tokelau","Turkmenistan","Tunisia","Tonga","Timor-Leste","Turkey","Trinidad and Tobago","Tuvalu","Taiwan","Tanzania, United Republic of","Ukraine","Uganda","United States Minor Outlying Islands","United States","Uruguay","Uzbekistan","Holy See (Vatican City State)","Saint Vincent and the Grenadines",
|
||||
"Venezuela","Virgin Islands, British","Virgin Islands, U.S.","Vietnam","Vanuatu","Wallis and Futuna","Samoa","Yemen","Mayotte","Serbia","South Africa","Zambia","Montenegro","Zimbabwe","Anonymous Proxy","Satellite Provider",
|
||||
"Other","Aland Islands","Guernsey","Isle of Man","Jersey","Saint Barthelemy","Saint Martin"};
|
||||
|
||||
|
||||
public CountryLookup(string fileName)
|
||||
{
|
||||
fileInput = new FileStream(fileName, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
|
||||
public string lookupCountryCode(string str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "--";
|
||||
}
|
||||
return lookupCountryCode(addr);
|
||||
}
|
||||
|
||||
private long addrToNum(IPAddress addr)
|
||||
{
|
||||
long ipnum = 0;
|
||||
byte[] b = BitConverter.GetBytes(addr.Address);
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
long y = b[i];
|
||||
if (y < 0)
|
||||
{
|
||||
y += 256;
|
||||
}
|
||||
ipnum += y << ((3 - i) * 8);
|
||||
}
|
||||
return ipnum;
|
||||
}
|
||||
|
||||
public string lookupCountryCode(IPAddress addr)
|
||||
{
|
||||
return (countryCode[(int)SeekCountry(0, addrToNum(addr), 31)]);
|
||||
}
|
||||
|
||||
public string lookupCountryName(string str)
|
||||
{
|
||||
IPAddress addr;
|
||||
try
|
||||
{
|
||||
addr = IPAddress.Parse(str);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
return "An Unknown Country";
|
||||
}
|
||||
return lookupCountryName(addr);
|
||||
}
|
||||
|
||||
public string lookupCountryName(IPAddress addr)
|
||||
{
|
||||
return (countryName[(int)SeekCountry(0, addrToNum(addr), 31)]);
|
||||
}
|
||||
|
||||
private long SeekCountry(long offset, long ipnum, int depth)
|
||||
{
|
||||
byte[] buf = new byte[6];
|
||||
long[] x = new long[2];
|
||||
|
||||
fileInput.Seek(6 * offset, 0);
|
||||
fileInput.Read(buf, 0, 6);
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
x[i] = 0;
|
||||
for (int j = 0; j < 3; j++)
|
||||
{
|
||||
int y = buf[i * 3 + j];
|
||||
if (y < 0)
|
||||
{
|
||||
y += 256;
|
||||
}
|
||||
x[i] += (y << (j * 8));
|
||||
}
|
||||
}
|
||||
|
||||
if ((ipnum & (1 << depth)) > 0)
|
||||
{
|
||||
if (x[1] >= COUNTRY_BEGIN)
|
||||
{
|
||||
return x[1] - COUNTRY_BEGIN;
|
||||
}
|
||||
return SeekCountry(x[1], ipnum, depth - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x[0] >= COUNTRY_BEGIN)
|
||||
{
|
||||
return x[0] - COUNTRY_BEGIN;
|
||||
}
|
||||
return SeekCountry(x[0], ipnum, depth - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
Plugins/Welcome/GeoIP.dat
Normal file
BIN
Plugins/Welcome/GeoIP.dat
Normal file
Binary file not shown.
109
Plugins/Welcome/Plugin.cs
Normal file
109
Plugins/Welcome/Plugin.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using SharedLibrary;
|
||||
using System.Collections.Generic;
|
||||
using SharedLibrary.Interfaces;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using SharedLibrary.Network;
|
||||
|
||||
namespace Welcome_Plugin
|
||||
{
|
||||
public class Plugin : IPlugin
|
||||
{
|
||||
Dictionary<int, float> PlayerPings;
|
||||
int PingAverageCount;
|
||||
|
||||
public string Author
|
||||
{
|
||||
get
|
||||
{
|
||||
return "RaidMax";
|
||||
}
|
||||
}
|
||||
|
||||
public float Version
|
||||
{
|
||||
get
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "Welcome Plugin";
|
||||
}
|
||||
}
|
||||
|
||||
public async Task OnLoadAsync()
|
||||
{
|
||||
PlayerPings = new Dictionary<int, float>();
|
||||
PingAverageCount = 1;
|
||||
}
|
||||
|
||||
public async Task OnUnloadAsync()
|
||||
{
|
||||
PlayerPings.Clear();
|
||||
PlayerPings = null;
|
||||
}
|
||||
|
||||
public async Task OnTickAsync(Server S)
|
||||
{
|
||||
int MaxPing = (await S.GetDvarAsync<int>("sv_maxping")).Value;
|
||||
|
||||
if (MaxPing == 0)
|
||||
return;
|
||||
|
||||
foreach (int PlayerID in PlayerPings.Keys)
|
||||
{
|
||||
var Player = S.Players.Find(p => p.DatabaseID == PlayerID);
|
||||
PlayerPings[PlayerID] = PlayerPings[PlayerID] + (Player.Ping - PlayerPings[PlayerID]) / PingAverageCount;
|
||||
if (PlayerPings[PlayerID] > MaxPing)
|
||||
await Player.Kick($"Your average ping of ^5{PlayerPings[PlayerID]} ^7is too high for this server", null);
|
||||
}
|
||||
|
||||
if (PingAverageCount > 100)
|
||||
PingAverageCount = 1;
|
||||
}
|
||||
|
||||
public async Task OnEventAsync(Event E, Server S)
|
||||
{
|
||||
if (E.Type == Event.GType.Connect)
|
||||
{
|
||||
Player newPlayer = E.Origin;
|
||||
|
||||
if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked)
|
||||
await E.Owner.Broadcast(Utilities.levelToColor(newPlayer.Level) + " ^5" + newPlayer.Name + " ^7has joined the server.");
|
||||
|
||||
await newPlayer.Tell($"Welcome ^5{newPlayer.Name}^7, this your ^5{newPlayer.TimesConnected()} ^7time connecting!");
|
||||
|
||||
if (newPlayer.Level == Player.Permission.Flagged)
|
||||
await E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name}^7 has joined!");
|
||||
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
CountryLookupProj.CountryLookup CLT = new CountryLookupProj.CountryLookup("Plugins/GeoIP.dat");
|
||||
await E.Owner.Broadcast($"^5{newPlayer.Name} ^7hails from ^5{CLT.lookupCountryName(newPlayer.IP)}");
|
||||
}
|
||||
|
||||
catch (Exception)
|
||||
{
|
||||
E.Owner.Manager.GetLogger().WriteError("Could not open file Plugins/GeoIP.dat for Welcome Plugin");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PlayerPings.Add(E.Origin.DatabaseID, 1.0f);
|
||||
}
|
||||
|
||||
if (E.Type == Event.GType.Disconnect)
|
||||
{
|
||||
PlayerPings.Remove(E.Origin.DatabaseID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
36
Plugins/Welcome/Properties/AssemblyInfo.cs
Normal file
36
Plugins/Welcome/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Welcome Plugin")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Welcome Plugin")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2015")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fd92be42-d165-449d-8648-fb1f6638b451")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// 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("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
84
Plugins/Welcome/Welcome Plugin.csproj
Normal file
84
Plugins/Welcome/Welcome Plugin.csproj
Normal file
@ -0,0 +1,84 @@
|
||||
<?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>{AF097E6B-48D5-4452-9CCF-0A81A21F341D}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Welcome_Plugin</RootNamespace>
|
||||
<AssemblyName>WelcomePlugin</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>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release-Stable|AnyCPU'">
|
||||
<OutputPath>bin\Release-Stable\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</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.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="CountryLookup.cs" />
|
||||
<Compile Include="Plugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="GeoIP.dat" />
|
||||
<None Include="packages.config" />
|
||||
</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)BUILD\plugins\"
|
||||
copy /Y "$(ProjectDir)GeoIP.dat" "$(SolutionDir)Admin\bin\$(ConfigurationName)\GeoIP.dat"</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>
|
4
Plugins/Welcome/packages.config
Normal file
4
Plugins/Welcome/packages.config
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Newtonsoft.Json" version="10.0.2" targetFramework="net451" />
|
||||
</packages>
|
Reference in New Issue
Block a user