hopefully finished with RCon changes.

added more tests.
fixed issues from event changes (there's most definitely still issues related to that)
This commit is contained in:
RaidMax
2018-10-02 12:39:08 -05:00
parent 7fa0b52543
commit f4ac815d07
15 changed files with 428 additions and 219 deletions

View File

@ -124,7 +124,7 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
var _ = await E.Target.Kick(E.Data, E.Origin).WaitAsync() ?
var _ = !(await E.Target.Kick(E.Data, E.Origin).WaitAsync()).Failed ?
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}") :
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
}
@ -173,16 +173,20 @@ namespace SharedLibraryCore.Commands
})
{ }
private static readonly string TempBanRegex = @"([0-9]+\w+)\ (.+)";
public override async Task ExecuteAsync(GameEvent E)
{
String Message = Utilities.RemoveWords(E.Data, 1).Trim();
var length = E.Data.Split(' ')[0].ToLower().ParseTimespan();
if (length.TotalHours >= 1 && length.TotalHours < 2)
Message = E.Data.Replace("1h", "").Replace("1H", "");
var match = Regex.Match(E.Data, TempBanRegex);
if (match.Success)
{
string tempbanReason = match.Groups[2].ToString();
var length = match.Groups[1].ToString().ParseTimespan();
var _ = await E.Target.TempBan(Message, length, E.Origin).WaitAsync() ?
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") :
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
var _ = !(await E.Target.TempBan(tempbanReason, length, E.Origin).WaitAsync()).Failed ?
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}") :
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
}
}
}
@ -206,7 +210,7 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
var _ = await E.Target.Ban(E.Data, E.Origin).WaitAsync() ?
var _ = !(await E.Target.Ban(E.Data, E.Origin).WaitAsync()).Failed ?
E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"]}") :
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"]} {E.Target.Name}");
}
@ -721,11 +725,10 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
var flagEvent = E.Target.Flag(E.Data, E.Origin);
if (E.FailReason == GameEvent.EventFailReason.Permission)
{
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}");
@ -738,25 +741,11 @@ namespace SharedLibraryCore.Commands
else
{
E.Target.Level = Player.Permission.Flagged;
Penalty newPenalty = new Penalty()
{
Type = Penalty.PenaltyType.Flag,
Expires = DateTime.UtcNow,
Offender = E.Target,
Offense = E.Data,
Punisher = E.Origin,
Active = true,
When = DateTime.UtcNow,
Link = E.Target.AliasLink
};
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
}
return Task.CompletedTask;
}
}
@ -843,6 +832,7 @@ namespace SharedLibraryCore.Commands
else
{
// todo: move into server
Penalty newReport = new Penalty()
{
Type = Penalty.PenaltyType.Report,

View File

@ -24,11 +24,9 @@ namespace SharedLibraryCore.Database
public DbSet<EFMeta> EFMeta { get; set; }
public DbSet<EFChangeHistory> EFChangeHistory { get; set; }
/// <summary>
/// this only works if there's one connection string
/// </summary>
private static string _ConnectionString;
private static string _provider;
static string _ConnectionString;
static string _provider;
public DatabaseContext(DbContextOptions<DatabaseContext> opt) : base(opt) { }
@ -63,12 +61,12 @@ namespace SharedLibraryCore.Database
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = $"{currentPath}{Path.DirectorySeparatorChar}Database.db".Substring(6) };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
#if DEBUG == true
optionsBuilder.UseMySql("UserId=root;Password=dev;Host=127.0.0.1;port=3306;Database=IW4MAdmin");
// optionsBuilder.UseNpgsql("UserId=dev;Password=dev;Host=127.0.0.1;port=5432;Database=IW4MAdmin");
#else
//#if DEBUG == true
//optionsBuilder.UseMySql("UserId=root;Password=dev;Host=127.0.0.1;port=3306;Database=IW4MAdmin");
// optionsBuilder.UseNpgsql("UserId=dev;Password=dev;Host=127.0.0.1;port=5432;Database=IW4MAdmin");
//#else
optionsBuilder.UseSqlite(connection);
#endif
//#endif
}
else

