clean up rcon, fix a bunch of little things

This commit is contained in:
RaidMax 2018-09-29 14:52:22 -05:00
parent 5d93e7ac57
commit d45729d7e1
33 changed files with 993 additions and 813 deletions

View File

@ -9,69 +9,15 @@ namespace IW4MAdmin.Application
{
class GameEventHandler : IEventHandler
{
static long NextEventId = 1;
readonly IManager Manager;
readonly SortedList<long, GameEvent> OutOfOrderEvents;
readonly SemaphoreSlim IsProcessingEvent;
public GameEventHandler(IManager mgr)
{
Manager = mgr;
OutOfOrderEvents = new SortedList<long, GameEvent>();
IsProcessingEvent = new SemaphoreSlim(1, 1);
}
public void AddEvent(GameEvent gameEvent)
{
#if DEBUG
Manager.GetLogger().WriteDebug($"Got new event of type {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif
GameEvent sortedEvent = null;
lock (OutOfOrderEvents)
{
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
while (sortedEvent?.Id == Interlocked.Read(ref NextEventId))
{
if (OutOfOrderEvents.Count > 0)
{
OutOfOrderEvents.RemoveAt(0);
}
AddEvent(sortedEvent);
sortedEvent = OutOfOrderEvents.Values.FirstOrDefault();
}
}
// both the gameEvent Id and the LastEventId are thread safe because we want to synchronize when the
// event occurs
if (gameEvent.Id == Interlocked.Read(ref NextEventId))
{
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
#if DEBUG == true
Manager.GetLogger().WriteDebug($"notified event {gameEvent.Type} for {gameEvent.Owner} with id {gameEvent.Id}");
#endif
Interlocked.Increment(ref NextEventId);
}
// a "newer" event has been added before and "older" one has been added (due to threads and context switching)
// 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)
{
if (!OutOfOrderEvents.TryGetValue(gameEvent.Id, out GameEvent discard))
{
OutOfOrderEvents.Add(gameEvent.Id, gameEvent);
}
}
}
((Manager as ApplicationManager).OnServerEvent)(this, new GameEventArgs(null, false, gameEvent));
}
}
}

View File

@ -2,8 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.IO;
using System.Threading.Tasks;
using System.Text;
using System.Reflection;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
@ -12,18 +13,12 @@ using SharedLibraryCore.Helpers;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Objects;
using SharedLibraryCore.Services;
using IW4MAdmin.Application.API;
using Microsoft.Extensions.Configuration;
using WebfrontCore;
using SharedLibraryCore.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Text;
using IW4MAdmin.Application.API.Master;
using System.Reflection;
using SharedLibraryCore.Database;
using SharedLibraryCore.Events;
using IW4MAdmin.Application.API.Master;
namespace IW4MAdmin.Application
{
public class ApplicationManager : IManager
@ -69,16 +64,15 @@ namespace IW4MAdmin.Application
StartTime = DateTime.UtcNow;
OnQuit = new ManualResetEventSlim();
PageList = new PageList();
OnServerEvent += OnServerEventAsync;
OnServerEvent += OnGameEvent;
OnServerEvent += EventApi.OnGameEvent;
}
private async void OnServerEventAsync(object sender, GameEventArgs args)
private async void OnGameEvent(object sender, GameEventArgs args)
{
//if (!await ProcessingEvent.WaitAsync(60000))
//{
// Logger.WriteWarning(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_EVENT_TIMEOUT"]);
//}
#if DEBUG == true
Logger.WriteDebug($"Entering event process for {args.Event.Id}");
#endif
var newEvent = args.Event;
@ -90,68 +84,72 @@ namespace IW4MAdmin.Application
Logger.WriteDebug($"Delaying origin execution of event type {newEvent.Type} for {newEvent.Origin} because they are not authed");
if (newEvent.Type == GameEvent.EventType.Command)
{
await newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
newEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["SERVER_DELAYED_EVENT_WAIT"]);
}
// offload it to the player to keep
newEvent.Origin.DelayedEvents.Enqueue(newEvent);
newEvent.OnProcessed.Set();
goto finish;
}
// if the target client is not in an authorized state (detected by RCon) don't execute the event
if (GameEvent.ShouldTargetEventBeDelayed(newEvent))
else if (GameEvent.ShouldTargetEventBeDelayed(newEvent))
{
Logger.WriteDebug($"Delaying target execution of event type {newEvent.Type} for {newEvent.Target} because they are not authed");
// offload it to the player to keep
newEvent.Target.DelayedEvents.Enqueue(newEvent);
newEvent.OnProcessed.Set();
goto finish;
}
await newEvent.Owner.ExecuteEvent(newEvent);
// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
(//newEvent.Origin?.State == Player.ClientState.Connected ||
newEvent.Type == GameEvent.EventType.Connect))
else
{
var events = newEvent.Origin.DelayedEvents;
// add the delayed event to the queue
while (events.Count > 0)
await newEvent.Owner.ExecuteEvent(newEvent);
// save the event info to the database
var changeHistorySvc = new ChangeHistoryService();
await changeHistorySvc.Add(args.Event);
// todo: this is a hacky mess
if (newEvent.Origin?.DelayedEvents.Count > 0 &&
(//newEvent.Origin?.State == Player.ClientState.Connected ||
newEvent.Type == GameEvent.EventType.Connect))
{
var oldEvent = events.Dequeue();
var events = newEvent.Origin.DelayedEvents;
var e = new GameEvent()
// add the delayed event to the queue
while (events.Count > 0)
{
Type = oldEvent.Type,
Origin = newEvent.Origin,
Data = oldEvent.Data,
Extra = oldEvent.Extra,
Owner = oldEvent.Owner,
Message = oldEvent.Message,
Target = oldEvent.Target,
Remote = oldEvent.Remote
};
var oldEvent = events.Dequeue();
e.Origin = newEvent.Origin;
// check if the target was assigned
if (e.Target != null)
{
// update the target incase they left or have newer info
e.Target = newEvent.Owner.GetPlayersAsList()
.FirstOrDefault(p => p.NetworkId == e.Target.NetworkId);
// we have to throw out the event because they left
if (e.Target == null)
var e = new GameEvent()
{
Logger.WriteWarning($"Delayed event for {e.Origin} was removed because the target has left");
// hack: don't do anything with the event because the target is invalid
e.Type = GameEvent.EventType.Unknown;
Type = oldEvent.Type,
Origin = newEvent.Origin,
Data = oldEvent.Data,
Extra = oldEvent.Extra,
Owner = oldEvent.Owner,
Message = oldEvent.Message,
Target = oldEvent.Target,
Remote = oldEvent.Remote
};
e.Origin = newEvent.Origin;
// check if the target was assigned
if (e.Target != null)
{
// update the target incase they left or have newer info
e.Target = newEvent.Owner.GetPlayersAsList()
.FirstOrDefault(p => p.NetworkId == e.Target.NetworkId);
// we have to throw out the event because they left
if (e.Target == null)
{
Logger.WriteWarning($"Delayed event for {e.Origin} was ignored because the target has left");
// hack: don't do anything with the event because the target is invalid
e.Type = GameEvent.EventType.Unknown;
}
}
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
this.GetEventHandler().AddEvent(e);
}
Logger.WriteDebug($"Adding delayed event of type {e.Type} for {e.Origin} back for processing");
this.GetEventHandler().AddEvent(e);
}
}
@ -163,7 +161,7 @@ namespace IW4MAdmin.Application
// this happens if a plugin requires login
catch (AuthorizationException ex)
{
await newEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_NOTAUTHORIZED"]} - {ex.Message}");
newEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_NOTAUTHORIZED"]} - {ex.Message}");
}
catch (NetworkException ex)
@ -183,16 +181,8 @@ namespace IW4MAdmin.Application
Logger.WriteDebug(ex.GetExceptionInfo());
}
finish:
//if (ProcessingEvent.CurrentCount < 1)
//{
// ProcessingEvent.Release(1);
//}
// tell anyone waiting for the output that we're done
newEvent.OnProcessed.Set();
var changeHistorySvc = new ChangeHistoryService();
await changeHistorySvc.Add(args.Event);
}
public IList<Server> GetServers()
@ -547,10 +537,7 @@ namespace IW4MAdmin.Application
public void Start()
{
// this needs to be run seperately from the main thread
#if !DEBUG
// start heartbeat
var _ = Task.Run(() => SendHeartbeat(new HeartbeatState()));
#endif
_ = Task.Run(() => UpdateServerStates());
while (Running)

View File

@ -71,7 +71,6 @@ namespace IW4MAdmin
polledPlayer.Ping < 1 ||
polledPlayer.ClientNumber < 0)
{
//Logger.WriteDebug($"Skipping client not in connected state {P}");
return false;
}
@ -237,7 +236,7 @@ namespace IW4MAdmin
{
autoKickClient.AdministeredPenalties.Add(new EFPenalty() { AutomatedOffense = currentBan.AutomatedOffense });
}
await player.Ban($"{currentBan.Offense}", autoKickClient);
player.Ban($"{currentBan.Offense}", autoKickClient);
}
// they didn't fully connect so empty their slot
@ -286,6 +285,7 @@ namespace IW4MAdmin
public override async Task ExecuteEvent(GameEvent E)
{
bool canExecuteCommand = true;
if (!await ProcessEvent(E))
{
return;
@ -318,7 +318,7 @@ namespace IW4MAdmin
}
catch (AuthorizationException e)
{
await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
canExecuteCommand = false;
}
catch (Exception Except)
@ -334,7 +334,7 @@ namespace IW4MAdmin
(canExecuteCommand ||
E.Origin?.Level == Player.Permission.Console))
{
await (((Command)E.Extra).ExecuteAsync(E));
var _ = (((Command)E.Extra).ExecuteAsync(E));
}
}
@ -345,7 +345,7 @@ namespace IW4MAdmin
/// <returns></returns>
override protected async Task<bool> ProcessEvent(GameEvent E)
{
if (E.Type == GameEvent.EventType.Connect)
if (E.Type == GameEvent.EventType.Connect)
{
E.Origin.State = Player.ClientState.Authenticated;
// add them to the server
@ -366,7 +366,9 @@ namespace IW4MAdmin
});
if (E.Origin.Level > Player.Permission.Moderator)
await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
{
E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
}
}
else if (E.Type == GameEvent.EventType.Join)
@ -558,8 +560,8 @@ namespace IW4MAdmin
}
// only check every 2 minutes if the server doesn't seem to be responding
/* if ((DateTime.Now - LastPoll).TotalMinutes < 0.5 && ConnectionErrors >= 1)
return true;*/
/* if ((DateTime.Now - LastPoll).TotalMinutes < 0.5 && ConnectionErrors >= 1)
return true;*/
try
{
@ -668,7 +670,9 @@ namespace IW4MAdmin
string[] messages = this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]).Split(Environment.NewLine);
foreach (string message in messages)
await Broadcast(message);
{
Broadcast(message);
}
NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1;
start = DateTime.Now;
@ -811,7 +815,7 @@ namespace IW4MAdmin
Logger.WriteError($"{logPath} {loc["SERVER_ERROR_DNE"]}");
#if !DEBUG
throw new ServerException($"{loc["SERVER_ERROR_LOG"]} {logPath}");
//#else
//#else
LogEvent = new GameLogEventDetection(this, logPath, logfile.Value);
#endif
}
@ -824,7 +828,7 @@ namespace IW4MAdmin
_ = Task.Run(() => LogEvent.PollForChanges());
#if !DEBUG
await Broadcast(loc["BROADCAST_ONLINE"]);
Broadcast(loc["BROADCAST_ONLINE"]);
#endif
}
@ -847,13 +851,13 @@ namespace IW4MAdmin
{
if (Target.Warnings >= 4)
{
await Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient);
Target.Kick(loc["SERVER_WARNLIMT_REACHED"], Utilities.IW4MAdminClient);
return;
}
Target.Warnings++;
String Message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}";
await Target.CurrentServer.Broadcast(Message);
String message = $"^1{loc["SERVER_WARNING"]} ^7[^3{Target.Warnings}^7]: ^3{Target.Name}^7, {Reason}";
Target.CurrentServer.Broadcast(message);
}
Penalty newPenalty = new Penalty()

