final changes for 2.0 release
This commit is contained in:
parent
30ec0527d2
commit
c60a1f3d33
@ -18,10 +18,12 @@
|
||||
<ApplicationIcon />
|
||||
<AssemblyName>IW4MAdmin</AssemblyName>
|
||||
<Configurations>Debug;Release;Prerelease</Configurations>
|
||||
<Win32Resource />
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="RestEase" Version="1.4.5" />
|
||||
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -34,6 +36,21 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="DefaultSettings.json">
|
||||
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||
|
@ -18,6 +18,7 @@ using WebfrontCore;
|
||||
using SharedLibraryCore.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace IW4MAdmin.Application
|
||||
{
|
||||
@ -161,6 +162,9 @@ namespace IW4MAdmin.Application
|
||||
else if (config.Servers.Count == 0)
|
||||
throw new ServerException("A server configuration in IW4MAdminSettings.json is invalid");
|
||||
|
||||
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
|
||||
Utilities.EncodingType = Encoding.GetEncoding(config.CustomParserEncoding ?? "windows-1252");
|
||||
|
||||
#endregion
|
||||
#region PLUGINS
|
||||
SharedLibraryCore.Plugins.PluginImporter.Load(this);
|
||||
|
@ -32,7 +32,7 @@ namespace Application.RconParsers
|
||||
{
|
||||
string[] LineSplit = await connection.SendQueryAsync(StaticHelpers.QueryType.DVAR, dvarName);
|
||||
|
||||
if (LineSplit.Length != 3)
|
||||
if (LineSplit.Length < 3)
|
||||
{
|
||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
||||
e.Data["dvar_name"] = dvarName;
|
||||
@ -41,7 +41,7 @@ namespace Application.RconParsers
|
||||
|
||||
string[] ValueSplit = LineSplit[1].Split(new char[] { '"' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (ValueSplit.Length != 5)
|
||||
if (ValueSplit.Length < 5)
|
||||
{
|
||||
var e = new DvarException($"DVAR \"{dvarName}\" does not exist");
|
||||
e.Data["dvar_name"] = dvarName;
|
||||
@ -75,17 +75,20 @@ namespace Application.RconParsers
|
||||
{
|
||||
List<Player> StatusPlayers = new List<Player>();
|
||||
|
||||
if (Status.Length < 4)
|
||||
throw new ServerException("Unexpected status response received");
|
||||
|
||||
foreach (String S in Status)
|
||||
{
|
||||
String responseLine = S.Trim();
|
||||
|
||||
if (Regex.Matches(responseLine, @"^\d+", RegexOptions.IgnoreCase).Count > 0)
|
||||
if (Regex.Matches(responseLine, @" *^\d+", RegexOptions.IgnoreCase).Count > 0)
|
||||
{
|
||||
String[] playerInfo = responseLine.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
int cID = -1;
|
||||
int Ping = -1;
|
||||
Int32.TryParse(playerInfo[2], out Ping);
|
||||
String cName = Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(responseLine.Substring(46, 18).StripColors().Trim())));
|
||||
String cName = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(responseLine.Substring(46, 18).StripColors().Trim())));
|
||||
long npID = Regex.Match(responseLine, @"([a-z]|[0-9]){16}", RegexOptions.IgnoreCase).Value.ConvertLong();
|
||||
int.TryParse(playerInfo[0], out cID);
|
||||
var regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
||||
@ -99,7 +102,8 @@ namespace Application.RconParsers
|
||||
ClientNumber = cID,
|
||||
IPAddress = cIP,
|
||||
Ping = Ping,
|
||||
Score = score
|
||||
Score = score,
|
||||
IsBot = npID == -1
|
||||
};
|
||||
StatusPlayers.Add(P);
|
||||
}
|
||||
|
@ -166,7 +166,7 @@ namespace Application.RconParsers
|
||||
|
||||
Int32.TryParse(playerInfo[3], out Ping);
|
||||
var regex = Regex.Match(responseLine, @"\^7.*\ +0 ");
|
||||
string name = Encoding.UTF8.GetString(Encoding.Convert(Encoding.UTF7, Encoding.UTF8, Encoding.UTF7.GetBytes(regex.Value.Substring(0, regex.Value.Length - 2).StripColors().Trim())));
|
||||
string name = Encoding.UTF8.GetString(Encoding.Convert(Utilities.EncodingType, Encoding.UTF8, Utilities.EncodingType.GetBytes(regex.Value.Substring(0, regex.Value.Length - 2).StripColors().Trim())));
|
||||
long networkId = playerInfo[4].ConvertLong();
|
||||
int.TryParse(playerInfo[0], out clientId);
|
||||
regex = Regex.Match(responseLine, @"\d+\.\d+\.\d+.\d+\:\d{1,5}");
|
||||
@ -175,7 +175,9 @@ namespace Application.RconParsers
|
||||
#endif
|
||||
int ipAddress = regex.Value.Split(':')[0].ConvertToIP();
|
||||
regex = Regex.Match(responseLine, @"[0-9]{1,2}\s+[0-9]+\s+");
|
||||
int score = Int32.Parse(playerInfo[1]);
|
||||
int score = 0;
|
||||
// todo: fix this when T6M score is valid ;)
|
||||
//int score = Int32.Parse(playerInfo[1]);
|
||||
|
||||
StatusPlayers.Add(new Player()
|
||||
{
|
||||
@ -184,7 +186,8 @@ namespace Application.RconParsers
|
||||
ClientNumber = clientId,
|
||||
IPAddress = ipAddress,
|
||||
Ping = Ping,
|
||||
Score = score
|
||||
Score = score,
|
||||
IsBot = networkId < 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -151,6 +151,7 @@ namespace IW4MAdmin
|
||||
|
||||
// Do the player specific stuff
|
||||
player.ClientNumber = polledPlayer.ClientNumber;
|
||||
player.IsBot = polledPlayer.IsBot;
|
||||
player.Score = polledPlayer.Score;
|
||||
player.CurrentServer = this;
|
||||
Players[player.ClientNumber] = player;
|
||||
@ -648,7 +649,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 = game.Value == string.Empty ?
|
||||
$"{basepath.Value.Replace('\\', Path.DirectorySeparatorChar)}{Path.DirectorySeparatorChar}{mainPath}{Path.DirectorySeparatorChar}{logfile.Value}" :
|
||||
|
@ -36,6 +36,9 @@
|
||||
<Compile Include="master\context\base.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="master\context\history.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="master\context\__init__.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@ -51,6 +54,9 @@
|
||||
<Compile Include="Master\resources\authenticate.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="master\resources\history_graph.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Master\resources\instance.py">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
@ -88,37 +94,12 @@
|
||||
<Folder Include="master\schema\" />
|
||||
<Folder Include="Master\resources\" />
|
||||
<Folder Include="Master\static\" />
|
||||
<Folder Include="Master\static\content\" />
|
||||
<Folder Include="Master\static\fonts\" />
|
||||
<Folder Include="Master\static\scripts\" />
|
||||
<Folder Include="Master\templates\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="FolderProfile.pubxml" />
|
||||
<Content Include="master\config\master.json" />
|
||||
<Content Include="requirements.txt" />
|
||||
<Content Include="Master\static\content\bootstrap.css" />
|
||||
<Content Include="Master\static\content\bootstrap.min.css" />
|
||||
<Content Include="Master\static\content\site.css" />
|
||||
<Content Include="Master\static\fonts\glyphicons-halflings-regular.eot" />
|
||||
<Content Include="Master\static\fonts\glyphicons-halflings-regular.svg" />
|
||||
<Content Include="Master\static\fonts\glyphicons-halflings-regular.ttf" />
|
||||
<Content Include="Master\static\fonts\glyphicons-halflings-regular.woff" />
|
||||
<Content Include="Master\static\scripts\bootstrap.js" />
|
||||
<Content Include="Master\static\scripts\bootstrap.min.js" />
|
||||
<Content Include="Master\static\scripts\jquery-1.10.2.intellisense.js" />
|
||||
<Content Include="Master\static\scripts\jquery-1.10.2.js" />
|
||||
<Content Include="Master\static\scripts\jquery-1.10.2.min.js" />
|
||||
<Content Include="Master\static\scripts\jquery-1.10.2.min.map" />
|
||||
<Content Include="Master\static\scripts\jquery.validate-vsdoc.js" />
|
||||
<Content Include="Master\static\scripts\jquery.validate.js" />
|
||||
<Content Include="Master\static\scripts\jquery.validate.min.js" />
|
||||
<Content Include="Master\static\scripts\jquery.validate.unobtrusive.js" />
|
||||
<Content Include="Master\static\scripts\jquery.validate.unobtrusive.min.js" />
|
||||
<Content Include="Master\static\scripts\modernizr-2.6.2.js" />
|
||||
<Content Include="Master\static\scripts\respond.js" />
|
||||
<Content Include="Master\static\scripts\respond.min.js" />
|
||||
<Content Include="Master\static\scripts\_references.js" />
|
||||
<Content Include="Master\templates\index.html" />
|
||||
<Content Include="Master\templates\layout.html" />
|
||||
</ItemGroup>
|
||||
|
@ -1,11 +1,15 @@
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
from master.context.history import History
|
||||
|
||||
from master.schema.instanceschema import InstanceSchema
|
||||
|
||||
import time
|
||||
|
||||
class Base():
|
||||
def __init__(self):
|
||||
self.history = History()
|
||||
self.instance_list = {}
|
||||
self.server_list = {}
|
||||
self.token_list = {}
|
||||
self.scheduler = BackgroundScheduler()
|
||||
self.scheduler.start()
|
||||
@ -16,6 +20,23 @@ class Base():
|
||||
name='Remove stale instances if no heartbeat in 120 seconds',
|
||||
replace_existing=True
|
||||
)
|
||||
self.scheduler.add_job(
|
||||
func=self._update_history_count,
|
||||
trigger=IntervalTrigger(seconds=30),
|
||||
id='update history',
|
||||
name='update client and instance count every 30 seconds',
|
||||
replace_existing=True
|
||||
)
|
||||
|
||||
def _update_history_count(self):
|
||||
servers = [instance.servers for instance in self.instance_list.values()]
|
||||
servers = [inner for outer in servers for inner in outer]
|
||||
client_num = 0
|
||||
# force it being a number
|
||||
for server in servers:
|
||||
client_num += server.clientnum
|
||||
self.history.add_client_history(client_num)
|
||||
self.history.add_instance_history(len(self.instance_list))
|
||||
|
||||
def _remove_staleinstances(self):
|
||||
for key, value in list(self.instance_list.items()):
|
||||
@ -28,9 +49,6 @@ class Base():
|
||||
def get_instances(self):
|
||||
return self.instance_list.values()
|
||||
|
||||
def get_server_count(self):
|
||||
return self.server_list.count
|
||||
|
||||
def get_instance_count(self):
|
||||
return self.instance_list.count
|
||||
|
||||
|
23
Master/master/context/history.py
Normal file
23
Master/master/context/history.py
Normal file
@ -0,0 +1,23 @@
|
||||
import time
|
||||
from random import randint
|
||||
|
||||
class History():
|
||||
def __init__(self):
|
||||
self.client_history = list()
|
||||
self.instance_history = list()
|
||||
|
||||
def add_client_history(self, client_num):
|
||||
if len(self.client_history) > 1440:
|
||||
self.client_history = self.client_history[1:]
|
||||
self.client_history.append({
|
||||
'count' : client_num,
|
||||
'time' : int(time.time())
|
||||
})
|
||||
|
||||
def add_instance_history(self, instance_num):
|
||||
if len(self.instance_history) > 1440:
|
||||
self.instance_history = self.instance_history[1:]
|
||||
self.instance_history.append({
|
||||
'count' : instance_num,
|
||||
'time' : int(time.time())
|
||||
})
|
46
Master/master/resources/history_graph.py
Normal file
46
Master/master/resources/history_graph.py
Normal file
@ -0,0 +1,46 @@
|
||||
from flask_restful import Resource
|
||||
from pygal.style import Style
|
||||
from master import ctx
|
||||
import pygal
|
||||
import timeago
|
||||
from math import ceil
|
||||
|
||||
class HistoryGraph(Resource):
|
||||
def get(self, history_count):
|
||||
try:
|
||||
custom_style = Style(
|
||||
background='transparent',
|
||||
plot_background='transparent',
|
||||
foreground='rgba(109, 118, 126, 0.3)',
|
||||
foreground_strong='rgba(109, 118, 126, 0.3)',
|
||||
foreground_subtle='rgba(109, 118, 126, 0.3)',
|
||||
opacity='0.1',
|
||||
opacity_hover='0.2',
|
||||
transition='100ms ease-in',
|
||||
colors=('#007acc', '#749363')
|
||||
)
|
||||
|
||||
graph = pygal.StackedLine(
|
||||
interpolate='cubic',
|
||||
interpolation_precision=3,
|
||||
#x_labels_major_every=100,
|
||||
#x_labels_major_count=500,
|
||||
stroke_style={'width': 0.4},
|
||||
show_dots=False,
|
||||
show_legend=False,
|
||||
fill=True,
|
||||
style=custom_style,
|
||||
disable_xml_declaration=True)
|
||||
|
||||
instance_count = [history['time'] for history in ctx.history.instance_history][-history_count:]
|
||||
|
||||
if len(instance_count) > 0:
|
||||
graph.x_labels = [ timeago.format(instance_count[0])]
|
||||
|
||||
graph.add('Instance Count', [history['count'] for history in ctx.history.instance_history][-history_count:])
|
||||
graph.add('Client Count', [history['count'] for history in ctx.history.client_history][-history_count:])
|
||||
return { 'message' : graph.render(),
|
||||
'data_points' : len(instance_count)
|
||||
}, 200
|
||||
except Exception as e:
|
||||
return { 'message' : str(e) }, 500
|
@ -4,8 +4,10 @@ from master.resources.null import Null
|
||||
from master.resources.instance import Instance
|
||||
from master.resources.authenticate import Authenticate
|
||||
from master.resources.version import Version
|
||||
from master.resources.history_graph import HistoryGraph
|
||||
|
||||
api.add_resource(Null, '/null')
|
||||
api.add_resource(Instance, '/instance/', '/instance/<string:id>')
|
||||
api.add_resource(Version, '/version')
|
||||
api.add_resource(Authenticate, '/authenticate')
|
||||
api.add_resource(HistoryGraph, '/history/', '/history/<int:history_count>')
|
@ -1,5 +1,46 @@
|
||||
{% extends "layout.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<figure>
|
||||
<div id="history_graph">{{history_graph|safe}}</div>
|
||||
<figcaption class="float-right pr-3 mr-4">
|
||||
<span id="history_graph_zoom_out" class="h4 oi oi-zoom-out text-muted" style="cursor:pointer;"></span>
|
||||
<span id="history_graph_zoom_in" class="h4 oi oi-zoom-in text-muted" style="cursor:pointer;"></span>
|
||||
</figcaption>
|
||||
</figure>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script type="text/javascript" src="http://kozea.github.com/pygal.js/latest/pygal-tooltips.min.js"></script>
|
||||
<script>
|
||||
let dataPoints = {{data_points}};
|
||||
let zoomLevel = Math.ceil(dataPoints / 2);
|
||||
//console.log(dataPoints);
|
||||
|
||||
function updateHistoryGraph() {
|
||||
$.get('/history/' + zoomLevel)
|
||||
.done(function (content) {
|
||||
$('#history_graph').html(content.message);
|
||||
});
|
||||
}
|
||||
setInterval(updateHistoryGraph, 30000);
|
||||
|
||||
$('#history_graph_zoom_out').click(function () {
|
||||
// console.log(zoomLevel);
|
||||
zoomLevel = zoomLevel * 2 < dataPoints ? Math.ceil(zoomLevel * 2) : dataPoints;
|
||||
updateHistoryGraph();
|
||||
});
|
||||
|
||||
$('#history_graph_zoom_in').click(function () {
|
||||
// console.log(zoomLevel);
|
||||
zoomLevel = zoomLevel / 2 > 2 ? Math.ceil(zoomLevel / 2) : 2;
|
||||
updateHistoryGraph();
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -1,44 +1,32 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html class="bg-dark">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>IW4MAdmin Master | {{ title }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/content/bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/content/site.css" />
|
||||
<script src="/static/scripts/modernizr-2.6.2.js"></script>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css" integrity="sha256-BJ/G+e+y7bQdrYkS2RBTyNfBHpA9IuGaPmf9htub5MQ=" crossorigin="anonymous" />
|
||||
<style type="text/css">
|
||||
.active {
|
||||
stroke-width: initial !important;
|
||||
}
|
||||
|
||||
.oi:hover {
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a href="/" class="navbar-brand">Application name</a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="{{ url_for('home') }}">Home</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<body class="bg-dark">
|
||||
|
||||
<div class="container body-content">
|
||||
<div class="container body-content bg-dark">
|
||||
{% block content %}{% endblock %}
|
||||
<hr />
|
||||
<footer>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="/static/scripts/jquery-1.10.2.js"></script>
|
||||
<script src="/static/scripts/bootstrap.js"></script>
|
||||
<script src="/static/scripts/respond.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
|
||||
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
|
||||
crossorigin="anonymous"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -5,13 +5,14 @@ Routes and views for the flask application.
|
||||
from datetime import datetime
|
||||
from flask import render_template
|
||||
from master import app
|
||||
from master.resources.history_graph import HistoryGraph
|
||||
|
||||
@app.route('/')
|
||||
@app.route('/home')
|
||||
def home():
|
||||
"""Renders the home page."""
|
||||
_history_graph = HistoryGraph().get(500)
|
||||
return render_template(
|
||||
'index.html',
|
||||
title='Home Page',
|
||||
year=datetime.now().year,
|
||||
title='API Overview',
|
||||
history_graph = _history_graph[0]['message'],
|
||||
data_points = _history_graph[0]['data_points']
|
||||
)
|
@ -1,4 +1,5 @@
|
||||
|
||||
|
||||
# IW4MAdmin
|
||||
### Quick Start Guide
|
||||
### Version 2.0
|
||||
@ -40,6 +41,10 @@ When **IW4MAdmin** is launched for the _first time_, you will be prompted to set
|
||||
* Shows a link to your server's discord on the webfront
|
||||
* _This feature requires an invite link to your discord server_
|
||||
|
||||
`Use Custom Encoding Parser`
|
||||
* Allows alternative encodings to be used for parsing game information and events
|
||||
* **Russian users should use this and then specify** `windows-1251` **as the encoding string**
|
||||
|
||||
#### Advanced Configuration
|
||||
If you wish to further customize your experience of **IW4MAdmin**, the following configuration file(s) will allow you to changes core options using any text-editor.
|
||||
|
||||
|
@ -214,11 +214,11 @@ namespace SharedLibraryCore.Commands
|
||||
public class CUnban : Command
|
||||
{
|
||||
public CUnban() :
|
||||
base("unban", "unban player by database id", "ub", Player.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
base("unban", "unban player by client id", "ub", Player.Permission.SeniorAdmin, true, new CommandArgument[]
|
||||
{
|
||||
new CommandArgument()
|
||||
{
|
||||
Name = "databaseID",
|
||||
Name = "client id",
|
||||
Required = true,
|
||||
},
|
||||
new CommandArgument()
|
||||
@ -576,6 +576,12 @@ namespace SharedLibraryCore.Commands
|
||||
|
||||
public override async Task ExecuteAsync(GameEvent E)
|
||||
{
|
||||
if (E.Data.Length < 3)
|
||||
{
|
||||
await E.Origin.Tell("Please enter at least 3 characters");
|
||||
return;
|
||||
}
|
||||
|
||||
IList<EFClient> db_players = (await (E.Owner.Manager.GetClientService() as ClientService)
|
||||
.GetClientByName(E.Data))
|
||||
.OrderByDescending(p => p.LastConnection)
|
||||
|
@ -17,6 +17,7 @@ namespace SharedLibraryCore.Configuration
|
||||
public string DiscordInviteCode { get; set; }
|
||||
public string IPHubAPIKey { get; set; }
|
||||
public string WebfrontBindUrl { get; set; }
|
||||
public string CustomParserEncoding { get; set; }
|
||||
public string Id { get; set; }
|
||||
public List<ServerConfiguration> Servers { get; set; }
|
||||
public int AutoMessagePeriod { get; set; }
|
||||
@ -32,6 +33,10 @@ namespace SharedLibraryCore.Configuration
|
||||
EnableSteppedHierarchy = Utilities.PromptBool("Enable stepped privilege hierarchy");
|
||||
EnableCustomSayName = Utilities.PromptBool("Enable custom say name");
|
||||
|
||||
bool useCustomParserEncoding = Utilities.PromptBool("Use custom encoding parser");
|
||||
CustomParserEncoding = useCustomParserEncoding ? Utilities.PromptString("Enter encoding string") : "windows-1252";
|
||||
|
||||
|
||||
WebfrontBindUrl = "http://127.0.0.1:1624";
|
||||
|
||||
if (EnableCustomSayName)
|
||||
|
@ -39,7 +39,7 @@ namespace SharedLibraryCore
|
||||
if (fileName != string.Empty)
|
||||
{
|
||||
Name = fileName;
|
||||
Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Encoding.UTF8);
|
||||
Handle = new StreamReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true), Utilities.EncodingType);
|
||||
|
||||
sze = Handle.BaseStream.Length;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ namespace SharedLibraryCore.RCon
|
||||
#if DEBUG
|
||||
Log.WriteDebug($"Received {bytesRead} bytes from {ServerConnection.RemoteEndPoint}");
|
||||
#endif
|
||||
connectionState.ResponseString.Append(Encoding.UTF7.GetString(connectionState.Buffer, 0, bytesRead).TrimEnd('\0') + '\n');
|
||||
connectionState.ResponseString.Append(Utilities.EncodingType.GetString(connectionState.Buffer, 0, bytesRead).TrimEnd('\0') + '\n');
|
||||
|
||||
if (!connectionState.Buffer.Take(4).ToArray().SequenceEqual(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }))
|
||||
throw new NetworkException("Unexpected packet received");
|
||||
|
@ -226,6 +226,9 @@ namespace SharedLibraryCore.Services
|
||||
|
||||
public async Task<IList<EFClient>> GetClientByName(string name)
|
||||
{
|
||||
if (name.Length < 3)
|
||||
return new List<EFClient>();
|
||||
|
||||
using (var context = new DatabaseContext())
|
||||
{
|
||||
var iqClients = (from alias in context.Aliases
|
||||
|
@ -20,6 +20,7 @@ namespace SharedLibraryCore
|
||||
{
|
||||
public static string OperatingDirectory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
|
||||
public static readonly Task CompletedTask = Task.FromResult(false);
|
||||
public static Encoding EncodingType;
|
||||
|
||||
//Get string with specified number of spaces -- really only for visual output
|
||||
public static String GetSpaces(int Num)
|
||||
@ -197,15 +198,9 @@ namespace SharedLibraryCore
|
||||
|
||||
public static int ConvertToIP(this string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
return BitConverter.ToInt32(System.Net.IPAddress.Parse(str).GetAddressBytes(), 0);
|
||||
}
|
||||
System.Net.IPAddress.TryParse(str, out System.Net.IPAddress ip);
|
||||
|
||||
catch (FormatException)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ip == null ? 0 : BitConverter.ToInt32(ip.GetAddressBytes(), 0);
|
||||
}
|
||||
|
||||
public static string ConvertIPtoString(this int ip)
|
||||
|
Loading…
Reference in New Issue
Block a user