View File

@ -181,10 +181,10 @@ namespace SharedLibraryCore
/// asynchronously wait for GameEvent to be processed
/// </summary>
/// <returns>waitable task </returns>
public Task<bool> WaitAsync(int timeOut = int.MaxValue) => Task.Run(() =>
public Task<GameEvent> WaitAsync(int timeOut = int.MaxValue) => Task.Run(() =>
{
OnProcessed.Wait(timeOut);
return !Failed;
return this;
});
/// <summary>
@ -214,7 +214,7 @@ namespace SharedLibraryCore
/// <returns>true if event should be delayed, false otherwise</returns>
public static bool ShouldTargetEventBeDelayed(GameEvent queuedEvent)
{
return queuedEvent.Target != null &&
return (queuedEvent.Target != null && queuedEvent.Target.ClientNumber != -1) &&
(queuedEvent.Target.State != Player.ClientState.Connected &&
queuedEvent.Target.NetworkId != 0);
}

View File

@ -101,7 +101,7 @@ namespace SharedLibraryCore.Objects
Data = message
};
CurrentServer.Manager.GetEventHandler().AddEvent(e);
this.CurrentServer?.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -118,7 +118,7 @@ namespace SharedLibraryCore.Objects
Message = warnReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
// enforce level restrictions
@ -130,7 +130,36 @@ namespace SharedLibraryCore.Objects
this.Warnings++;
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// clear all warnings for a client
/// </summary>
/// <param name="sender">client performing the warn clear</param>
/// <returns></returns>
public GameEvent WarnClear(Player sender)
{
var e = new GameEvent()
{
Type = GameEvent.EventType.WarnClear,
Origin = sender,
Target = this,
Owner = sender.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
this.Warnings = 0;
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -149,58 +178,26 @@ namespace SharedLibraryCore.Objects
Data = reportReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
if (this.Level > sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this == sender)
else if (this.Equals(sender))
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
if (CurrentServer.Reports.Count(rep => (rep.Origin.NetworkId == sender.NetworkId &&
rep.Target.NetworkId == this.NetworkId)) > 0)
else if (CurrentServer.Reports.Count(report => (report.Origin.NetworkId == sender.NetworkId &&
report.Target.NetworkId == this.NetworkId)) > 0)
{
e.FailReason = GameEvent.EventFailReason.Exception;
return e;
}
CurrentServer.Reports.Add(new Report(this, sender, reportReason));
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// clear all warnings for a client
/// </summary>
/// <param name="sender">client performing the warn clear</param>
/// <returns></returns>
public GameEvent WarnClear(Player sender)
{
var e = new GameEvent()
{
Type = GameEvent.EventType.WarnClear,
Origin = sender,
Target = this,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
this.Warnings = 0;
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -218,22 +215,26 @@ namespace SharedLibraryCore.Objects
Origin = sender,
Data = flagReason,
Message = flagReason,
Owner = this.CurrentServer
Target = this,
Owner = sender.CurrentServer
};
if (sender.Level <= this.Level)
if (this.Level >= sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this.Level == Player.Permission.Flagged)
else if (this.Level == Player.Permission.Flagged)
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
else
{
this.Level = Player.Permission.Flagged;
}
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -249,26 +250,28 @@ namespace SharedLibraryCore.Objects
{
Type = GameEvent.EventType.Unflag,
Origin = sender,
Target = this,
Data = unflagReason,
Message = unflagReason,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this.Level != Player.Permission.Flagged)
else if (this.Level != Player.Permission.Flagged)
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
this.Level = Permission.User;
else
{
this.Level = Permission.User;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -286,17 +289,16 @@ namespace SharedLibraryCore.Objects
Target = this,
Origin = sender,
Data = kickReason,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
if (this.Level > sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -312,10 +314,11 @@ namespace SharedLibraryCore.Objects
{
Type = GameEvent.EventType.TempBan,
Message = tempbanReason,
Data = tempbanReason,
Origin = sender,
Target = this,
Extra = banLength,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
// enforce level restrictions
@ -325,7 +328,7 @@ namespace SharedLibraryCore.Objects
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -340,9 +343,10 @@ namespace SharedLibraryCore.Objects
{
Type = GameEvent.EventType.Ban,
Message = banReason,
Data = banReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
// enforce level restrictions
@ -352,7 +356,7 @@ namespace SharedLibraryCore.Objects
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -371,17 +375,16 @@ namespace SharedLibraryCore.Objects
Data = unbanReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
Owner = sender.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
if (this.Level > sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
sender.CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
@ -432,7 +435,7 @@ namespace SharedLibraryCore.Objects
public override bool Equals(object obj)
{
return ((Player)obj).NetworkId == NetworkId;
return ((Player)obj).NetworkId == this.NetworkId;
}
public override int GetHashCode() => (int)NetworkId;

View File

@ -8,15 +8,8 @@ namespace SharedLibraryCore.Objects
{
public class Report
{
public Report(Player T, Player O, String R)
{
Target = T;
Origin = O;
Reason = R;
}
public Player Target { get; private set; }
public Player Origin { get; private set; }
public String Reason { get; private set; }
public Player Target { get; set; }
public Player Origin { get; set; }
public String Reason { get; set; }
}
}

View File

@ -17,6 +17,10 @@ namespace SharedLibraryCore.RCon
const int BufferSize = 4096;
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1);
public readonly ManualResetEventSlim OnSentData = new ManualResetEventSlim(false);
public readonly ManualResetEventSlim OnReceivedData = new ManualResetEventSlim(false);
public SocketAsyncEventArgs SendEventArgs { get; set; } = new SocketAsyncEventArgs();
public SocketAsyncEventArgs ReceiveEventArgs { get; set; } = new SocketAsyncEventArgs();
public DateTime LastQuery { get; set; } = DateTime.Now;
}
@ -43,23 +47,24 @@ namespace SharedLibraryCore.RCon
var connectionState = ActiveQueries[this.Endpoint];
var timeLeft = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
#if DEBUG == true
Log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]");
#endif
// enter the semaphore so only one query is sent at a time per server.
await connectionState.OnComplete.WaitAsync();
if (timeLeft > 0)
var timeSinceLastQuery = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
if (timeSinceLastQuery < StaticHelpers.FloodProtectionInterval)
{
await Task.Delay((int)timeLeft);
await Task.Delay(StaticHelpers.FloodProtectionInterval - (int)timeSinceLastQuery);
}
connectionState.LastQuery = DateTime.Now;
#if DEBUG == true
Log.WriteDebug($"Waiting for semaphore to be released [${this.Endpoint}]");
#endif
// enter the semaphore so only one query is sent at a time per server.
await connectionState.OnComplete.WaitAsync();
#if DEBUG == true
Log.WriteDebug($"Semaphore has been released [${this.Endpoint}]");
Log.WriteDebug($"Semaphore has been released [{this.Endpoint}]");
Log.WriteDebug($"Query [{this.Endpoint},{type.ToString()},{parameters}]");
#endif
byte[] payload = null;
@ -82,33 +87,41 @@ namespace SharedLibraryCore.RCon
}
byte[] response = null;
retrySend:
connectionState.SendEventArgs.UserToken = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
DontFragment = true,
Ttl = 42,
ExclusiveAddressUse = true,
};
connectionState.OnSentData.Reset();
connectionState.OnReceivedData.Reset();
connectionState.ConnectionAttempts++;
#if DEBUG == true
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})");
Log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
#endif
try
{
response = await SendPayloadAsync(payload);
connectionState.OnComplete.Release(1);
connectionState.ConnectionAttempts = 0;
}
catch (Exception ex)
{
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
{
connectionState.ConnectionAttempts++;
Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})");
Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
await Task.Delay(StaticHelpers.FloodProtectionInterval);
goto retrySend;
}
// the next thread can go ahead and enter
connectionState.OnComplete.Release(1);
connectionState.OnComplete.Release(1);
Log.WriteDebug(ex.GetExceptionInfo());
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
}
connectionState.ConnectionAttempts = 0;
string responseString = Utilities.EncodingType.GetString(response, 0, response.Length).TrimEnd('\0') + '\n';
if (responseString.Contains("Invalid password"))
@ -128,45 +141,54 @@ namespace SharedLibraryCore.RCon
private async Task<byte[]> SendPayloadAsync(byte[] payload)
{
var rconSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
DontFragment = true,
Ttl = 42,
ExclusiveAddressUse = true
};
var connectionState = ActiveQueries[this.Endpoint];
var rconSocket = (Socket)connectionState.SendEventArgs.UserToken;
var outgoingDataArgs = new SocketAsyncEventArgs()
if (connectionState.ReceiveEventArgs.RemoteEndPoint == null &&
connectionState.SendEventArgs.RemoteEndPoint == null)
{
RemoteEndPoint = this.Endpoint
};
outgoingDataArgs.SetBuffer(payload);
outgoingDataArgs.Completed += OnDataSent;
// send the data to the server
bool sendDataPending = rconSocket.SendToAsync(outgoingDataArgs);
var incomingDataArgs = new SocketAsyncEventArgs()
{
RemoteEndPoint = this.Endpoint
};
incomingDataArgs.SetBuffer(connectionState.ReceiveBuffer);
incomingDataArgs.Completed += OnDataReceived;
// get our response back
rconSocket.ReceiveFromAsync(incomingDataArgs);
if (!await connectionState.OnComplete.WaitAsync(StaticHelpers.SocketTimeout.Milliseconds))
{
// we no longer care about the data because the server is being too slow
incomingDataArgs.Completed -= OnDataReceived;
// the next thread can go ahead and make a query
connectionState.OnComplete.Release(1);
throw new NetworkException("Timed out waiting for response", rconSocket);
// setup the event handlers only once because we're reusing the event args
connectionState.SendEventArgs.Completed += OnDataSent;
connectionState.ReceiveEventArgs.Completed += OnDataReceived;
connectionState.SendEventArgs.RemoteEndPoint = this.Endpoint;
connectionState.ReceiveEventArgs.RemoteEndPoint = this.Endpoint;
connectionState.ReceiveEventArgs.DisconnectReuseSocket = true;
connectionState.SendEventArgs.DisconnectReuseSocket = true;
}
byte[] response = connectionState.ReceiveBuffer;
connectionState.SendEventArgs.SetBuffer(payload);
// send the data to the server
bool sendDataPending = rconSocket.SendToAsync(connectionState.SendEventArgs);
if (sendDataPending)
{
// the send has not been completed asyncronously
if (!await Task.Run(() => connectionState.OnSentData.Wait(StaticHelpers.SocketTimeout)))
{
rconSocket.Close();
throw new NetworkException("Timed out sending data", rconSocket);
}
}
connectionState.ReceiveEventArgs.SetBuffer(connectionState.ReceiveBuffer);
// get our response back
bool receiveDataPending = rconSocket.ReceiveFromAsync(connectionState.ReceiveEventArgs);
if (receiveDataPending)
{
if (!await Task.Run(() => connectionState.OnReceivedData.Wait(StaticHelpers.SocketTimeout)))
{
rconSocket.Close();
throw new NetworkException("Timed out waiting for response", rconSocket);
}
}
byte[] response = connectionState.ReceiveBuffer
.Take(connectionState.ReceiveEventArgs.BytesTransferred)
.ToArray();
return response;
}
@ -175,10 +197,7 @@ namespace SharedLibraryCore.RCon
#if DEBUG == true
Log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}");
#endif
if (ActiveQueries[this.Endpoint].OnComplete.CurrentCount == 0)
{
ActiveQueries[this.Endpoint].OnComplete.Release(1);
}
ActiveQueries[this.Endpoint].OnReceivedData.Set();
}
private void OnDataSent(object sender, SocketAsyncEventArgs e)
@ -186,6 +205,7 @@ namespace SharedLibraryCore.RCon
#if DEBUG == true
Log.WriteDebug($"Sent {e.Buffer.Length} bytes to {e.ConnectSocket.RemoteEndPoint.ToString()}");
#endif
ActiveQueries[this.Endpoint].OnSentData.Set();
}
}
}