View File

@ -73,6 +73,9 @@
<Compile Include="master\resources\null.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="Master\resources\server.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="master\resources\version.py">
<SubType>Code</SubType>
</Compile>
@ -108,7 +111,9 @@
<Folder Include="master\templates\" />
</ItemGroup>
<ItemGroup>
<None Include="FolderProfile.pubxml" />
<Content Include="master\config\master.json" />
<Content Include="master\templates\serverlist.html" />
<Content Include="requirements.txt" />
<Content Include="master\templates\index.html" />
<Content Include="master\templates\layout.html" />

View File

@ -1,6 +1,6 @@
class ServerModel(object):
def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype):
def __init__(self, id, port, game, hostname, clientnum, maxclientnum, map, gametype, ip):
self.id = id
self.port = port
self.game = game
@ -9,6 +9,7 @@ class ServerModel(object):
self.maxclientnum = maxclientnum
self.map = map
self.gametype = gametype
self.ip = ip
def __repr__(self):
return '<ServerModel(id={id})>'.format(id=self.id)

View File

@ -4,6 +4,7 @@ from flask_jwt_extended import jwt_required
from marshmallow import ValidationError
from master.schema.instanceschema import InstanceSchema
from master import ctx
import json
class Instance(Resource):
def get(self, id=None):
@ -21,6 +22,8 @@ class Instance(Resource):
@jwt_required
def put(self, id):
try:
for server in request.json['servers']:
server['ip'] = request.remote_addr
instance = InstanceSchema().load(request.json)
except ValidationError as err:
return {'message' : err.messages }, 400
@ -30,6 +33,8 @@ class Instance(Resource):
@jwt_required
def post(self):
try:
for server in request.json['servers']:
server['ip'] = request.remote_addr
instance = InstanceSchema().load(request.json)
except ValidationError as err:
return {'message' : err.messages }, 400

View File

@ -0,0 +1,6 @@
from flask_restful import Resource
class Server(Resource):
"""description of class"""

View File

@ -6,6 +6,7 @@ from master.resources.authenticate import Authenticate
from master.resources.version import Version
from master.resources.history_graph import HistoryGraph
from master.resources.localization import Localization
from master.resources.server import Server
api.add_resource(Null, '/null')
api.add_resource(Instance, '/instance/', '/instance/<string:id>')
@ -13,3 +14,4 @@ api.add_resource(Version, '/version')
api.add_resource(Authenticate, '/authenticate')
api.add_resource(HistoryGraph, '/history/', '/history/<int:history_count>')
api.add_resource(Localization, '/localization/', '/localization/<string:language_tag>')
api.add_resource(Server, '/server')

View File

@ -6,17 +6,20 @@ class ServerSchema(Schema):
required=True,
validate=validate.Range(1, 2147483647, 'invalid id')
)
ip = fields.Str(
required=True
)
port = fields.Int(
required=True,
validate=validate.Range(1, 665535, 'invalid port')
validate=validate.Range(1, 65535, 'invalid port')
)
game = fields.String(
required=True,
validate=validate.Length(1, 8, 'invalid game name')
validate=validate.Length(1, 5, 'invalid game name')
)
hostname = fields.String(
required=True,
validate=validate.Length(1, 48, 'invalid hostname')
validate=validate.Length(1, 64, 'invalid hostname')
)
clientnum = fields.Int(
required=True,

View File

@ -39,6 +39,7 @@
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8="
crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
{% block scripts %}{% endblock %}
</body>
</html>

View File

@ -0,0 +1,113 @@
{% extends "layout.html" %}
{% block content %}
<!-- todo: move this! -->
<style>
.server-row {
cursor: pointer;
}
.modal-content, .nav-item {
background-color: #212529;
color: #fff;
}
.modal-header, .modal-footer {
border-color: #32383e !important;
}
.modal-dark button.close, a.nav-link {
color: #fff;
}
</style>
<div class="modal modal-dark" id="serverModalCenter" tabindex="-1" role="dialog" aria-labelledby="serverModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="serverModalTitle">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<div class="h5" id="server_socket"></div>
</div>
<div class="modal-footer">
<button id="connect_button" type="button" class="btn btn-dark">Connect</button>
<button type="button" class="btn btn-dark" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<nav>
<div class="nav nav-tabs" id="server_game_tabs" role="tablist">
{% for game in games %}
<a class="nav-item nav-link {{'active' if loop.first else ''}}" id="{{game}}_servers_tab" data-toggle="tab" href="#{{game}}_servers" role="tab" aria-controls="{{game}}_servers" aria-selected="{{'true' if loop.first else 'false' }}">{{game}}</a>
{% endfor %}
</div>
</nav>
<div class="tab-content" id="server_game_tabs_content">
{% for game, servers in games.items() %}
<div class="tab-pane {{'show active' if loop.first else ''}}" id="{{game}}_servers" role="tabpanel" aria-labelledby="{{game}}_servers_tab">
<table class="table table-dark table-striped table-hover table-responsive-lg">
<thead>
<tr>
<th>Server Name</th>
<th>Map Name</th>
<th>Players</th>
<th>Mode</th>
<th class="text-center">Connect</th>
</tr>
</thead>
<tbody>
{% for server in servers %}
<tr class="server-row" data-toggle="modal" data-target="#serverModalCenter"
data-ip="{{server.ip}}" data-port="{{server.port}}">
<td data-hostname="{{server.hostname}}" class="server-hostname">{{server.hostname}}</td>
<td data-map="{{server.map}} " class="server-map">{{server.map}}</td>
<td data-clientnum="{{server.clientnum}}" data-maxclientnum="{{server.maxclientnum}}"
class="server-clientnum">
{{server.clientnum}}/{{server.maxclientnum}}
</td>
<td data-gametype="{{server.gametype}}" class="server-gametype">{{server.gametype}}</td>
<td class="text-center"><span class="oi oi-play-circle"></span></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</div>
<div class="w-100 small text-right text-muted">
<span>Developed by RaidMax</span><br />
<span>PRERELEASE</span>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
$(document).ready(() => {
$('.server-row').off('click');
$('.server-row').on('click', function (e) {
$('#serverModalTitle').text($(this).find('.server-hostname').text());
$('#server_socket').text(`/connect ${$(this).data('ip')}:${$(this).data('port')}`);
});
$('#connect_button').off('click');
$('#connect_button').on('click', (e) => alert('soon...'));
});
</script>
{% endblock %}

View File

@ -4,8 +4,9 @@ Routes and views for the flask application.
from datetime import datetime
from flask import render_template
from master import app
from master import app, ctx
from master.resources.history_graph import HistoryGraph
from collections import defaultdict
@app.route('/')
def home():
@ -19,3 +20,16 @@ def home():
client_count = _history_graph[0]['client_count'],
server_count = _history_graph[0]['server_count']
)
@app.route('/servers')
def servers():
servers = defaultdict(list)
if len(ctx.instance_list.values()) > 0:
ungrouped_servers = [server for instance in ctx.instance_list.values() for server in instance.servers]
for server in ungrouped_servers:
servers[server.game].append(server)
return render_template(
'serverlist.html',
title = 'Server List',
games = servers
)

View File

@ -15,7 +15,8 @@ namespace IW4MAdmin.Plugins.Login.Commands
Name = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_PASSWORD"],
Required = true
}
}){ }
})
{ }
public override async Task ExecuteAsync(GameEvent E)
{
@ -25,12 +26,12 @@ namespace IW4MAdmin.Plugins.Login.Commands
if (hashedPassword[0] == client.Password)
{
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]);
}
else
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
}
}
}

View File

@ -22,10 +22,10 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
ConcurrentDictionary<int, Tracking> ProfanityCounts;
IManager Manager;
public async Task OnEventAsync(GameEvent E, Server S)
public Task OnEventAsync(GameEvent E, Server S)
{
if (!Settings.Configuration().EnableProfanityDeterment)
return;
return Task.CompletedTask; ;
if (E.Type == GameEvent.EventType.Connect)
{
@ -48,7 +48,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
if (containsObjectionalWord)
{
await E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
E.Origin.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
{
ClientId = 1
});
@ -84,7 +84,7 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
var clientProfanity = ProfanityCounts[E.Origin.ClientId];
if (clientProfanity.Infringements >= Settings.Configuration().KickAfterInfringementCount)
{
await clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
clientProfanity.Client.Kick(Settings.Configuration().ProfanityKickMessage, new Player()
{
ClientId = 1
});
@ -94,13 +94,14 @@ namespace IW4MAdmin.Plugins.ProfanityDeterment
{
clientProfanity.Infringements++;
await clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
clientProfanity.Client.Warn(Settings.Configuration().ProfanityWarningMessage, new Player()
{
ClientId = 1
});
}
}
}
return Task.CompletedTask;
}
public async Task OnLoadAsync(IManager manager)

View File

