clean up rcon, fix a bunch of little things
This commit is contained in:
parent
5d93e7ac57
commit
d45729d7e1
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@ -264,7 +263,7 @@ namespace IW4MAdmin
|
||||
if (cNum >= 0 && Players[cNum] != null)
|
||||
{
|
||||
Player Leaving = Players[cNum];
|
||||
|
||||
|
||||
// occurs when the player disconnects via log before being authenticated by RCon
|
||||
if (Leaving.State != Player.ClientState.Connected)
|
||||
{
|
||||
@ -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;
|
||||
@ -778,7 +782,7 @@ namespace IW4MAdmin
|
||||
Logger.WriteWarning("Game log file not properly initialized, restarting map...");
|
||||
await this.ExecuteCommandAsync("map_restart");
|
||||
logfile = await this.GetDvarAsync<string>("g_log");
|
||||
}
|
||||
}
|
||||
|
||||
CustomCallback = await ScriptLoaded();
|
||||
string mainPath = EventParser.GetGameDir();
|
||||
@ -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()
|
||||
|
@ -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" />
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
6
Master/master/resources/server.py
Normal file
6
Master/master/resources/server.py
Normal file
@ -0,0 +1,6 @@
|
||||
from flask_restful import Resource
|
||||
|
||||
class Server(Resource):
|
||||
"""description of class"""
|
||||
|
||||
|
@ -6,10 +6,12 @@ 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>')
|
||||
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(Localization, '/localization/', '/localization/<string:language_tag>')
|
||||
api.add_resource(Server, '/server')
|
@ -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,
|
||||
|
@ -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>
|
||||
|
113
Master/master/templates/serverlist.html
Normal file
113
Master/master/templates/serverlist.html
Normal 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">×</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 %}
|
@ -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
|
||||
)
|
||||
|
@ -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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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}\"");
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user