cleaned up some namespace discrepancies

fixed the coloring for custom groups translation
add reserved slots
add webhook project to show notifications in discord
This commit is contained in:
RaidMax 2018-08-07 13:43:09 -05:00
parent bbade07646
commit a0fafe5797
27 changed files with 301 additions and 63 deletions

1
.gitignore vendored
View File

@ -226,3 +226,4 @@ bootstrap-custom.min.css
/WebfrontCore/Views/Plugins/Stats
/WebfrontCore/wwwroot/**/dds
/DiscordWebhook/env

View File

@ -5,7 +5,7 @@
<TargetFramework>netcoreapp2.1</TargetFramework>
<MvcRazorExcludeRefAssembliesFromPublish>false</MvcRazorExcludeRefAssembliesFromPublish>
<PackageId>RaidMax.IW4MAdmin.Application</PackageId>
<Version>2.1.2</Version>
<Version>2.1.4</Version>
<Authors>RaidMax</Authors>
<Company>Forever None</Company>
<Product>IW4MAdmin</Product>

View File

@ -65,6 +65,12 @@ namespace IW4MAdmin.Application.Core
public IList<Player> GetAuthenticatedClients()
{
if (AuthenticatedClients.Values.Count > 18)
{
Program.ServerManager.GetLogger().WriteWarning($"auth client count is {AuthenticatedClients.Values.Count}, this is bad");
return AuthenticatedClients.Values.Take(18).ToList();
}
return AuthenticatedClients.Values.ToList();
}

View File