@ -139,10 +139,7 @@ namespace IW4MAdmin.Plugins.Stats.Cheat
}
// flag
if (
#if DEBUG == false
currentStrain > Thresholds.MaxStrainFlag &&
#endif
if (currentStrain > Thresholds.MaxStrainFlag &&
HitCount >= 10)
{
result = new DetectionPenaltyResult()

View File

@ -63,12 +63,16 @@ namespace IW4MAdmin.Plugins.Stats.Commands
if (!E.Message.IsBroadcastCommand())
{
foreach (var stat in topStats)
await E.Origin.Tell(stat);
{
E.Origin.Tell(stat);
}
}
else
{
foreach (var stat in topStats)
await E.Owner.Broadcast(stat);
{
E.Owner.Broadcast(stat);
}
}
}
}

View File

@ -34,12 +34,12 @@ namespace IW4MAdmin.Plugins.Stats.Commands
// fixme: this doesn't work properly when another context exists
await svc.SaveChangesAsync();
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]);
}
else
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_FAIL"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_FAIL"]);
}
}
}

View File

@ -76,16 +76,15 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
foreach (var stat in topStats)
{
await E.Origin.Tell(stat);
await Task.Delay(SharedLibraryCore.RCon.StaticHelpers.FloodProtectionInterval);
E.Origin.Tell(stat);
}
}
else
{
foreach (var stat in topStats)
{
await E.Owner.Broadcast(stat);
await Task.Delay(SharedLibraryCore.RCon.StaticHelpers.FloodProtectionInterval);
E.Owner.Broadcast(stat);
}
}
}

View File

@ -35,39 +35,40 @@ namespace IW4MAdmin.Plugins.Stats.Commands
if (E.Target == null)
{
await E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]);
return;
E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]);
}
}
var clientStats = new GenericRepository<EFClientStatistics>();
int serverId = E.Owner.GetHashCode();
if (E.Target != null)
{
pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId).First();
pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId)).First();
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
}
else
{
pStats = pStats = clientStats.Find(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId).First();
pStats = (await clientStats.FindAsync(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId)).First();
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()}";
}
if (E.Message.IsBroadcastCommand())
{
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
await E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7");
await E.Owner.Broadcast(statLine);
E.Owner.Broadcast($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{name}^7");
E.Owner.Broadcast(statLine);
}
else
{
if (E.Target != null)
await E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7");
await E.Origin.Tell(statLine);
{
E.Origin.Tell($"{loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"]} ^5{E.Target.Name}^7");
}
E.Origin.Tell(statLine);
}
}
}

View File

@ -243,8 +243,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
}
var playerStats = Servers[serverId].PlayerStats;
var statsSvc = ContextThreads[serverId];
var detectionStats = Servers[serverId].PlayerDetections;
var statsSvc = ContextThreads[serverId];
if (playerStats.ContainsKey(pl.ClientId))
{
@ -295,17 +295,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await statsSvc.ClientStatSvc.SaveChangesAsync();
}
// adjusts for adding new hit location
if (clientStats.HitLocations.Count == 19)
{
clientStats.HitLocations.Add(new EFHitLocationCount()
{
Location = IW4Info.HitLocation.shield
});
await statsSvc.ClientStatSvc.SaveChangesAsync();
}
// for stats before rating
if (clientStats.EloRating == 0.0)
{
@ -382,7 +371,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (match.Success)
{
// this gives us what time the player is on
// this gives us what team the player is on
var attackerStats = Servers[serverId].PlayerStats[attackerClientId];
var victimStats = Servers[serverId].PlayerStats[victimClientId];
IW4Info.Team victimTeam = (IW4Info.Team)Enum.Parse(typeof(IW4Info.Team), match.Groups[4].ToString());
@ -507,13 +496,14 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await ApplyPenalty(clientDetection.ProcessTotalRatio(clientStats), clientDetection, attacker, ctx);
}
await ctx.SaveChangesAsync();
await clientStatsSvc.SaveChangesAsync();
await ctx.SaveChangesAsync();
}
catch (Exception ex)
{
Log.WriteError("AC ERROR");
Log.WriteDebug(ex.GetExceptionInfo());
}
OnProcessingPenalty.Release(1);
@ -529,7 +519,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
break;
}
await attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player()
attacker.Ban(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_STATS_CHEAT_DETECTED"], new Player()
{
ClientId = 1,
AdministeredPenalties = new List<EFPenalty>()
@ -587,80 +577,80 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
while ((change = clientDetection.Tracker.GetNextChange()) != default(EFACSnapshot))
{
//if (change.HitOriginId == 0)
//{
// change.HitOriginId = change.HitOrigin.Vector3Id;
// change.HitOrigin = null;
//}
//if (change.HitDestinationId == 0)
//{
// change.HitDestinationId = change.HitDestination.Vector3Id;
// change.HitDestination = null;
//}
//if (change.CurrentViewAngleId == 0)
//{
// change.CurrentViewAngleId = change.CurrentViewAngle.Vector3Id;
// change.CurrentViewAngle = null;
//}
//if (change.LastStrainAngleId == 0)
//{
// change.LastStrainAngleId = change.LastStrainAngle.Vector3Id;
// change.LastStrainAngle = null;
//}
if (change.HitOrigin.Vector3Id > 0)
{
change.HitOriginId = change.HitOrigin.Vector3Id;
ctx.Attach(change.HitOrigin);
}
else if (change.HitOrigin.Vector3Id == 0)
{
ctx.Add(change.HitOrigin);
}
if (change.HitDestination.Vector3Id > 0)
{
change.HitDestinationId = change.HitDestination.Vector3Id;
ctx.Attach(change.HitDestination);
}
else if (change.HitDestination.Vector3Id == 0)
{
ctx.Add(change.HitOrigin);
}
if (change.CurrentViewAngle.Vector3Id > 0)
{
change.CurrentViewAngleId = change.CurrentViewAngle.Vector3Id;
ctx.Attach(change.CurrentViewAngle);
}
else if (change.CurrentViewAngle.Vector3Id == 0)
{
ctx.Add(change.HitOrigin);
}
if (change.LastStrainAngle.Vector3Id > 0)
{
change.LastStrainAngleId = change.LastStrainAngle.Vector3Id;
ctx.Attach(change.LastStrainAngle);
}
ctx.Add(change);
else if (change.LastStrainAngle.Vector3Id == 0)
{
ctx.Add(change.HitOrigin);
}
ctx.Add(change);
}
}
public async Task AddStandardKill(Player attacker, Player victim)
{
int serverId = attacker.CurrentServer.GetHashCode();
EFClientStatistics attackerStats = null;
try
if (!Servers[serverId].PlayerStats.ContainsKey(attacker.ClientId))
{
attackerStats = await AddPlayer(attacker);
}
else
{
attackerStats = Servers[serverId].PlayerStats[attacker.ClientId];
}
catch (KeyNotFoundException)
EFClientStatistics victimStats = null;
if (!Servers[serverId].PlayerStats.ContainsKey(victim.ClientId))
{
// happens when the client has disconnected before the last status update
Log.WriteWarning($"[Stats::AddStandardKill] kill attacker ClientId is invalid {attacker.ClientId}-{attacker}");
return;
victimStats = await AddPlayer(victim);
}
EFClientStatistics victimStats = null;
try
else
{
victimStats = Servers[serverId].PlayerStats[victim.ClientId];
}
catch (KeyNotFoundException)
{
Log.WriteWarning($"[Stats::AddStandardKill] kill victim ClientId is invalid {victim.ClientId}-{victim}");
return;
}
#if DEBUG
Log.WriteDebug("Calculating standard kill");
#endif
@ -691,7 +681,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
StreakMessage.MessageOnStreak(-1, -1);
if (streakMessage != string.Empty)
await attacker.Tell(streakMessage);
{
attacker.Tell(streakMessage);
}
// fixme: why?
if (double.IsNaN(victimStats.SPM) || double.IsNaN(victimStats.Skill))
@ -711,6 +703,8 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// update their performance
#if !DEBUG
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 2.5)
#else
if ((DateTime.UtcNow - attackerStats.LastStatHistoryUpdate).TotalMinutes >= 0.1)
#endif
{
attackerStats.LastStatHistoryUpdate = DateTime.UtcNow;
@ -765,13 +759,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
if (clientHistory.RatingHistoryId == 0)
{
ctx.Add(clientHistory);
// Log.WriteDebug($"adding first time client history {client.ClientId}");
await ctx.SaveChangesAsync();
}
else
{
//ctx.Update(clientHistory);
}
#region INDIVIDUAL_SERVER_PERFORMANCE
@ -793,8 +780,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.First();
ctx.Remove(ratingToRemove);
//Log.WriteDebug($"remove oldest rating {client.ClientId}");
await ctx.SaveChangesAsync();
}
// set the previous newest to false
@ -810,8 +795,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
ctx.Update(ratingToUnsetNewest);
ctx.Entry(ratingToUnsetNewest).Property(r => r.Newest).IsModified = true;
ratingToUnsetNewest.Newest = false;
//Log.WriteDebug($"unsetting previous newest flag {client.ClientId}");
await ctx.SaveChangesAsync();
}
}
@ -829,9 +812,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
// add new rating for current server
ctx.Add(newServerRating);
//Log.WriteDebug($"adding new server rating {client.ClientId}");
await ctx.SaveChangesAsync();
#endregion
#region OVERALL_RATING
// select all performance & time played for current client
@ -877,8 +857,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
.First();
ctx.Remove(ratingToRemove);
//Log.WriteDebug($"remove oldest overall rating {client.ClientId}");
await ctx.SaveChangesAsync();
}
// set the previous average newest to false
@ -894,8 +872,6 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
ctx.Update(ratingToUnsetNewest);
ctx.Entry(ratingToUnsetNewest).Property(r => r.Newest).IsModified = true;
ratingToUnsetNewest.Newest = false;
//Log.WriteDebug($"unsetting overall newest rating {client.ClientId}");
await ctx.SaveChangesAsync();
}
}
@ -913,7 +889,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
ctx.Add(averageRating);
#endregion
//Log.WriteDebug($"adding new average rating {client.ClientId}");
await ctx.SaveChangesAsync();
}
}
@ -1148,7 +1124,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
await statsSvc.ServerSvc.SaveChangesAsync();
statsSvc = null;
// this should prevent the gunk for having a long lasting context.
// this should prevent the gunk from having a long lasting context.
ContextThreads[serverId] = new ThreadSafeStatsService();
}

View File