View File

@ -39,11 +39,11 @@ namespace SharedLibraryCore.RCon
/// <summary>
/// timeout in seconds to wait for a socket send or receive before giving up
/// </summary>
public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 0, 0,150);
public static readonly int SocketTimeout = 1000;
/// <summary>
/// interval in milliseconds to wait before sending the next RCon request
/// </summary>
public static readonly int FloodProtectionInterval = 350;
public static readonly int FloodProtectionInterval = 635;
public static readonly int AllowedConnectionFails = 3;
}
}

View File

@ -192,14 +192,14 @@ namespace SharedLibraryCore
/// </summary>
/// <param name="Reason">Reason for kicking</param>
/// <param name="Target">Player to kick</param>
abstract public Task Kick(String Reason, Player Target, Player Origin);
abstract protected Task Kick(String Reason, Player Target, Player Origin);
/// <summary>
/// Temporarily ban a player ( default 1 hour ) from the server
/// </summary>
/// <param name="Reason">Reason for banning the player</param>
/// <param name="Target">The player to ban</param>
abstract public Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin);
abstract protected Task TempBan(String Reason, TimeSpan length, Player Target, Player Origin);
/// <summary>
/// Perm ban a player from the server
@ -207,7 +207,7 @@ namespace SharedLibraryCore
/// <param name="Reason">The reason for the ban</param>
/// <param name="Target">The person to ban</param>
/// <param name="Origin">The person who banned the target</param>
abstract public Task Ban(String Reason, Player Target, Player Origin);
abstract protected Task Ban(String Reason, Player Target, Player Origin);
abstract public Task Warn(String Reason, Player Target, Player Origin);