@ -10,7 +10,7 @@ namespace IW4MAdmin.Application.EventParsers
{
class IW4EventParser : IEventParser
{
private const string SayRegex = @"(say|sayteam);(.{16,32});([0-9]+)(.*);(.*)";
private const string SayRegex = @"(say|sayteam);(.{1,32});([0-9]+)(.*);(.*)";
public virtual GameEvent GetEvent(Server server, string logLine)
{
@ -121,7 +121,7 @@ namespace IW4MAdmin.Application.EventParsers
// join
if (eventType == "J")
{
var regexMatch = Regex.Match(logLine, @"^(J;)(.{4,32});([0-9]+);(.*)$");
var regexMatch = Regex.Match(logLine, @"^(J;)(.{1,32});([0-9]+);(.*)$");
if (regexMatch.Success)
{
return new GameEvent()
@ -141,7 +141,7 @@ namespace IW4MAdmin.Application.EventParsers
if (eventType == "Q")
{
var regexMatch = Regex.Match(logLine, @"^(Q;)(.{4,32});([0-9]+);(.*)$");
var regexMatch = Regex.Match(logLine, @"^(Q;)(.{1,32});([0-9]+);(.*)$");
if (regexMatch.Success)
{
return new GameEvent()

View File

@ -39,7 +39,7 @@ namespace IW4MAdmin.Application
catch (Exception) { }
string LogLine = $"[{DateTime.Now.ToString("HH:mm:ss")}] - {stringType}: {msg}";
string LogLine = $"[{DateTime.Now.ToString("MM.dd.yyy HH:mm:ss.fff")}] - {stringType}: {msg}";
lock (ThreadLock)
{
#if DEBUG

View File

@ -5,7 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Application.Misc
namespace IW4MAdmin.Application.Misc
{
public class VPNCheck
{

View File

@ -1,14 +1,13 @@
using Application.RconParsers;
using SharedLibraryCore.RCon;
using SharedLibraryCore.RCon;
using System;
using System.Collections.Generic;
using System.Text;
namespace Application.RconParsers
namespace IW4MAdmin.Application.RconParsers
{
class IW3RConParser : IW4RConParser
{
private static CommandPrefix Prefixes = new CommandPrefix()
private static readonly CommandPrefix Prefixes = new CommandPrefix()
{
Tell = "tell {0} {1}",
Say = "say {0}",

View File

@ -10,7 +10,7 @@ using SharedLibraryCore;
using SharedLibraryCore.RCon;
using SharedLibraryCore.Exceptions;
namespace Application.RconParsers
namespace IW4MAdmin.Application.RconParsers
{
class IW4RConParser : IRConParser
{
@ -111,18 +111,12 @@ namespace Application.RconParsers
Name = name,
NetworkId = networkId,
ClientNumber = clientNumber,
IPAddress = ip,
IPAddress = ip == 0 ? int.MinValue : ip,
Ping = ping,
Score = score,
IsBot = ip == 0
};
if (P.IsBot)
{
// set it to 127.0.0.2
P.IPAddress = 33554559;
}
StatusPlayers.Add(P);
}
}

View File

@ -2,17 +2,15 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Text;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Objects;
using SharedLibraryCore.RCon;
using SharedLibraryCore.Exceptions;
using System.Text;
using System.Linq;
using System.Net.Http;
namespace Application.RconParsers
namespace IW4MAdmin.WApplication.RconParsers
{
public class IW5MRConParser : IRConParser
{

View File

@ -12,7 +12,7 @@ using System.Text;
using System.Linq;
using System.Net.Http;
namespace Application.RconParsers
namespace IW4MAdmin.Application.RconParsers
{
public class T6MRConParser : IRConParser
{

View File

@ -14,13 +14,14 @@ using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Localization;
using Application.Misc;
using Application.RconParsers;
using IW4MAdmin.Application.Misc;
using IW4MAdmin.Application.RconParsers;
using IW4MAdmin.Application.EventParsers;
using IW4MAdmin.Application.IO;
using SharedLibraryCore.Localization;
using IW4MAdmin.Application.Core;
using IW4MAdmin.WApplication.RconParsers;
namespace IW4MAdmin
{
@ -56,7 +57,6 @@ namespace IW4MAdmin
public async Task OnPlayerJoined(Player logClient)
{
if (Players[logClient.ClientNumber] == null ||
Players[logClient.ClientNumber].NetworkId != logClient.NetworkId)
{
@ -184,6 +184,16 @@ namespace IW4MAdmin
player = client.AsPlayer();
}
// reserved slots stuff
if ((MaxClients - ClientNum) < ServerConfig.ReservedSlotNumber &&
! player.IsPrivileged())
{
Logger.WriteDebug($"Kicking {polledPlayer} their spot is reserved");
string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_SLOT_IS_RESERVED"]);
await this.ExecuteCommandAsync(formattedKick);
return false;
}
Logger.WriteInfo($"Client {player} connected...");
// Do the player specific stuff
@ -590,7 +600,6 @@ namespace IW4MAdmin
AuthQueue.AuthenticateClients(CurrentPlayers);
// all polled players should be authenticated
var addPlayerTasks = AuthQueue.GetAuthenticatedClients()
.Select(client => AddPlayer(client));
@ -796,7 +805,7 @@ namespace IW4MAdmin
CustomCallback = await ScriptLoaded();
string mainPath = EventParser.GetGameDir();
#if DEBUG
basepath.Value = @"D:\";
basepath.Value = @"\\192.168.88.253\Call of Duty Black Ops II";
#endif
string logPath;
if (GameName == Game.IW5)

View File

@ -0,0 +1,134 @@
import requests
import time
import json
import collections
class WebhookAuthor():
def __init__(self, name=None, url=None, icon_url=None):
if name:
self.name = name
if url:
self.url = url
if icon_url:
self.icon_url = icon_url
class WebhookField():
def __init__(self, name=None, value=None, inline=False):
if name:
self.name = name
if value:
self.value = value
if inline:
self.inline = inline
class WebhookEmbed():
def __init__(self):
self.author = ''
self.title = ''
self.url = ''
self.description = ''
self.color = 0
self.fields = []
self.thumbnail = {}
class WebhookParams():
def __init__(self, username=None, avatar_url=None, content=None):
self.username = ''
self.avatar_url = ''
self.content = ''
self.embeds = []
def to_json(self):
return json.dumps(self, default=lambda o: o.__dict__, sort_keys=True)
def get_client_profile(profile_id):
return '{}/Client/ProfileAsync/{}'.format(base_url, str(profile_id))
with open('config.json') as json_config_file:
json_config = json.load(json_config_file)
# this should be an URL to an IP or FQN to an IW4MAdmin instance
# ie http://127.0.0.1 or http://IW4MAdmin.com
base_url = json_config['IW4MAdminUrl']
end_point = '/api/event'
request_url = base_url + end_point
# this should be the full discord webhook url
# ie https://discordapp.com/api/webhooks/<id>/<token>
discord_webhook_url = json_config['DiscordWebhookUrl']
# this should be the numerical id of the discord group
# 12345678912345678
notify_role_ids = json_config['NotifyRoleIds']
def get_new_events():
events = []
response = requests.get(request_url)
data = response.json()
for event in data:
# commonly used event info items
event_type = event['eventType']['name']
server_name = event['ownerEntity']['name']
webhook_item = WebhookParams()
webhook_item_embed = WebhookEmbed()
webhook_item.username = 'IW4MAdmin'
webhook_item.avatar_url = 'https://raidmax.org/IW4MAdmin/img/iw4adminicon-3.png'
webhook_item_embed.color = 31436
webhook_item_embed.url = base_url
webhook_item_embed.thumbnail = { 'url' : 'https://raidmax.org/IW4MAdmin/img/iw4adminicon-3.png' }
webhook_item.embeds.append(webhook_item_embed)
role_ids_string = ''
for id in notify_role_ids:
role_ids_string += '\r\n<@&{}>\r\n'.format(id)
webhook_notifyrole = WebhookField('Notifies',role_ids_string)
if event_type == 'Report':
origin_client_name = event['originEntity']['name']
origin_client_id = int(event['originEntity']['id'])
target_client_name = event['targetEntity']['name']
target_client_id = int(event['targetEntity']['id'])
report_reason = event['extraInfo']
server_field = WebhookField('Server', server_name)
report_reason_field = WebhookField('Reason', report_reason)
reported_by_field = WebhookField('By', '[{}]({})'.format(origin_client_name, get_client_profile(origin_client_id)))
reported_field = WebhookField('Reported Player', '[{}]({})'.format(target_client_name, get_client_profile(target_client_id)))
webhook_item_embed.title = 'Player Reported'
webhook_item_embed.fields.append(server_field)
webhook_item_embed.fields.append(reported_field)
webhook_item_embed.fields.append(reported_by_field)
webhook_item_embed.fields.append(report_reason_field)
#make sure there's at least one group to notify
if len(notify_role_ids) > 0:
webhook_item.content = role_ids_string
else:
continue
events.append(webhook_item)
return events
def execute_webhook(data):
for event in data:
event_json = event.to_json()
response = requests.post(discord_webhook_url, data=event_json, headers={'Content-type' : 'application/json'})
def run():
while True:
try:
new_events = get_new_events()
execute_webhook(new_events)
except:
print('failed to get new events')
time.sleep(2.5)
if __name__ == "__main__":
run()

View File

@ -0,0 +1,50 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>15a81d6e-7502-46ce-8530-0647a380b5f4</ProjectGuid>
<ProjectHome>.</ProjectHome>
<StartupFile>DiscordWebhook.py</StartupFile>
<SearchPath>
</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<Name>DiscordWebhook</Name>
<RootNamespace>DiscordWebhook</RootNamespace>
<InterpreterId>MSBuild|env|$(MSBuildProjectFullPath)</InterpreterId>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="DiscordWebhook.py" />
</ItemGroup>
<ItemGroup>
<Interpreter Include="env\">
<Id>env</Id>
<Version>3.6</Version>
<Description>env (Python 3.6 (64-bit))</Description>
<InterpreterPath>Scripts\python.exe</InterpreterPath>
<WindowsInterpreterPath>Scripts\pythonw.exe</WindowsInterpreterPath>
<PathEnvironmentVariable>PYTHONPATH</PathEnvironmentVariable>
<Architecture>X64</Architecture>
</Interpreter>
</ItemGroup>
<ItemGroup>
<Content Include="config.json" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Python Tools\Microsoft.PythonTools.targets" />
<!-- Uncomment the CoreCompile target to enable the Build command in
Visual Studio and specify your pre- and post-build commands in
the BeforeBuild and AfterBuild targets below. -->
<!--<Target Name="CoreCompile" />-->
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
</Project>

View File

@ -0,0 +1,5 @@
{
"IW4MAdminUrl": "",
"DiscordWebhookUrl": "",
"NotifyRoleIds": []
}

View File

@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Plugins\Tests\Test
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IW4ScriptCommands", "Plugins\IW4ScriptCommands\IW4ScriptCommands.csproj", "{6C706CE5-A206-4E46-8712-F8C48D526091}"
EndProject
Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "DiscordWebhook", "DiscordWebhook\DiscordWebhook.pyproj", "{15A81D6E-7502-46CE-8530-0647A380B5F4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -287,6 +289,18 @@ Global
{6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x64.Build.0 = Release|Any CPU
{6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.ActiveCfg = Release|Any CPU
{6C706CE5-A206-4E46-8712-F8C48D526091}.Release|x86.Build.0 = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x64.ActiveCfg = Debug|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Debug|x86.ActiveCfg = Debug|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Any CPU.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|Mixed Platforms.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x64.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Prerelease|x86.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x64.ActiveCfg = Release|Any CPU
{15A81D6E-7502-46CE-8530-0647A380B5F4}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -411,14 +411,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
kill.DeathType == IW4Info.MeansOfDeath.MOD_HEAD_SHOT)
{
clientStats.HitLocations.Single(hl => hl.Location == kill.HitLoc).HitCount += 1;
//statsSvc.ClientStatSvc.Update(clientStats);
// await statsSvc.ClientStatSvc.SaveChangesAsync();
}
//statsSvc.KillStatsSvc.Insert(kill);
//await statsSvc.KillStatsSvc.SaveChangesAsync();
if (Plugin.Config.Configuration().EnableAntiCheat)
{
async Task executePenalty(Cheat.DetectionPenaltyResult penalty)

View File

@ -14,6 +14,7 @@ namespace SharedLibraryCore.Configuration
public bool UseT6MParser { get; set; }
public bool UseIW5MParser { get; set; }
public string ManualLogPath { get; set; }
public int ReservedSlotNumber { get; set; }
public IBaseConfiguration Generate()
{
@ -46,6 +47,8 @@ namespace SharedLibraryCore.Configuration
if (UseIW5MParser)
ManualLogPath = Utilities.PromptString(loc["SETUP_SERVER_MANUALLOG"]);
ReservedSlotNumber = loc["SETUP_SERVER_RESERVEDSLOT"].PromptInt(0, 32);
return this;
}

View File

@ -13,6 +13,7 @@ namespace SharedLibraryCore.Dtos
public string PunisherName { get; set; }
public int PunisherId { get; set; }
public string PunisherLevel { get; set; }
public int PunisherLevelId { get; set; }
public string Offense { get; set; }
public string AutomatedOffense { get; set; }
public string Type { get; set; }

View File

@ -199,7 +199,8 @@ namespace SharedLibraryCore
return id;
var bot = Regex.Match(str, @"bot[0-9]+").Value;
if (!string.IsNullOrEmpty(bot))
return -1;//Convert.ToInt64(bot.Substring(3)) + 1;
// should set their GUID to the negation of their 1 based index (-1 - -18)
return -(Convert.ToInt64(bot.Substring(3)) + 1);
return 0;
}
@ -365,7 +366,7 @@ namespace SharedLibraryCore
CurrentAlias = client.CurrentAlias,
CurrentAliasId = client.CurrentAlias.AliasId,
// todo: make sure this is up to date
IsBot = client.NetworkId == -1,
IsBot = client.IPAddress == int.MinValue,
Password = client.Password,
PasswordSalt = client.PasswordSalt
};
@ -379,6 +380,33 @@ namespace SharedLibraryCore
return (Console.ReadLine().ToLower().FirstOrDefault() as char?) == 'y';
}
/// <summary>
/// prompt user to enter a number
/// </summary>
/// <param name="question">question to prompt with</param>
/// <param name="maxValue">maximum value to allow</param>
/// <param name="minValue">minimum value to allow</param>
/// <returns>integer from user's input</returns>
public static int PromptInt(this string question, int minValue = 0, int maxValue = int.MaxValue)
{
Console.Write($"{question}: ");
int response;
while (!int.TryParse(Console.ReadLine(), out response) ||
response < minValue ||
response > maxValue)
{
string range = "";
if (minValue != 0 || maxValue != int.MaxValue)
{
range = $" [{minValue}-{maxValue}]";
}
Console.Write($"Please enter a valid number{range}: ");
}
return response;
}
public static string PromptString(string question)
{
string response;
@ -455,6 +483,5 @@ namespace SharedLibraryCore
var response = await server.RemoteConnection.SendQueryAsync(RCon.StaticHelpers.QueryType.GET_INFO);
return response.FirstOrDefault(r => r[0] == '\\')?.DictionaryFromKeyValue();
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using System;
using System.Collections.Generic;
using System.Linq;
@ -39,8 +40,8 @@ namespace WebfrontCore.Controllers.API
player.Ping,
State = player.State.ToString(),
player.ClientNumber,
ConnectionTime = (DateTime.UtcNow - player.ConnectionTime).TotalSeconds,
player.Level,
ConnectionTime = Math.Round((DateTime.UtcNow - player.ConnectionTime).TotalSeconds, 0),
Level = player.Level.ToLocalizedLevelName(),
})
});

View File

@ -21,6 +21,7 @@ namespace WebfrontCore.ViewComponents
PunisherId = p.PunisherId,
PunisherName = p.Punisher.Name,
PunisherLevel = p.Punisher.Level.ToLocalizedLevelName(),
PunisherLevelId = (int)p.Punisher.Level,
#if DEBUG
Offense = !string.IsNullOrEmpty(p.AutomatedOffense) ? p.AutomatedOffense : p.Offense,
#else

View File

@ -16,7 +16,7 @@
{
<div class="row pt-2 pb-2 bg-dark">
<div class="col-5">@Html.ActionLink(client.Name, "ProfileAsync", "Client", new { id = client.ClientId })</div>
<div class="col-4 level-color-@client.Level.ToLower()">@client.Level</div>
<div class="col-4 level-color-@client.LevelInt">@client.Level</div>
<div class="col-3 text-right">@client.LastSeen @loc["WEBFRONT_PENALTY_TEMPLATE_AGO"]</div>
</div>
}

View File

@ -7,7 +7,7 @@
}
<div id="profile_wrapper" class="row d-flex d-sm-inline-flex justify-content-center justify-content-left pb-3">
<div class="mr-auto ml-auto ml-sm-0 mr-sm-0">
<div id="profile_avatar" class="mb-4 mb-md-0 text-center level-bgcolor-@Model.Level.ToLower()" style="background-image:url('@string.Format("https://gravatar.com/avatar/{0}?size=168&default=blank&rating=pg", gravatarUrl)">
<div id="profile_avatar" class="mb-4 mb-md-0 text-center level-bgcolor-@Model.LevelInt" style="background-image:url('@string.Format("https://gravatar.com/avatar/{0}?size=168&default=blank&rating=pg", gravatarUrl)">
@if (string.IsNullOrEmpty(gravatarUrl))
{
<span class="profile-shortcode">@shortCode</span>
@ -71,7 +71,7 @@
}
</div>
<div id="profile_level" class="text-muted mb-2">
<h5><span class="level-color-@Model.Level.ToLower()"><strong>@Model.Level</strong></span></h5>
<h5><span class="level-color-@Model.LevelInt"><strong>@Model.Level</strong></span></h5>
</div>
<div id="profile_time_played" class="text-muted">
@loc["WEBFRONT_PROFILE_PLAYER"] <span class="text-primary">@Model.TimePlayed</span> @loc["GLOBAL_TIME_HOURS"]

View File

@ -29,7 +29,7 @@
<tr class="d-table-row d-md-none bg-dark">
<th scope="row" class="bg-primary">@loc["WEBFRONT_PENALTY_TEMPLATE_ADMIN"]</th>
<td>
@Html.ActionLink(Model.PunisherName, "ProfileAsync", "Client", new { id = Model.PunisherId }, new { @class = "level-color-" + Model.PunisherLevel.ToLower() })
@Html.ActionLink(Model.PunisherName, "ProfileAsync", "Client", new { id = Model.PunisherId }, new { @class = "level-color-" + Model.PunisherLevelId }) })
</td>
</tr>
@ -60,7 +60,7 @@
@Model.Offense
</td>
<td>
@Html.ActionLink(Model.PunisherName, "ProfileAsync", "Client", new { id = Model.PunisherId }, new { @class = "level-color-" + Model.PunisherLevel.ToLower() })
@Html.ActionLink(Model.PunisherName, "ProfileAsync", "Client", new { id = Model.PunisherId }, new { @class = "level-color-" + Model.PunisherLevelId }) })
</td>
<td class="text-right text-light">
@{

View File

@ -36,7 +36,7 @@
@{
for (int i = 0; i < half; i++)
{
string levelColorClass = !ViewBag.Authorized ? "" : $"level-color-{Model.Players[i].Level.ToLower()}";
string levelColorClass = !ViewBag.Authorized ? "" : $"level-color-{Model.Players[i].LevelInt}";
<span>@Html.ActionLink(Model.Players[i].Name, "ProfileAsync", "Client", new { id = Model.Players[i].ClientId }, new { @class = levelColorClass })</span><br />
}
}
@ -45,7 +45,7 @@
@{
for (int i = half; i < Model.ClientCount; i++)
{
string levelColorClass = !ViewBag.Authorized ? "" : $"level-color-{Model.Players[i].Level.ToLower()}";
string levelColorClass = !ViewBag.Authorized ? "" : $"level-color-{Model.Players[i].LevelInt}";
<span>@Html.ActionLink(Model.Players[i].Name, "ProfileAsync", "Client", new { id = Model.Players[i].ClientId }, new { @class = levelColorClass })</span><br />
}
}

View File

@ -44,6 +44,7 @@
<None Include="wwwroot\css\global.min.css" CopyToPublishDirectory="Always" />
<None Include="wwwroot\js\global.min.js" CopyToPublishDirectory="Always" />
<None Include="wwwroot\images\icon.png" CopyToPublishDirectory="Always" />
<None Include="wwwroot\images\icons\**\*.png" CopyToPublishDirectory="Always"/>
<None Include="wwwroot\lib\open-iconic\font\fonts\open-iconic.ttf" CopyToPublishDirectory="Always" />
<None Include="wwwroot\lib\open-iconic\font\fonts\open-iconic.woff" CopyToPublishDirectory="Always" />
<None Include="wwwroot\lib\open-iconic\font\fonts\open-iconic.otf" CopyToPublishDirectory="Always" />

View File

@ -2,81 +2,81 @@
background-color: grey;
}
.level-color-user, .level-color-guest {
.level-color-user, .level-color-guest, .level-color-0 {
color: #6c757d;
color: rgba(255, 255, 255, 0.68);
}
.level-bgcolor-user, .level-bgcolor-guest {
.level-bgcolor-user, .level-bgcolor-guest, .level-bgcolor-0 {
background-color: #6c757d;
background-color: rgba(255, 255, 255, 0.68);
}
.level-color-trusted {
.level-color-trusted, .level-color-2 {
color: #749363;
color: rgba(116,147,99,1);
}
.level-bgcolor-trusted {
.level-bgcolor-trusted, .level-bgcolor-2 {
background-color: #749363;
background-color: rgba(116,147,99,1);
}
.level-color-flagged {
.level-color-flagged, .level-color-1 {
color: #fd9c38;
color: rgba(253, 139, 22, 0.85);
}
.level-bgcolor-flagged {
.level-bgcolor-flagged, .level-bgcolor-1 {
background-color: #fd9c38;
background-color: rgba(253, 139, 22, 0.85);
}
.level-color-banned, .level-color-console {
.level-color-banned, .level-color-console, .level-color--1 {
color: #ff6060;
color: rgba(255, 69, 69, 0.85);
}
.level-bgcolor-banned {
.level-bgcolor-banned, .level-bgcolor--1 {
background-color: #ff6060;
background-color: rgba(255, 69, 69, 0.85);
}
.level-color-moderator {
.level-color-moderator, .level-color-3 {
color: #f0de8b;
color: rgba(235, 211, 101, 0.75);
}
.level-bgcolor-moderator {
.level-bgcolor-moderator, .level-bgcolor-3 {
background-color: #f0de8b;
background-color: rgba(235, 211, 101, 0.75);
}
.level-color-administrator {
.level-color-administrator, .level-color-4 {
color: #f1a8e8;
color: rgba(236, 130, 222, 0.69);
}
.level-bgcolor-administrator {
.level-bgcolor-administrator, .level.bgcolor-4 {
background-color: #f1a8e8;
background-color: rgba(236, 130, 222, 0.69);
}
.level-color-senioradmin {
.level-color-senioradmin, .level-color-5 {
color: #50bcc3;
color: rgba(50, 177, 185, 0.85);
}
.level-bgcolor-senioradmin {
.level-bgcolor-senioradmin, .level-bgcolor-5 {
background-color: #50bcc3;
background-color: rgba(50, 177, 185, 0.85);
}
.level-color-owner {
.level-color-owner, .level-color-6 {
color: rgb(0, 122, 204);
}
.level-bgcolor-owner {
.level-bgcolor-owner, .level-bgcolor-6 {
background-color: rgb(0, 122, 204);
}