@ -14,7 +14,7 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
{
get
{
return new GenericRepository<EFClientStatistics>();
return new GenericRepository<EFClientStatistics>(true);
}
}
public GenericRepository<EFServer> ServerSvc { get; private set; }
@ -30,11 +30,9 @@ namespace IW4MAdmin.Plugins.Stats.Helpers
public ThreadSafeStatsService()
{
//ClientStatSvc = new GenericRepository<EFClientStatistics>();
ServerSvc = new GenericRepository<EFServer>();
KillStatsSvc = new GenericRepository<EFClientKill>();
ServerStatsSvc = new GenericRepository<EFServerStatistics>();
//MessageSvc = new GenericRepository<EFClientMessage>();
}
}
}

View File

@ -81,11 +81,15 @@ namespace IW4MAdmin.Plugins.Stats
break;
case GameEvent.EventType.Kill:
if (!E.Owner.CustomCallback)
{
await Manager.AddStandardKill(E.Origin, E.Target);
}
break;
case GameEvent.EventType.Damage:
if (!E.Owner.CustomCallback)
{
Manager.AddDamageEvent(E.Data, E.Origin.ClientId, E.Target.ClientId, E.Owner.GetHashCode());
}
break;
case GameEvent.EventType.ScriptDamage:
killInfo = (E.Data != null) ? E.Data.Split(';') : new string[0];

View File

@ -82,17 +82,17 @@ namespace IW4MAdmin.Plugins.Welcome
{
Player newPlayer = E.Origin;
if (newPlayer.Level >= Player.Permission.Trusted && !E.Origin.Masked)
await E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer));
E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().PrivilegedAnnouncementMessage, newPlayer));
await newPlayer.Tell(ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer));
newPlayer.Tell(ProcessAnnouncement(Config.Configuration().UserWelcomeMessage, newPlayer));
if (newPlayer.Level == Player.Permission.Flagged)
{
var penalty = await new GenericRepository<EFPenalty>().FindAsync(p => p.OffenderId == newPlayer.ClientId && p.Type == Penalty.PenaltyType.Flag);
await E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penalty.FirstOrDefault()?.Offense}) has joined!");
E.Owner.ToAdmins($"^1NOTICE: ^7Flagged player ^5{newPlayer.Name} ^7({penalty.FirstOrDefault()?.Offense}) has joined!");
}
else
await E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer));
E.Owner.Broadcast(ProcessAnnouncement(Config.Configuration().UserAnnouncementMessage, newPlayer));
}
}

View File

@ -10,7 +10,7 @@ namespace SharedLibraryCore.Commands
{
public class CommandProcessing
{
public static async Task<Command> ValidateCommand(GameEvent E)
public static async Task<Command> ValidateCommand(GameEvent E)
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var Manager = E.Owner.Manager;
@ -27,7 +27,7 @@ namespace SharedLibraryCore.Commands
if (C == null)
{
await E.Origin.Tell(loc["COMMAND_UNKNOWN"]);
E.Origin.Tell(loc["COMMAND_UNKNOWN"]);
throw new CommandException($"{E.Origin} entered unknown command \"{CommandString}\"");
}
@ -36,14 +36,14 @@ namespace SharedLibraryCore.Commands
if (E.Origin.Level < C.Permission)
{
await E.Origin.Tell(loc["COMMAND_NOACCESS"]);
E.Origin.Tell(loc["COMMAND_NOACCESS"]);
throw new CommandException($"{E.Origin} does not have access to \"{C.Name}\"");
}
if (Args.Length < (C.RequiredArgumentCount))
{
await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
await E.Origin.Tell(C.Syntax);
E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
E.Origin.Tell(C.Syntax);
throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\"");
}
@ -82,7 +82,7 @@ namespace SharedLibraryCore.Commands
matchingPlayers = E.Owner.GetClientByName(E.Data.Trim());
if (matchingPlayers.Count > 1)
{
await E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
throw new CommandException($"{E.Origin} had multiple players found for {C.Name}");
}
else if (matchingPlayers.Count == 1)
@ -95,8 +95,8 @@ namespace SharedLibraryCore.Commands
if (E.Data.Length == 0 && C.RequiredArgumentCount > 1)
{
await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
await E.Origin.Tell(C.Syntax);
E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
E.Origin.Tell(C.Syntax);
throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\"");
}
}
@ -107,9 +107,11 @@ namespace SharedLibraryCore.Commands
matchingPlayers = E.Owner.GetClientByName(Args[0]);
if (matchingPlayers.Count > 1)
{
await E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
E.Origin.Tell(loc["COMMAND_TARGET_MULTI"]);
foreach (var p in matchingPlayers)
await E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}");
{
E.Origin.Tell($"[^3{p.ClientNumber}^7] {p.Name}");
}
throw new CommandException($"{E.Origin} had multiple players found for {C.Name}");
}
else if (matchingPlayers.Count == 1)
@ -125,8 +127,8 @@ namespace SharedLibraryCore.Commands
E.Data == String.Empty) &&
C.RequiresTarget)
{
await E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
await E.Origin.Tell(C.Syntax);
E.Origin.Tell(loc["COMMAND_MISSINGARGS"]);
E.Origin.Tell(C.Syntax);
throw new CommandException($"{E.Origin} did not supply enough arguments for \"{C.Name}\"");
}
}
@ -134,7 +136,7 @@ namespace SharedLibraryCore.Commands
if (E.Target == null && C.RequiresTarget)
{
await E.Origin.Tell(loc["COMMAND_TARGET_NOTFOUND"]);
E.Origin.Tell(loc["COMMAND_TARGET_NOTFOUND"]);
throw new CommandException($"{E.Origin} specified invalid player for \"{C.Name}\"");
}
}

View File