View File

@ -322,13 +322,13 @@ namespace SharedLibraryCore
public static TimeSpan ParseTimespan(this string input)
{
var expressionMatch = Regex.Match(input, @"[0-9]+.\b");
var expressionMatch = Regex.Match(input, @"([0-9]+)(\w+)");
if (!expressionMatch.Success) // fallback to default tempban length of 1 hour
return new TimeSpan(1, 0, 0);
char lengthDenote = expressionMatch.Value.ToLower()[expressionMatch.Value.Length - 1];
int length = Int32.Parse(expressionMatch.Value.Substring(0, expressionMatch.Value.Length - 1));
char lengthDenote = expressionMatch.Groups[2].ToString()[0];
int length = Int32.Parse(expressionMatch.Groups[1].ToString());
var loc = CurrentLocalization.LocalizationIndex;
@ -337,22 +337,22 @@ namespace SharedLibraryCore
return new TimeSpan(0, length, 0);
}
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_HOURS"].First()))
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_HOURS"][0]))
{
return new TimeSpan(length, 0, 0);
}
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_DAYS"].First()))
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_DAYS"][0]))
{
return new TimeSpan(length, 0, 0, 0);
}
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_WEEKS"].First()))
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_WEEKS"][0]))
{
return new TimeSpan(length * 7, 0, 0, 0);
}
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_YEARS"].First()))
if (lengthDenote == char.ToLower(loc["GLOBAL_TIME_YEARS"][0]))
{
return new TimeSpan(length * 365, 0, 0, 0);
}