fixed broken broadcast events
events don't get out of order when a invalid event line throws exception handle the stats history update with no change throwing DBConcurrencyException
This commit is contained in:
parent
46bdc2ac33
commit
cc7628d058
@ -18,29 +18,15 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
string[] lineSplit = logLine.Split(';');
|
||||
string eventType = lineSplit[0];
|
||||
|
||||
// kill
|
||||
if (eventType == "K")
|
||||
{
|
||||
if (!server.CustomCallback)
|
||||
{
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Kill,
|
||||
Data = logLine,
|
||||
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6)),
|
||||
Target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == "JoinTeam")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.JoinTeam,
|
||||
Data = eventType,
|
||||
Origin = server.GetPlayersAsList().FirstOrDefault(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Origin = origin,
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
@ -55,13 +41,15 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
.Replace("\x15", "")
|
||||
.Trim();
|
||||
|
||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
|
||||
if (message[0] == '!' || message[0] == '@')
|
||||
{
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Command,
|
||||
Data = message,
|
||||
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
|
||||
Origin = origin,
|
||||
Owner = server,
|
||||
Message = message
|
||||
};
|
||||
@ -71,33 +59,56 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
Type = GameEvent.EventType.Say,
|
||||
Data = message,
|
||||
Origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2)),
|
||||
Origin = origin,
|
||||
Owner = server,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (eventType == "K")
|
||||
{
|
||||
if (!server.CustomCallback)
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 6));
|
||||
var target = server.GetPlayersAsList().First(c => c.ClientNumber == Utilities.ClientIdFromString(lineSplit, 2));
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Kill,
|
||||
Data = logLine,
|
||||
Origin = origin,
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (eventType == "ScriptKill")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ScriptKill,
|
||||
Data = logLine,
|
||||
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()),
|
||||
Origin = origin,
|
||||
Target = target,
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
|
||||
if (eventType == "ScriptDamage")
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.ScriptDamage,
|
||||
Data = logLine,
|
||||
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[2].ConvertLong()),
|
||||
Origin = origin,
|
||||
Target = target,
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
@ -107,12 +118,15 @@ namespace IW4MAdmin.Application.EventParsers
|
||||
{
|
||||
if (Regex.Match(eventType, @"^(D);((?:bot[0-9]+)|(?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[A-Z]|[0-9])+);([0-9]+);(axis|allies);(.+);((?:[0-9]+|[a-z]+|_)+);([0-9]+);((?:[A-Z]|_)+);((?:[a-z]|_)+)$").Success)
|
||||
{
|
||||
var origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong());
|
||||
var target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong());
|
||||
|
||||
return new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Damage,
|
||||
Data = eventType,
|
||||
Origin = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[5].ConvertLong()),
|
||||
Target = server.GetPlayersAsList().First(c => c.NetworkId == lineSplit[1].ConvertLong()),
|
||||
Origin = origin,
|
||||
Target = target,
|
||||
Owner = server
|
||||
};
|
||||
}
|
||||
|
@ -15,14 +15,11 @@ namespace IW4MAdmin.Application
|
||||
private readonly IManager Manager;
|
||||
static long NextEventId = 1;
|
||||
private readonly SortedList<long, GameEvent> OutOfOrderEvents;
|
||||
private readonly SemaphoreSlim ProcessingEvent;
|
||||
|
||||
public GameEventHandler(IManager mgr)
|
||||
{
|
||||
Manager = mgr;
|
||||
OutOfOrderEvents = new SortedList<long, GameEvent>();
|
||||
ProcessingEvent = new SemaphoreSlim(0);
|
||||
ProcessingEvent.Release();
|
||||
}
|
||||
|
||||
public bool AddEvent(GameEvent gameEvent)
|
||||
@ -30,13 +27,23 @@ namespace IW4MAdmin.Application
|
||||
#if DEBUG
|
||||
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
|
||||
#endif
|
||||
while (OutOfOrderEvents.Values.FirstOrDefault()?.Id == Interlocked.Read(ref NextEventId))
|
||||
GameEvent sortedEvent = null;
|
||||
lock (OutOfOrderEvents)
|
||||
{
|
||||
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
|
||||
}
|
||||
|
||||
while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
|
||||
{
|
||||
lock (OutOfOrderEvents)
|
||||
{
|
||||
var fixedEvent = OutOfOrderEvents.Values[0];
|
||||
OutOfOrderEvents.RemoveAt(0);
|
||||
AddEvent(fixedEvent);
|
||||
}
|
||||
AddEvent(sortedEvent);
|
||||
|
||||
lock (OutOfOrderEvents)
|
||||
{
|
||||
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,9 +51,10 @@ namespace IW4MAdmin.Application
|
||||
// event occurs
|
||||
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
|
||||
{
|
||||
Manager.GetLogger().WriteDebug($"Starting to wait for event with id {gameEvent.Id}");
|
||||
#if DEBUG == true
|
||||
Manager.GetLogger().WriteDebug($"sent event with id {gameEvent.Id} to be processed");
|
||||
#endif
|
||||
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
|
||||
Manager.GetLogger().WriteDebug($"Finished waiting for event with id {gameEvent.Id}");
|
||||
Interlocked.Increment(ref NextEventId);
|
||||
}
|
||||
|
||||
@ -54,8 +62,10 @@ namespace IW4MAdmin.Application
|
||||
// so me must wait until the next expected one arrives
|
||||
else
|
||||
{
|
||||
#if DEBUG == true
|
||||
Manager.GetLogger().WriteWarning("Incoming event is out of order");
|
||||
Manager.GetLogger().WriteDebug($"Expected event Id is {Interlocked.Read(ref NextEventId)}, but got {gameEvent.Id} instead");
|
||||
#endif
|
||||
|
||||
// this prevents multiple threads from adding simultaneously
|
||||
lock (OutOfOrderEvents)
|
||||
|
@ -2,10 +2,7 @@
|
||||
using SharedLibraryCore.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
@ -76,7 +73,6 @@ namespace IW4MAdmin.Application.IO
|
||||
{
|
||||
try
|
||||
{
|
||||
// todo: catch elsewhere
|
||||
var e = Parser.GetEvent(server, eventLine);
|
||||
#if DEBUG == true
|
||||
server.Logger.WriteDebug($"Parsed event with id {e.Id} from http");
|
||||
|
@ -529,9 +529,9 @@ namespace IW4MAdmin
|
||||
if (E.Type == GameEvent.EventType.Broadcast)
|
||||
{
|
||||
// this is a little ugly but I don't want to change the abstract class
|
||||
if (E.Message != null)
|
||||
if (E.Data != null)
|
||||
{
|
||||
await E.Owner.ExecuteCommandAsync(E.Message);
|
||||
await E.Owner.ExecuteCommandAsync(E.Data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,11 @@ import datetime
|
||||
class Authenticate(Resource):
|
||||
def post(self):
|
||||
instance_id = request.json['id']
|
||||
if ctx.get_token(instance_id) is not False:
|
||||
return { 'message' : 'that id already has a token'}, 401
|
||||
else:
|
||||
expires = datetime.timedelta(days=30)
|
||||
token = create_access_token(instance_id, expires_delta=expires)
|
||||
ctx.add_token(instance_id, token)
|
||||
return { 'access_token' : token }, 200
|
||||
#todo: see why this is failing
|
||||
#if ctx.get_token(instance_id) is not False:
|
||||
# return { 'message' : 'that id already has a token'}, 401
|
||||
#else:
|
||||
expires = datetime.timedelta(days=30)
|
||||
token = create_access_token(instance_id, expires_delta=expires)
|
||||
ctx.add_token(instance_id, token)
|
||||
return { 'access_token' : token }, 200
|
||||
|
@ -45,7 +45,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
|
||||
var thirtyDaysAgo = DateTime.UtcNow.AddMonths(-1);
|
||||
var iqClientRatings = (from rating in context.Set<EFRating>()
|
||||
#if !DEBUG
|
||||
#if DEBUG == false
|
||||
where rating.ActivityAmount >= Plugin.Config.Configuration().TopPlayersMinPlayTime
|
||||
#endif
|
||||
where rating.RatingHistory.Client.Level != Player.Permission.Banned
|
||||
@ -561,9 +561,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
}
|
||||
|
||||
// update their performance
|
||||
#if !DEBUG
|
||||
//#if !DEBUG
|
||||
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5)
|
||||
#endif
|
||||
//#endif
|
||||
{
|
||||
await UpdateStatHistory(attacker, attackerStats);
|
||||
attackerStats.LastStatHistoryUpdate = DateTime.UtcNow;
|
||||
@ -718,7 +718,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
|
||||
ActivityAmount = clientStatsList.Sum(s => s.TimePlayed)
|
||||
});
|
||||
|
||||
await ctx.SaveChangesAsync();
|
||||
try
|
||||
{
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
catch (DbUpdateConcurrencyException e)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,8 +13,6 @@ using SharedLibraryCore.Services;
|
||||
using IW4MAdmin.Plugins.Stats.Config;
|
||||
using IW4MAdmin.Plugins.Stats.Helpers;
|
||||
using IW4MAdmin.Plugins.Stats.Models;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
|
||||
namespace IW4MAdmin.Plugins.Stats
|
||||
{
|
||||
@ -77,8 +75,6 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
string[] killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 13)
|
||||
{
|
||||
// todo: remove me
|
||||
E.Owner.Logger.WriteDebug($"Starting Add script hit (kill) for event with id {E.Id}");
|
||||
await Manager.AddScriptHit(false, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
||||
}
|
||||
@ -95,8 +91,6 @@ namespace IW4MAdmin.Plugins.Stats
|
||||
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];
|
||||
if (killInfo.Length >= 13)
|
||||
{
|
||||
// todo: remove me
|
||||
E.Owner.Logger.WriteDebug($"Starting Add script hit (damage) for event with id {E.Id}");
|
||||
await Manager.AddScriptHit(true, E.Time, E.Origin, E.Target, S.GetHashCode(), S.CurrentMap.Name, killInfo[7], killInfo[8],
|
||||
killInfo[5], killInfo[6], killInfo[3], killInfo[4], killInfo[9], killInfo[10], killInfo[11], killInfo[12], killInfo[13]);
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
using Microsoft.EntityFrameworkCore.Query.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Storage;
|
||||
using SharedLibraryCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WebfrontCore.Controllers;
|
||||
@ -48,6 +52,7 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
};
|
||||
|
||||
var messages = await iqMessages.ToListAsync();
|
||||
string sql = iqMessages.ToSql();
|
||||
|
||||
return View("_MessageContext", messages);
|
||||
}
|
||||
@ -78,4 +83,32 @@ namespace IW4MAdmin.Plugins.Stats.Web.Controllers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class IQueryableExtensions
|
||||
{
|
||||
private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();
|
||||
|
||||
private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");
|
||||
|
||||
private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");
|
||||
|
||||
private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");
|
||||
|
||||
private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");
|
||||
|
||||
public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
|
||||
{
|
||||
var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
|
||||
var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
|
||||
var queryModel = modelGenerator.ParseQuery(query.Expression);
|
||||
var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
|
||||
var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
|
||||
var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
|
||||
var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
|
||||
modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
|
||||
var sql = modelVisitor.Queries.First().ToString();
|
||||
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,8 +150,8 @@ namespace SharedLibraryCore.RCon
|
||||
// will this really prevent flooding?
|
||||
if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
|
||||
{
|
||||
//await Task.Delay(350);
|
||||
Thread.Sleep(350);
|
||||
//await Task.Delay(350);
|
||||
}
|
||||
|
||||
LastQuery = DateTime.Now;
|
||||
|
@ -116,7 +116,7 @@ namespace SharedLibraryCore
|
||||
/// <param name="Message">Message to be sent to all players</param>
|
||||
public async Task Broadcast(String Message)
|
||||
{
|
||||
#if !DEBUG
|
||||
#if DEBUG == false
|
||||
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, Message);
|
||||
#else
|
||||
Logger.WriteVerbose(Message.StripColors());
|
||||
@ -124,8 +124,12 @@ namespace SharedLibraryCore
|
||||
var e = new GameEvent()
|
||||
{
|
||||
Type = GameEvent.EventType.Broadcast,
|
||||
#if DEBUG == true
|
||||
Data = Message,
|
||||
Owner = this
|
||||
#else
|
||||
Data = formattedMessage,
|
||||
#endif
|
||||
Owner = this,
|
||||
};
|
||||
|
||||
Manager.GetEventHandler().AddEvent(e);
|
||||
|
@ -65,14 +65,14 @@ $(document).ready(function () {
|
||||
'serverId': $(this).data('serverid'),
|
||||
'when': $(this).data('when')
|
||||
})
|
||||
.done(function (response) {
|
||||
$('.client-message-context').remove();
|
||||
location.after(response);
|
||||
hideLoader();
|
||||
})
|
||||
.fail(function (jqxhr, textStatus, error) {
|
||||
errorLoader();
|
||||
});
|
||||
.done(function (response) {
|
||||
$('.client-message-context').remove();
|
||||
location.after(response);
|
||||
hideLoader();
|
||||
})
|
||||
.fail(function (jqxhr, textStatus, error) {
|
||||
errorLoader();
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user