@ -39,13 +39,15 @@ namespace SharedLibraryCore.Commands
if ((await (E.Owner.Manager.GetClientService() as ClientService).GetOwners()).Count == 0)
{
E.Origin.Level = Player.Permission.Owner;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_SUCCESS"]);
// so setpassword/login works
E.Owner.Manager.GetPrivilegedClients().Add(E.Origin.ClientId, E.Origin);
await E.Owner.Manager.GetClientService().Update(E.Origin);
}
else
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_FAIL"]);
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_OWNER_FAIL"]);
}
}
}
@ -69,22 +71,9 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
if (E.Origin.Level <= E.Target.Level)
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"]} {E.Target.Name}");
else
if (!await E.Target.Warn(E.Data, E.Origin).WaitAsync())
{
await E.Target.Warn(E.Data, E.Origin);
var e = new GameEvent()
{
Type = GameEvent.EventType.Warn,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARN_FAIL"]} {E.Target.Name}");
}
}
}
@ -104,9 +93,10 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
E.Target.Warnings = 0;
String Message = $"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"]} {E.Target.Name}";
await E.Owner.Broadcast(Message);
if (await E.Target.WarnClear(E.Origin).WaitAsync())
{
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WARNCLEAR_SUCCESS"]} {E.Target.Name}");
}
}
}
@ -130,24 +120,9 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
if (E.Origin.Level > E.Target.Level)
{
await E.Target.Kick(E.Data, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_SUCCESS"]}");
var e = new GameEvent()
{
Type = GameEvent.EventType.Kick,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_KICK_FAIL"]} {E.Target.Name}");
var _ = await E.Target.Kick(E.Data, E.Origin).WaitAsync() ?
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}");
}
}
@ -164,9 +139,10 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
await E.Owner.Broadcast($"{(E.Owner.GameName == Server.Game.IW4 ? "^:" : "")}{E.Origin.Name} - ^6{E.Data}^7");
E.Owner.Broadcast($"{(E.Owner.GameName == Server.Game.IW4 ? "^:" : "")}{E.Origin.Name} - ^6{E.Data}^7", E.Origin);
return Task.CompletedTask;
}
}
@ -200,24 +176,9 @@ namespace SharedLibraryCore.Commands
if (length.TotalHours >= 1 && length.TotalHours < 2)
Message = E.Data.Replace("1h", "").Replace("1H", "");
if (E.Origin.Level > E.Target.Level)
{
await E.Target.TempBan(Message, length, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_SUCCESS"]} ^5{length.TimeSpanText()}");
var e = new GameEvent()
{
Type = GameEvent.EventType.TempBan,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
}
else
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_TEMPBAN_FAIL"]} {E.Target.Name}");
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}");
}
}
@ -241,13 +202,9 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
if (E.Origin.Level > E.Target.Level)
{
await E.Target.Ban(E.Data, E.Origin);
await E.Origin.Tell($"^5{E.Target} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_SUCCESS"]}");
}
else
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BAN_FAIL"]} {E.Target.Name}");
var _ = await E.Target.Ban(E.Data, E.Origin).WaitAsync() ?
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}");
}
}
@ -274,12 +231,12 @@ namespace SharedLibraryCore.Commands
var penalties = await E.Owner.Manager.GetPenaltyService().GetActivePenaltiesAsync(E.Target.AliasLinkId);
if (penalties.Where(p => p.Type == Penalty.PenaltyType.Ban || p.Type == Penalty.PenaltyType.TempBan).FirstOrDefault() != null)
{
await E.Owner.Unban(E.Data, E.Target, E.Origin);
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"]} {E.Target}");
await E.Target.Unban(E.Data, E.Origin).WaitAsync();
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_SUCCESS"]} {E.Target}");
}
else
{
await E.Origin.Tell($"{E.Target} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"]}");
E.Origin.Tell($"{E.Target} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNBAN_FAIL"]}");
}
}
}
@ -290,10 +247,12 @@ namespace SharedLibraryCore.Commands
base("whoami", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_WHO_DESC"], "who", Player.Permission.User, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
String You = String.Format("{0} [^3#{1}^7] {2} [^3@{3}^7] [{4}^7] IP: {5}", E.Origin.Name, E.Origin.ClientNumber, E.Origin.NetworkId, E.Origin.ClientId, Utilities.ConvertLevelToColor(E.Origin.Level, E.Origin.ClientPermission.Name), E.Origin.IPAddressString);
await E.Origin.Tell(You);
E.Origin.Tell(You);
return Task.CompletedTask;
}
}
@ -303,7 +262,7 @@ namespace SharedLibraryCore.Commands
base("list", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_LIST_DESC"], "l", Player.Permission.Moderator, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
StringBuilder playerList = new StringBuilder();
int count = 0;
@ -322,8 +281,7 @@ namespace SharedLibraryCore.Commands
if (count == 2 || E.Owner.GetPlayersAsList().Count == 1)
{
await E.Origin.Tell(playerList.ToString());
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell(playerList.ToString());
count = 0;
playerList = new StringBuilder();
continue;
@ -334,8 +292,12 @@ namespace SharedLibraryCore.Commands
if (playerList.Length > 0)
{
await E.Origin.Tell(playerList.ToString());
E.Origin.Tell(playerList.ToString());
}
// todo: make no players response for webfront
return Task.CompletedTask;
}
}
@ -352,7 +314,7 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
String cmd = E.Data.Trim();
@ -364,15 +326,16 @@ namespace SharedLibraryCore.Commands
if (C.Name == cmd.ToLower() ||
C.Alias == cmd.ToLower())
{
await E.Origin.Tell("[^3" + C.Name + "^7] " + C.Description);
await E.Origin.Tell(C.Syntax);
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell($"[^3{C.Name}^7] {C.Description}");
E.Origin.Tell(C.Syntax);
found = true;
}
}
if (!found)
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_NOTFOUND"]);
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_NOTFOUND"]);
}
}
else
@ -388,20 +351,18 @@ namespace SharedLibraryCore.Commands
helpResponse.Append(" [^3" + C.Name + "^7] ");
if (count >= 4)
{
if (E.Message[0] == '@')
await E.Owner.Broadcast(helpResponse.ToString());
else
await E.Origin.Tell(helpResponse.ToString());
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell(helpResponse.ToString());
helpResponse = new StringBuilder();
count = 0;
}
count++;
}
}
await E.Origin.Tell(helpResponse.ToString());
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_MOREINFO"]);
E.Origin.Tell(helpResponse.ToString());
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_HELP_MOREINFO"]);
}
return Task.CompletedTask;
}
}
@ -415,10 +376,9 @@ namespace SharedLibraryCore.Commands
{
await E.Owner.ExecuteCommandAsync("fast_restart");
if (!E.Origin.Masked)
await E.Owner.Broadcast($"^5{E.Origin.Name} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_UNMASKED"]}");
else
await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_MASKED"]);
var _ = !E.Origin.Masked ?
E.Owner.Broadcast($"^5{E.Origin.Name} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_UNMASKED"]}") :
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FASTRESTART_MASKED"]);
}
}
@ -430,10 +390,10 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
if (!E.Origin.Masked)
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]");
else
await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]);
var _ = !E.Origin.Masked ?
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"]} [^5{E.Origin.Name}^7]", E.Origin) :
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAPROTATE"], E.Origin);
await Task.Delay(5000);
await E.Owner.ExecuteCommandAsync("map_rotate");
}
@ -461,7 +421,7 @@ namespace SharedLibraryCore.Commands
{
if (E.Target == E.Origin)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SELF"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SELF"]);
return;
}
@ -471,14 +431,14 @@ namespace SharedLibraryCore.Commands
if (newPerm == Player.Permission.Owner &&
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableMultipleOwners)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_OWNER"]);
return;
}
if (E.Origin.Level < Player.Permission.Owner &&
!E.Owner.Manager.GetApplicationSettings().Configuration().EnableSteppedHierarchy)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}");
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_STEPPEDDISABLED"]} ^5{E.Target.Name}");
return;
}
@ -486,7 +446,7 @@ namespace SharedLibraryCore.Commands
{
if (E.Origin.Level < Player.Permission.Owner)
{
await E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString()));
E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_LEVELTOOHIGH"], E.Target.Name, (E.Origin.Level - 1).ToString()));
return;
}
}
@ -500,7 +460,7 @@ namespace SharedLibraryCore.Commands
{
ActiveClient.Level = newPerm;
await E.Owner.Manager.GetClientService().Update(ActiveClient);
await ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
ActiveClient.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS_TARGET"]} {newPerm}");
}
else
@ -535,11 +495,13 @@ namespace SharedLibraryCore.Commands
E.Owner.Manager.GetEventHandler().AddEvent(e);
await E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}");
E.Origin.Tell($"{E.Target.Name} {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_SUCCESS"]}");
}
else
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_FAIL"]);
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_SETLEVEL_FAIL"]);
}
}
}
@ -549,9 +511,10 @@ namespace SharedLibraryCore.Commands
base("usage", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_DESC"], "us", Player.Permission.Moderator, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
await E.Origin.Tell($"IW4MAdmin {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"]}" + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB");
E.Origin.Tell($"IW4MAdmin {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_USAGE_TEXT"]} " + Math.Round(((System.Diagnostics.Process.GetCurrentProcess().PrivateMemorySize64 / 2048f) / 1200f), 1) + "MB");
return Task.CompletedTask;
}
}
@ -561,11 +524,12 @@ namespace SharedLibraryCore.Commands
base("uptime", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UPTIME_DESC"], "up", Player.Permission.Moderator, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
TimeSpan uptime = DateTime.Now - System.Diagnostics.Process.GetCurrentProcess().StartTime;
var loc = Utilities.CurrentLocalization.LocalizationIndex;
await E.Origin.Tell($"IW4M Admin {loc["COMMANDS_UPTIME_TEXT"]} {uptime.Days} {loc["GLOBAL_TIME_DAYS"]}, {uptime.Hours} {loc["GLOBAL_TIME_HOURS"]}, {uptime.Minutes} {loc["GLOBAL_TIME_MINUTES"]}");
E.Origin.Tell($"IW4M Admin {loc["COMMANDS_UPTIME_TEXT"]} {uptime.Days} {loc["GLOBAL_TIME_DAYS"]}, {uptime.Hours} {loc["GLOBAL_TIME_HOURS"]}, {uptime.Minutes} {loc["GLOBAL_TIME_MINUTES"]}");
return Task.CompletedTask;
}
}
@ -587,15 +551,14 @@ namespace SharedLibraryCore.Commands
Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ADMINS_NONE"];
}
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
foreach (string line in OnlineAdmins(E.Owner).Split(Environment.NewLine))
{
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
await t;
await Task.Delay(FloodProtectionInterval);
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast(line) : E.Origin.Tell(line);
}
return Task.CompletedTask;
}
}
@ -619,14 +582,14 @@ namespace SharedLibraryCore.Commands
{
if (m.Name.ToLower() == newMap || m.Alias.ToLower() == newMap)
{
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}");
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_SUCCESS"]} ^5{m.Alias}");
await Task.Delay(5000);
await E.Owner.LoadMap(m.Name);
return;
}
}
await E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}");
E.Owner.Broadcast($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MAP_UKN"]} ^5{newMap}");
await Task.Delay(5000);
await E.Owner.LoadMap(newMap);
}
@ -649,7 +612,7 @@ namespace SharedLibraryCore.Commands
{
if (E.Data.Length < 3)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_MIN"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_MIN"]);
return;
}
@ -660,7 +623,7 @@ namespace SharedLibraryCore.Commands
if (db_players.Count == 0)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_EMPTY"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FIND_EMPTY"]);
return;
}
@ -671,8 +634,7 @@ namespace SharedLibraryCore.Commands
string msg = P.Name.ToLower().Contains(E.Data.ToLower()) ?
$"[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}" :
$"({P.AliasLink.Children.FirstOrDefault(a => a.Name.ToLower().Contains(E.Data.ToLower()))?.Name})->[^3{P.Name}^7] [^3@{P.ClientId}^7] - [{ Utilities.ConvertLevelToColor(P.Level, localizedLevel)}^7] - {P.IPAddressString} | last seen {Utilities.GetTimePassed(P.LastConnection)}";
await E.Origin.Tell(msg);
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell(msg);
}
}
}
@ -683,15 +645,14 @@ namespace SharedLibraryCore.Commands
base("rules", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_DESC"], "r", Player.Permission.User, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
if (E.Owner.Manager.GetApplicationSettings().Configuration().GlobalRules?.Count < 1 &&
E.Owner.ServerConfig.Rules?.Count < 1)
{
if (E.Message.IsBroadcastCommand())
await E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]);
else
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]);
var _ = E.Message.IsBroadcastCommand() ?
E.Owner.Broadcast(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]) :
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RULES_NONE"]);
}
else
@ -703,11 +664,11 @@ namespace SharedLibraryCore.Commands
foreach (string r in rules)
{
var t = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
await t;
await Task.Delay(FloodProtectionInterval);
var _ = E.Message.IsBroadcastCommand() ? E.Owner.Broadcast($"- {r}") : E.Origin.Tell($"- {r}");
}
}
return Task.CompletedTask;
}
}
@ -729,10 +690,11 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
await E.Target.Tell($"^1{E.Origin.Name} ^3[PM]^7 - {E.Data}");
await E.Origin.Tell($"To ^3{E.Target.Name} ^7-> {E.Data}");
E.Target.Tell($"^1{E.Origin.Name} ^3[PM]^7 - {E.Data}");
E.Origin.Tell($"To ^3{E.Target.Name} ^7-> {E.Data}");
return Task.CompletedTask;
}
}
@ -757,15 +719,17 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
if (E.Target.Level >= E.Origin.Level)
var flagEvent = E.Target.Flag(E.Data, E.Origin);
if (E.FailReason == GameEvent.EventFailReason.Permission)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}");
return;
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_FAIL"]} ^5{E.Target.Name}");
}
if (E.Target.Level == Player.Permission.Flagged)
else if (E.FailReason == GameEvent.EventFailReason.Invalid)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}");
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_ALREADYFLAGGED"]}");
}
else
@ -786,17 +750,7 @@ namespace SharedLibraryCore.Commands
await E.Owner.Manager.GetPenaltyService().Create(newPenalty);
var e = new GameEvent()
{
Type = GameEvent.EventType.Flag,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_SUCCESS"]} ^5{E.Target.Name}");
}
}
@ -815,36 +769,27 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
if (E.Target.Level >= E.Origin.Level)
var unflagEvent = E.Target.Unflag(E.Data, E.Origin);
if (unflagEvent.FailReason == GameEvent.EventFailReason.Permission)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"]} ^5{E.Target.Name}");
return;
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_FAIL"]} ^5{E.Target.Name}");
}
if (E.Target.Level == Player.Permission.Flagged)
else if (unflagEvent.FailReason == GameEvent.EventFailReason.Invalid)
{
E.Target.Level = Player.Permission.User;
await E.Owner.Manager.GetClientService().Update(E.Target);
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
var e = new GameEvent()
{
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner,
Type = GameEvent.EventType.Unflag
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_FLAG_UNFLAG"]} ^5{E.Target.Name}");
}
else
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNFLAG_NOTFLAGGED"]);
}
return Task.CompletedTask;
// todo: update immediately?
}
}
@ -867,61 +812,52 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override async Task ExecuteAsync(GameEvent commandEvent)
{
if (E.Data.ToLower().Contains("camp"))
if (commandEvent.Data.ToLower().Contains("camp"))
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_CAMP"]);
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_CAMP"]);
return;
}
if (E.Owner.Reports.Find(x => (x.Origin == E.Origin && x.Target.NetworkId == E.Target.NetworkId)) != null)
var reportEvent = commandEvent.Target.Report(commandEvent.Data, commandEvent.Origin);
if (reportEvent.FailReason == GameEvent.EventFailReason.Permission)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
return;
commandEvent.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"]} {commandEvent.Target.Name}");
}
if (E.Target == E.Origin)
else if (reportEvent.FailReason == GameEvent.EventFailReason.Invalid)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
return;
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_SELF"]);
}
if (E.Target.Level > E.Origin.Level)
else if (reportEvent.Failed)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL"]} {E.Target.Name}");
return;
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_FAIL_DUPLICATE"]);
}
E.Owner.Reports.Add(new Report(E.Target, E.Origin, E.Data));
Penalty newReport = new Penalty()
else
{
Type = Penalty.PenaltyType.Report,
Expires = DateTime.UtcNow,
Offender = E.Target,
Offense = E.Data,
Punisher = E.Origin,
Active = true,
When = DateTime.UtcNow,
Link = E.Target.AliasLink
};
commandEvent.Owner.Reports.Add(new Report(commandEvent.Target, commandEvent.Origin, commandEvent.Data));
await E.Owner.Manager.GetPenaltyService().Create(newReport);
Penalty newReport = new Penalty()
{
Type = Penalty.PenaltyType.Report,
Expires = DateTime.UtcNow,
Offender = commandEvent.Target,
Offense = commandEvent.Data,
Punisher = commandEvent.Origin,
Active = true,
When = DateTime.UtcNow,
Link = commandEvent.Target.AliasLink
};
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
await commandEvent.Owner.Manager.GetPenaltyService().Create(newReport);
var e = new GameEvent()
{
Type = GameEvent.EventType.Report,
Data = E.Data,
Origin = E.Origin,
Target = E.Target,
Owner = E.Owner
};
E.Owner.Manager.GetEventHandler().AddEvent(e);
await E.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", E.Origin.Name, E.Target.Name, E.Data));
commandEvent.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORT_SUCCESS"]);
commandEvent.Owner.ToAdmins(String.Format("^5{0}^7->^1{1}^7: {2}", commandEvent.Origin.Name, commandEvent.Target.Name, commandEvent.Data));
}
}
}
@ -938,26 +874,27 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
if (E.Data != null && E.Data.ToLower().Contains(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_CLEAR"]))
{
E.Owner.Reports = new List<Report>();
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_CLEAR_SUCCESS"]);
return;
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_CLEAR_SUCCESS"]);
return Task.CompletedTask;
}
if (E.Owner.Reports.Count < 1)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_NONE"]);
return;
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_REPORTS_NONE"]);
return Task.CompletedTask;
}
foreach (Report R in E.Owner.Reports)
{
await E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason));
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell(String.Format("^5{0}^7->^1{1}^7: {2}", R.Origin.Name, R.Target.Name, R.Reason));
}
return Task.CompletedTask;
}
}
@ -972,12 +909,12 @@ namespace SharedLibraryCore.Commands
if (E.Origin.Masked)
{
E.Origin.Masked = false;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_OFF"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_OFF"]);
}
else
{
E.Origin.Masked = true;
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_ON"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_MASK_ON"]);
}
await E.Owner.Manager.GetClientService().Update(E.Origin);
@ -1005,16 +942,15 @@ namespace SharedLibraryCore.Commands
if (penalty == null)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_NONE"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_NONE"]);
return;
}
string timeRemaining = penalty.Type == Penalty.PenaltyType.TempBan ? $"({(penalty.Expires - DateTime.UtcNow).TimeSpanText()} remaining)" : "";
string success = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_BANINFO_SUCCESS"];
await E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}");
E.Origin.Tell($"^1{E.Target.Name} ^7{string.Format(success, penalty.Punisher.Name)} {penalty.Punisher.Name} {timeRemaining}");
}
}
public class CListAlias : Command
@ -1030,22 +966,24 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
StringBuilder message = new StringBuilder();
var names = new List<string>(E.Target.AliasLink.Children.Select(a => a.Name));
var IPs = new List<string>(E.Target.AliasLink.Children.Select(a => a.IPAddress.ConvertIPtoString()).Distinct());
await E.Target.Tell($"[^3{E.Target}^7]");
E.Target.Tell($"[^3{E.Target}^7]");
message.Append($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_ALIASES"]}: ");
message.Append(String.Join(" | ", names));
await E.Origin.Tell(message.ToString());
E.Origin.Tell(message.ToString());
message.Clear();
message.Append($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ALIAS_IPS"]}: ");
message.Append(String.Join(" | ", IPs));
await E.Origin.Tell(message.ToString());
E.Origin.Tell(message.ToString());
return Task.CompletedTask;
}
}
@ -1066,9 +1004,9 @@ namespace SharedLibraryCore.Commands
{
var Response = await E.Owner.ExecuteCommandAsync(E.Data.Trim());
foreach (string S in Response)
await E.Origin.Tell(S.StripColors());
E.Origin.Tell(S.StripColors());
if (Response.Length == 0)
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_SUCCESS"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_RCON_SUCCESS"]);
}
}
@ -1078,14 +1016,14 @@ namespace SharedLibraryCore.Commands
base("plugins", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_DESC"], "p", Player.Permission.Administrator, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_LOADED"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PLUGINS_LOADED"]);
foreach (var P in Plugins.PluginImporter.ActivePlugins)
{
await E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
await Task.Delay(FloodProtectionInterval);
E.Origin.Tell(String.Format("^3{0} ^7[v^3{1}^7] by ^5{2}^7", P.Name, P.Version, P.Author));
}
return Task.CompletedTask;
}
}
@ -1095,9 +1033,10 @@ namespace SharedLibraryCore.Commands
base("getexternalip", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_DESC"], "ip", Player.Permission.User, false)
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"]} ^5{E.Origin.IPAddressString}");
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_IP_SUCCESS"]} ^5{E.Origin.IPAddressString}");
return Task.CompletedTask;
}
}
@ -1129,12 +1068,12 @@ namespace SharedLibraryCore.Commands
catch (FormatException)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_FAIL"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_FAIL"]);
return;
}
List<EFClient> inactiveUsers = null;
// todo: make an event for this
// update user roles
using (var context = new DatabaseContext())
{
@ -1146,8 +1085,7 @@ namespace SharedLibraryCore.Commands
inactiveUsers.ForEach(c => c.Level = Player.Permission.User);
await context.SaveChangesAsync();
}
await E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}");
E.Origin.Tell($"^5{inactiveUsers.Count} ^7{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PRUNE_SUCCESS"]}");
}
}
@ -1167,7 +1105,7 @@ namespace SharedLibraryCore.Commands
{
if (E.Data.Length < 5)
{
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_FAIL"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_FAIL"]);
return;
}
@ -1181,7 +1119,7 @@ namespace SharedLibraryCore.Commands
E.Owner.Manager.GetPrivilegedClients()[E.Origin.ClientId].PasswordSalt = hashedPassword[1];
await E.Owner.Manager.GetClientService().Update(E.Origin);
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PASSWORD_SUCCESS"]);
}
}
@ -1212,7 +1150,7 @@ namespace SharedLibraryCore.Commands
if (currentProcess == null)
{
await E.Origin.Tell("Could not find running/stalled instance of IW4x");
E.Origin.Tell("Could not find running/stalled instance of IW4x");
}
else
@ -1230,7 +1168,7 @@ namespace SharedLibraryCore.Commands
catch (Exceptions.NetworkException)
{
await E.Origin.Tell("Unable to cleanly shutdown server, forcing");
E.Origin.Tell("Unable to cleanly shutdown server, forcing");
}
if (!currentProcess.HasExited)
@ -1238,11 +1176,11 @@ namespace SharedLibraryCore.Commands
try
{
currentProcess.Kill();
await E.Origin.Tell("Successfully killed server process");
E.Origin.Tell("Successfully killed server process");
}
catch (Exception e)
{
await E.Origin.Tell("Could not kill server process");
E.Origin.Tell("Could not kill server process");
E.Owner.Logger.WriteDebug("Unable to kill process");
E.Owner.Logger.WriteDebug($"Exception: {e.Message}");
return;
@ -1265,22 +1203,24 @@ namespace SharedLibraryCore.Commands
})
{ }
public override async Task ExecuteAsync(GameEvent E)
public override Task ExecuteAsync(GameEvent E)
{
if (E.Message.IsBroadcastCommand())
{
if (E.Target == null)
await E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms");
E.Owner.Broadcast($"{E.Origin.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Origin.Ping}^7ms");
else
await E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
E.Owner.Broadcast($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
}
else
{
if (E.Target == null)
await E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms");
E.Origin.Tell($"{Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_SELF"]} ^5{E.Origin.Ping}^7ms");
else
await E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
E.Origin.Tell($"{E.Target.Name}'s {Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_PING_TARGET"]} ^5{E.Target.Ping}^7ms");
}
return Task.CompletedTask;
}
}
@ -1323,7 +1263,7 @@ namespace SharedLibraryCore.Commands
ctx.EFMeta.Add(gravatarMeta);
await ctx.SaveChangesAsync();
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_NEW"]);
return;
}
}
@ -1339,7 +1279,7 @@ namespace SharedLibraryCore.Commands
}
await ctx.SaveChangesAsync();
await E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_UPDATE"]);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GRAVATAR_SUCCESS_UPDATE"]);
}
}
}
@ -1402,7 +1342,7 @@ namespace SharedLibraryCore.Commands
public override async Task ExecuteAsync(GameEvent E)
{
await E.Origin.Tell(await GetNextMap(E.Owner));
E.Origin.Tell(await GetNextMap(E.Owner));
}
}
}

View File

@ -7,6 +7,28 @@ namespace SharedLibraryCore
{
public class GameEvent
{
public enum EventFailReason
{
/// <summary>
/// event execution did not fail
/// </summary>
None,
/// <summary>
/// an internal exception prevented the event
/// from executing
/// </summary>
Exception,
/// <summary>
/// event origin didn't have the necessary privileges
/// to execute the command
/// </summary>
Permission,
/// <summary>
/// executing the event would cause an invalid state
/// </summary>
Invalid
}
public enum EventType
{
/// <summary>
@ -52,75 +74,83 @@ namespace SharedLibraryCore
/// <summary>
/// a client sent a message
/// </summary>
Say,
Say = 100,
/// <summary>
/// a client was warned
/// </summary>
Warn,
Warn = 101,
/// <summary>
/// all warnings for a client were cleared
/// </summary>
WarnClear = 102,
/// <summary>
/// a client was reported
/// </summary>
Report,
Report = 103,
/// <summary>
/// a client was flagged
/// </summary>
Flag,
Flag = 104,
/// <summary>
/// a client was unflagged
/// </summary>
Unflag,
Unflag = 105,
/// <summary>
/// a client was kicked
/// </summary>
Kick,
Kick = 106,
/// <summary>
/// a client was tempbanned
/// </summary>
TempBan,
TempBan = 107,
/// <summary>
/// a client was banned
/// </summary>
Ban,
Ban = 108,
/// <summary>
/// a client was unbanned
/// </summary>
Unban = 109,
/// <summary>
/// a client entered a command
/// </summary>
Command,
Command = 110,
/// <summary>
/// a client's permission was changed
/// </summary>
ChangePermission,
ChangePermission = 111,
// events "generated" by IW4MAdmin
/// <summary>
/// a message is sent to all clients
/// </summary>
Broadcast,
Broadcast = 200,
/// <summary>
/// a message is sent to a specific client
/// </summary>
Tell,
Tell = 201,
// events "generated" by script/log
/// <summary>
/// AC Damage Log
/// </summary>
ScriptDamage,
ScriptDamage = 300,
/// <summary>
/// AC Kill Log
/// </summary>
ScriptKill,
ScriptKill = 301,
/// <summary>
/// damage info printed out by game script
/// </summary>
Damage,
Damage = 302,
/// <summary>
/// kill info printed out by game script
/// </summary>
Kill,
Kill = 303,
/// <summary>
/// team info printed out by game script
/// </summary>
JoinTeam,
JoinTeam = 304,
}
static long NextEventId;
@ -144,12 +174,18 @@ namespace SharedLibraryCore
public ManualResetEventSlim OnProcessed { get; set; }
public DateTime Time { get; set; }
public long Id { get; private set; }
public EventFailReason FailReason { get; set; }
public bool Failed => FailReason != EventFailReason.None;
/// <summary>
/// asynchronously wait for GameEvent to be processed
/// </summary>
/// <returns>waitable task </returns>
public Task<bool> WaitAsync(int timeOut = int.MaxValue) => Task.FromResult(OnProcessed.Wait(timeOut));
public Task<bool> WaitAsync(int timeOut = int.MaxValue) => Task.Run(() =>
{
OnProcessed.Wait(timeOut);
return !Failed;
});
/// <summary>
/// determine whether an event should be delayed or not

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
@ -9,5 +10,9 @@ namespace SharedLibraryCore.Exceptions
public class NetworkException : ServerException
{
public NetworkException(string msg) : base(msg) { }
public NetworkException(string msg, Socket s) : base(msg)
{
this.Data.Add("socket", s);
}
}
}

View File

@ -91,12 +91,12 @@ namespace SharedLibraryCore.Migrations
principalColumn: "SnapshotId",
onDelete: ReferentialAction.Restrict);
}
migrationBuilder.CreateIndex(
name: "IX_Vector3_EFACSnapshotSnapshotId",
table: "Vector3",
column: "EFACSnapshotSnapshotId");
migrationBuilder.CreateIndex(
name: "IX_Vector3_EFACSnapshotSnapshotId",
table: "Vector3",
column: "EFACSnapshotSnapshotId");
}
migrationBuilder.CreateIndex(
name: "IX_EFACSnapshot_ClientId",

View File

@ -86,53 +86,302 @@ namespace SharedLibraryCore.Objects
public override string ToString() => $"{Name}::{NetworkId}";
public String GetLastConnection()
/// <summary>
/// send a message directly to the connected client
/// </summary>
/// <param name="message">message content to send to client</param>
public GameEvent Tell(String message)
{
return Utilities.GetTimePassed(LastConnection);
var e = new GameEvent()
{
Message = message,
Target = this,
Owner = CurrentServer,
Type = GameEvent.EventType.Tell,
Data = message
};
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
public async Task Tell(String Message)
/// <summary>
/// warn a client with given reason
/// </summary>
/// <param name="warnReason">reason for warn</param>
/// <param name="sender">client performing the warn</param>
public GameEvent Warn(String warnReason, Player sender)
{
// this is console or remote so send immediately
if (ClientNumber < 0)
var e = new GameEvent()
{
await CurrentServer.Tell(Message, this);
Type = GameEvent.EventType.Warn,
Message = warnReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
else
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// report a client for a given reason
/// </summary>
/// <param name="reportReason">reason for the report</param>
/// <param name="sender">client performing the report</param>
/// <returns></returns>
public GameEvent Report(string reportReason, Player sender)
{
var e = new GameEvent()
{
var e = new GameEvent()
{
Message = Message,
Target = this,
Owner = CurrentServer,
Type = GameEvent.EventType.Tell,
Data = Message
};
Type = GameEvent.EventType.Report,
Message = reportReason,
Data = reportReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
};
CurrentServer.Manager.GetEventHandler().AddEvent(e);
if (this.Level < sender.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this == sender)
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
if (CurrentServer.Reports.Count(rep => (rep.Origin == sender &&
rep.Target.NetworkId == this.NetworkId)) > 0)
{
e.FailReason = GameEvent.EventFailReason.Exception;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
public async Task Kick(String Message, Player Sender)
/// <summary>
/// clear all warnings for a client
/// </summary>
/// <param name="sender">client performing the warn clear</param>
/// <returns></returns>
public GameEvent WarnClear(Player sender)
{
await CurrentServer.Kick(Message, this, 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);
return e;
}
public async Task TempBan(String Message, TimeSpan Length, Player Sender)
/// <summary>
/// flag a client for a given reason
/// </summary>
/// <param name="flagReason">reason for flagging</param>
/// <param name="sender">client performing the flag</param>
/// <returns>game event for the flag</returns>
public GameEvent Flag(string flagReason, Player sender)
{
await CurrentServer.TempBan(Message, Length, this, Sender);
var e = new GameEvent()
{
Type = GameEvent.EventType.Flag,
Origin = sender,
Data = flagReason,
Message = flagReason,
Owner = this.CurrentServer
};
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this.Level == Player.Permission.Flagged)
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
public async Task Warn(String Message, Player Sender)
/// <summary>
/// unflag a client for a given reason
/// </summary>
/// <param name="unflagReason">reason to unflag a player for</param>
/// <param name="sender">client performing the unflag</param>
/// <returns>game event for the un flug</returns>
public GameEvent Unflag(string unflagReason, Player sender)
{
await CurrentServer.Warn(Message, this, Sender);
var e = new GameEvent()
{
Type = GameEvent.EventType.Unflag,
Origin = sender,
Data = unflagReason,
Message = unflagReason,
Owner = this.CurrentServer
};
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
if (this.Level != Player.Permission.Flagged)
{
e.FailReason = GameEvent.EventFailReason.Invalid;
return e;
}
this.Level = Permission.User;
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
public async Task Ban(String Message, Player Sender)
/// <summary>
/// kick a client for the given reason
/// </summary>
/// <param name="kickReason">reason to kick for</param>
/// <param name="sender">client performing the kick</param>
public GameEvent Kick(String kickReason, Player sender)
{
await CurrentServer.Ban(Message, this, Sender);
var e = new GameEvent()
{
Type = GameEvent.EventType.Kick,
Message = kickReason,
Target = this,
Origin = sender,
Data = kickReason,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// temporarily ban a client for the given time span
/// </summary>
/// <param name="tempbanReason">reason for the temp ban</param>
/// <param name="banLength">how long the temp ban lasts</param>
/// <param name="sender">client performing the tempban</param>
public GameEvent TempBan(String tempbanReason, TimeSpan banLength, Player sender)
{
var e = new GameEvent()
{
Type = GameEvent.EventType.TempBan,
Message = tempbanReason,
Origin = sender,
Target = this,
Extra = banLength,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// permanently ban a client
/// </summary>
/// <param name="banReason">reason for the ban</param>
/// <param name="sender">client performing the ban</param>
public GameEvent Ban(String banReason, Player sender)
{
var e = new GameEvent()
{
Type = GameEvent.EventType.Ban,
Message = banReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
/// <summary>
/// unban a client
/// </summary>
/// <param name="unbanReason">reason for the unban</param>
/// <param name="sender">client performing the unban</param>
/// <returns></returns>
public GameEvent Unban(String unbanReason, Player sender)
{
var e = new GameEvent()
{
Type = GameEvent.EventType.Unban,
Message = unbanReason,
Data = unbanReason,
Origin = sender,
Target = this,
Owner = this.CurrentServer
};
// enforce level restrictions
if (sender.Level <= this.Level)
{
e.FailReason = GameEvent.EventFailReason.Permission;
return e;
}
CurrentServer.Manager.GetEventHandler().AddEvent(e);
return e;
}
[NotMapped]
Dictionary<string, object> _additionalProperties;
public T GetAdditionalProperty<T>(string name) => (T)_additionalProperties[name];

View File

@ -13,145 +13,44 @@ namespace SharedLibraryCore.RCon
{
class ConnectionState
{
public Socket Client { get; private set; }
public int BufferSize { get; private set; }
public byte[] Buffer { get; private set; }
public StringBuilder ResponseString { get; }
public ConnectionState(Socket cl)
{
BufferSize = 8192;
Buffer = new byte[BufferSize];
Client = cl;
ResponseString = new StringBuilder();
}
public int ConnectionAttempts { get; set; }
const int BufferSize = 4096;
public readonly byte[] ReceiveBuffer = new byte[BufferSize];
public readonly SemaphoreSlim OnComplete = new SemaphoreSlim(1, 1);
}
public class Connection
{
static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>();
public IPEndPoint Endpoint { get; private set; }
public string RConPassword { get; private set; }
ILogger Log;
int FailedSends;
int FailedReceives;
string response;
ManualResetEvent OnConnected;
ManualResetEvent OnSent;
ManualResetEvent OnReceived;
public Connection(string ipAddress, int port, string password, ILogger log)
{
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
RConPassword = password;
Log = log;
OnConnected = new ManualResetEvent(false);
OnSent = new ManualResetEvent(false);
OnReceived = new ManualResetEvent(false);
}
private void OnConnectedCallback(IAsyncResult ar)
{
var serverSocket = (Socket)ar.AsyncState;
try
{
serverSocket.EndConnect(ar);
#if DEBUG
Log.WriteDebug($"Successfully initialized socket to {serverSocket.RemoteEndPoint}");
#endif
OnConnected.Set();
}
catch (SocketException e)
{
throw new NetworkException($"Could not initialize socket for RCon - {e.Message}");
}
}
private void OnSentCallback(IAsyncResult ar)
{
Socket serverConnection = (Socket)ar.AsyncState;
try
{
int sentByteNum = serverConnection.EndSend(ar);
#if DEBUG
Log.WriteDebug($"Sent {sentByteNum} bytes to {serverConnection.RemoteEndPoint}");
#endif
// this is where we override our await to make it
OnSent.Set();
}
catch (Exception)
{
}
}
private void OnReceivedCallback(IAsyncResult ar)
{
var connectionState = (ConnectionState)ar.AsyncState;
var serverConnection = connectionState.Client;
try
{
int bytesRead = serverConnection.EndReceive(ar);
if (bytesRead > 0)
{
#if DEBUG
Log.WriteDebug($"Received {bytesRead} bytes from {serverConnection.RemoteEndPoint}");
#endif
FailedReceives = 0;
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");
/* if (FailedReceives == 0 && serverConnection.Available > 0)
{
serverConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
new AsyncCallback(OnReceivedCallback), connectionState);
}
else*/
{
response = connectionState.ResponseString.ToString();
OnReceived.Set();
}
}
else
{
response = connectionState.ResponseString.ToString();
OnReceived.Set();
}
}
catch (SocketException)
{
}
catch (ObjectDisposedException)
{
// Log.WriteWarning($"Tried to check for more available bytes for disposed socket on {Endpoint}");
}
}
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "", bool waitForResponse = true)
{
//// will this really prevent flooding?
//if ((DateTime.Now - LastQuery).TotalMilliseconds < 350)
//{
// Thread.Sleep(350);
// //await Task.Delay(350);
//}
if (!ActiveQueries.ContainsKey(this.Endpoint))
{
ActiveQueries.TryAdd(this.Endpoint, new ConnectionState());
}
// LastQuery = DateTime.Now;
var connectionState = ActiveQueries[this.Endpoint];
#if DEBUG == true
Log.WriteDebug($"Waiting for semaphore to be released [${this.Endpoint}]");
#endif
await connectionState.OnComplete.WaitAsync();
#if DEBUG == true
Log.WriteDebug($"Semaphore has been released [${this.Endpoint}]");
#endif
OnSent.Reset();
OnReceived.Reset();
byte[] payload = null;
switch (type)
@ -171,130 +70,103 @@ namespace SharedLibraryCore.RCon
break;
}
using (var socketConnection = new Socket(Endpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp) { ExclusiveAddressUse = true } )
byte[] response = null;
retrySend:
#if DEBUG == true
Log.WriteDebug($"Sending {payload.Length} bytes to [${this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})");
#endif
try
{
socketConnection.BeginConnect(Endpoint, new AsyncCallback(OnConnectedCallback), socketConnection);
retrySend:
try
{
#if DEBUG
Console.WriteLine($"Sending Command {parameters}");
#endif
if (!OnConnected.WaitOne(StaticHelpers.SocketTimeout))
throw new SocketException((int)SocketError.TimedOut);
socketConnection.BeginSend(payload, 0, payload.Length, 0, new AsyncCallback(OnSentCallback), socketConnection);
bool success = await Task.FromResult(OnSent.WaitOne(StaticHelpers.SocketTimeout));
if (!success)
{
FailedSends++;
#if DEBUG
Log.WriteDebug($"{FailedSends} failed sends to {socketConnection.RemoteEndPoint.ToString()}");
#endif
if (FailedSends < 4)
goto retrySend;
else if (FailedSends == 4)
Log.WriteError($"Failed to send data to {socketConnection.RemoteEndPoint}");
}
else
{
if (FailedSends >= 4)
{
Log.WriteVerbose($"Resumed send RCon connection with {socketConnection.RemoteEndPoint}");
FailedSends = 0;
}
}
}
catch (SocketException e)
{
// this result is normal if the server is not listening
if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
e.NativeErrorCode != (int)SocketError.TimedOut)
throw new NetworkException($"Unexpected error while sending data to server - {e.Message}");
}
if (!waitForResponse)
return await Task.FromResult(new string[] { "" });
var connectionState = new ConnectionState(socketConnection);
retryReceive:
try
{
socketConnection.BeginReceive(connectionState.Buffer, 0, connectionState.Buffer.Length, 0,
new AsyncCallback(OnReceivedCallback), connectionState);
bool success = await Task.FromResult(OnReceived.WaitOne(StaticHelpers.SocketTimeout));
if (!success)
{
FailedReceives++;
#if DEBUG
Log.WriteDebug($"{FailedReceives} failed receives from {socketConnection.RemoteEndPoint.ToString()}");
#endif
if (FailedReceives < 4)
goto retrySend;
else if (FailedReceives == 4)
{
// Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries");
}
if (FailedReceives >= 4)
{
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} {socketConnection.RemoteEndPoint.ToString()}");
}
}
else
{
if (FailedReceives >= 4)
{
Log.WriteVerbose($"Resumed receive RCon connection from {socketConnection.RemoteEndPoint.ToString()}");
FailedReceives = 0;
}
}
}
catch (SocketException e)
{
// this result is normal if the server is not listening
if (e.NativeErrorCode != (int)SocketError.ConnectionReset &&
e.NativeErrorCode != (int)SocketError.TimedOut)
throw new NetworkException($"Unexpected error while receiving data from server - {e.Message}");
else if (FailedReceives < 4)
{
goto retryReceive;
}
else if (FailedReceives == 4)
{
// Log.WriteError($"Failed to receive data from {socketConnection.RemoteEndPoint} after {FailedReceives} tries");
}
if (FailedReceives >= 4)
{
throw new NetworkException(e.Message);
}
}
string queryResponse = response;
if (queryResponse.Contains("Invalid password"))
throw new NetworkException("RCON password is invalid");
if (queryResponse.ToString().Contains("rcon_password"))
throw new NetworkException("RCON password has not been set");
string[] splitResponse = queryResponse.Split(new char[]
{
'\n'
}, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim()).ToArray();
return splitResponse;
response = await SendPayloadAsync(payload);
}
catch (Exception ex)
{
if(connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
{
connectionState.ConnectionAttempts++;
Log.WriteWarning($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}] ({connectionState.ConnectionAttempts++}/{StaticHelpers.AllowedConnectionFails})");
await Task.Delay(StaticHelpers.SocketTimeout.Milliseconds);
goto retrySend;
}
ActiveQueries.TryRemove(this.Endpoint, out _);
Log.WriteDebug(ex.GetExceptionInfo());
throw new NetworkException($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"]} [{this.Endpoint}]");
}
ActiveQueries.TryRemove(this.Endpoint, out _);
string responseString = Utilities.EncodingType.GetString(response, 0, response.Length).TrimEnd('\0') + '\n';
if (responseString.Contains("Invalid password"))
{
// todo: localize this
throw new NetworkException("RCON password is invalid");
}
if (responseString.ToString().Contains("rcon_password"))
{
throw new NetworkException("RCON password has not been set");
}
string[] splitResponse = responseString.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
.Select(line => line.Trim()).ToArray();
return splitResponse;
}
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 outgoingDataArgs = new SocketAsyncEventArgs()
{
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))
{
throw new NetworkException("Timed out waiting for response", rconSocket);
}
byte[] response = connectionState.ReceiveBuffer;
return response;
}
private void OnDataReceived(object sender, SocketAsyncEventArgs e)
{
#if DEBUG == true
Log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}");
#endif
ActiveQueries[this.Endpoint].OnComplete.Release(1);
}
private void OnDataSent(object sender, SocketAsyncEventArgs e)
{
#if DEBUG == true
Log.WriteDebug($"Sent {e.Buffer.Length} bytes to {e.ConnectSocket.RemoteEndPoint.ToString()}");
#endif
}
}
}

View File

@ -39,10 +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, 10);
public static readonly TimeSpan SocketTimeout = new TimeSpan(0, 0, 0, 0, 150);
/// <summary>
/// interval in milliseconds to wait before sending the next RCon request
/// </summary>
public static readonly int FloodProtectionInterval = 350;
public static readonly int AllowedConnectionFails = 3;
}
}

View File

@ -113,28 +113,28 @@ namespace SharedLibraryCore
/// <summary>
/// Send a message to all players
/// </summary>
/// <param name="Message">Message to be sent to all players</param>
public async Task Broadcast(String Message)
/// <param name="message">Message to be sent to all players</param>
public GameEvent Broadcast(string message, Player sender = null)
{
#if DEBUG == false
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}");
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Say, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{message}");
#else
Logger.WriteVerbose(Message.StripColors());
Logger.WriteVerbose(message.StripColors());
#endif
var e = new GameEvent()
{
Type = GameEvent.EventType.Broadcast,
#if DEBUG == true
Data = Message,
Data = message,
#else
Data = formattedMessage,
#endif
Owner = this,
Origin = sender,
};
Manager.GetEventHandler().AddEvent(e);
await Task.CompletedTask;
return e;
}
/// <summary>
@ -142,7 +142,7 @@ namespace SharedLibraryCore
/// </summary>
/// <param name="Message">Message to send</param>
/// <param name="Target">Player to send message to</param>
public async Task Tell(String Message, Player Target)
protected async Task Tell(String Message, Player Target)
{
#if !DEBUG
string formattedMessage = String.Format(RconParser.GetCommandPrefixes().Tell, Target.ClientNumber, $"{(CustomSayEnabled ? $"{CustomSayName}: " : "")}{Message}");
@ -179,11 +179,11 @@ namespace SharedLibraryCore
/// Send a message to all admins on the server
/// </summary>
/// <param name="message">Message to send out</param>
public async Task ToAdmins(String message)
public void ToAdmins(String message)
{
foreach (var client in GetPlayersAsList().Where(c => c.Level > Player.Permission.Flagged))
{
await client.Tell(message);
client.Tell(message);
}
}

View File

@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Services
@ -14,6 +13,14 @@ namespace SharedLibraryCore.Services
{
private DatabaseContext _context;
private DbSet<TEntity> _dbSet;
private readonly bool ShouldTrack;
public GenericRepository(bool shouldTrack)
{
this.ShouldTrack = shouldTrack;
}
public GenericRepository() { }
protected DbContext Context
{
@ -21,7 +28,7 @@ namespace SharedLibraryCore.Services
{
if (_context == null)
{
_context = new DatabaseContext(true);
_context = new DatabaseContext(ShouldTrack);
}
return _context;