Beispiel #1
0
        public override async Task ExecuteEvent(GameEvent E)
        {
            //if (Throttled)
            //   return;

            await ProcessEvent(E);

            Manager.GetEventApi().OnServerEvent(this, E);

            foreach (IPlugin P in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
            {
#if !DEBUG
                try
#endif
                {
                    if (cts.IsCancellationRequested)
                    {
                        break;
                    }

                    await P.OnEventAsync(E, this);
                }
#if !DEBUG
                catch (Exception Except)
                {
                    Logger.WriteError(String.Format("The plugin \"{0}\" generated an error. ( see log )", P.Name));
                    Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
                    Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
                    while (Except.InnerException != null)
                    {
                        Except = Except.InnerException;
                        Logger.WriteDebug($"Inner exception: {Except.Message}");
                    }
                    continue;
                }
#endif
            }
        }
Beispiel #2
0
        //Another version of client from line, written for the line created by a kill or death event
        override public Player ParseClientFromString(String[] L, int cIDPos)
        {
            if (L.Length < cIDPos)
            {
                Logger.WriteError("Line sent for client creation is not long enough!");
                return null;
            }

            int pID = -2; // apparently falling = -1 cID so i can't use it now
            int.TryParse(L[cIDPos].Trim(), out pID);

            if (pID == -1) // special case similar to mod_suicide
                int.TryParse(L[2], out pID);

            if (pID < 0 || pID > 17)
            {
                Logger.WriteError("Event player index " + pID + " is out of bounds!");
                Logger.WriteDebug("Offending line -- " + String.Join(";", L));
                return null;
            }

            return Players[pID];
        }
Beispiel #3
0
        //Process any server event
        override protected async Task ProcessEvent(GameEvent E)
        {
            if (E.Type == GameEvent.EventType.Connect)
            {
                // special case for IW5 when connect is from the log
                if (E.Extra != null)
                {
                    var logClient = (Player)E.Extra;
                    var client    = (await this.GetStatusAsync())
                                    .Single(c => c.ClientNumber == logClient.ClientNumber &&
                                            c.Name == logClient.Name);
                    client.NetworkId = logClient.NetworkId;

                    await AddPlayer(client);

                    // hack: to prevent plugins from registering it as a real connect
                    E.Type = GameEvent.EventType.Unknown;
                }

                else
                {
                    ChatHistory.Add(new ChatInfo()
                    {
                        Name    = E.Origin.Name,
                        Message = "CONNECTED",
                        Time    = DateTime.UtcNow
                    });

                    if (E.Origin.Level > Player.Permission.Moderator)
                    {
                        await E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
                    }
                }
            }

            else if (E.Type == GameEvent.EventType.Disconnect)
            {
                ChatHistory.Add(new ChatInfo()
                {
                    Name    = E.Origin.Name,
                    Message = "DISCONNECTED",
                    Time    = DateTime.UtcNow
                });
            }

            else if (E.Type == GameEvent.EventType.Script)
            {
                await ExecuteEvent(new GameEvent(GameEvent.EventType.Kill, E.Data, E.Origin, E.Target, this));
            }

            if (E.Type == GameEvent.EventType.Say && E.Data.Length >= 2)
            {
                if (E.Data.Substring(0, 1) == "!" || E.Data.Substring(0, 1) == "@" || E.Origin.Level == Player.Permission.Console)
                {
                    Command C = null;

                    try
                    {
                        C = await ValidateCommand(E);
                    }

                    catch (CommandException e)
                    {
                        Logger.WriteInfo(e.Message);
                    }

                    if (C != null)
                    {
                        if (C.RequiresTarget && E.Target == null)
                        {
                            Logger.WriteWarning("Requested event (command) requiring target does not have a target!");
                        }

                        try
                        {
                            if (!E.Remote && E.Origin.Level != Player.Permission.Console)
                            {
                                await ExecuteEvent(new GameEvent()
                                {
                                    Type   = GameEvent.EventType.Command,
                                    Data   = string.Empty,
                                    Origin = E.Origin,
                                    Target = E.Target,
                                    Owner  = this,
                                    Extra  = C,
                                    Remote = E.Remote
                                });
                            }

                            await C.ExecuteAsync(E);
                        }

                        catch (AuthorizationException e)
                        {
                            await E.Origin.Tell($"{loc["COMMAND_NOTAUTHORIZED"]} - {e.Message}");
                        }

                        catch (Exception Except)
                        {
                            Logger.WriteError(String.Format("A command request \"{0}\" generated an error.", C.Name));
                            Logger.WriteDebug(String.Format("Error Message: {0}", Except.Message));
                            Logger.WriteDebug(String.Format("Error Trace: {0}", Except.StackTrace));
                            await E.Origin.Tell("^1An internal error occured while processing your command^7");

#if DEBUG
                            await E.Origin.Tell(Except.Message);
#endif
                        }
                    }
                }

                else // Not a command
                {
                    E.Data = E.Data.StripColors();

                    ChatHistory.Add(new ChatInfo()
                    {
                        Name    = E.Origin.Name,
                        Message = E.Data,
                        Time    = DateTime.UtcNow
                    });
                }
            }

            if (E.Type == GameEvent.EventType.MapChange)
            {
                Logger.WriteInfo($"New map loaded - {ClientNum} active players");

                var dict = (Dictionary <string, string>)E.Extra;
                Gametype = dict["g_gametype"].StripColors();
                Hostname = dict["sv_hostname"].StripColors();

                string mapname = dict["mapname"].StripColors();
                CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
                {
                    Alias = mapname, Name = mapname
                };
            }

            if (E.Type == GameEvent.EventType.MapEnd)
            {
                Logger.WriteInfo("Game ending...");
            }

            //todo: move
            while (ChatHistory.Count > Math.Ceiling((double)ClientNum / 2))
            {
                ChatHistory.RemoveAt(0);
            }

            // the last client hasn't fully disconnected yet
            // so there will still be at least 1 client left
            if (ClientNum < 2)
            {
                ChatHistory.Clear();
            }
        }
Beispiel #4
0
        override public async Task <bool> AddPlayer(Player polledPlayer)
        {
            if ((polledPlayer.Ping == 999 && !polledPlayer.IsBot) ||
                polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) ||
                polledPlayer.ClientNumber < 0)
            {
                //Logger.WriteDebug($"Skipping client not in connected state {P}");
                return(true);
            }

            if (Players[polledPlayer.ClientNumber] != null &&
                Players[polledPlayer.ClientNumber].NetworkId == polledPlayer.NetworkId)
            {
                // update their ping & score
                Players[polledPlayer.ClientNumber].Ping  = polledPlayer.Ping;
                Players[polledPlayer.ClientNumber].Score = polledPlayer.Score;
                return(true);
            }
#if !DEBUG
            if (polledPlayer.Name.Length < 3)
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is too short");
                string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_MINNAME"]);
                await this.ExecuteCommandAsync(formattedKick);

                return(false);
            }

            if (Players.FirstOrDefault(p => p != null && p.Name == polledPlayer.Name) != null)
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is already in use");
                string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_NAME_INUSE"]);
                await this.ExecuteCommandAsync(formattedKick);

                return(false);
            }

            if (polledPlayer.Name == "Unknown Soldier" ||
                polledPlayer.Name == "UnknownSoldier" ||
                polledPlayer.Name == "CHEATER")
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is generic");
                string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_GENERICNAME"]);
                await this.ExecuteCommandAsync(formattedKick);

                return(false);
            }

            if (polledPlayer.Name.Where(c => Char.IsControl(c)).Count() > 0)
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name contains control characters");
                string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, loc["SERVER_KICK_CONTROLCHARS"]);
                await this.ExecuteCommandAsync(formattedKick);

                return(false);
            }
#endif
            Logger.WriteDebug($"Client slot #{polledPlayer.ClientNumber} now reserved");

            try
            {
                Player player = null;
                var    client = await Manager.GetClientService().GetUnique(polledPlayer.NetworkId);

                // first time client is connecting to server
                if (client == null)
                {
                    Logger.WriteDebug($"Client {polledPlayer} first time connecting");
                    player = (await Manager.GetClientService().Create(polledPlayer)).AsPlayer();
                }

                // client has connected in the past
                else
                {
                    client.LastConnection = DateTime.UtcNow;
                    client.Connections   += 1;

                    var existingAlias = client.AliasLink.Children
                                        .FirstOrDefault(a => a.Name == polledPlayer.Name && a.IPAddress == polledPlayer.IPAddress);

                    if (existingAlias == null)
                    {
                        Logger.WriteDebug($"Client {polledPlayer} has connected previously under a different ip/name");
                        client.CurrentAlias = new EFAlias()
                        {
                            IPAddress = polledPlayer.IPAddress,
                            Name      = polledPlayer.Name,
                        };
                        // we need to update their new ip and name to the virtual property
                        client.Name      = polledPlayer.Name;
                        client.IPAddress = polledPlayer.IPAddress;

                        await Manager.GetClientService().Update(client);
                    }

                    else if (existingAlias.Name == polledPlayer.Name)
                    {
                        client.CurrentAlias   = existingAlias;
                        client.CurrentAliasId = existingAlias.AliasId;
                        await Manager.GetClientService().Update(client);
                    }
                    player = client.AsPlayer();
                }

                // Do the player specific stuff
                player.ClientNumber          = polledPlayer.ClientNumber;
                player.IsBot                 = polledPlayer.IsBot;
                player.Score                 = polledPlayer.Score;
                player.CurrentServer         = this;
                Players[player.ClientNumber] = player;

                var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(player.AliasLinkId, player.IPAddress);

                var currentBan = activePenalties.FirstOrDefault(b => b.Expires > DateTime.UtcNow);

                if (currentBan != null)
                {
                    Logger.WriteInfo($"Banned client {player} trying to connect...");
                    var autoKickClient = (await Manager.GetClientService().Get(1)).AsPlayer();
                    autoKickClient.CurrentServer = this;

                    if (currentBan.Type == Penalty.PenaltyType.TempBan)
                    {
                        string formattedKick = String.Format(RconParser.GetCommandPrefixes().Kick, polledPlayer.ClientNumber, $"{loc["SERVER_TB_REMAIN"]} ({(currentBan.Expires - DateTime.UtcNow).TimeSpanText()} left)");
                        await this.ExecuteCommandAsync(formattedKick);
                    }
                    else
                    {
                        await player.Kick($"{loc["SERVER_BAN_PREV"]} {currentBan.Offense}", autoKickClient);
                    }

                    if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban)
                    {
                        await player.Ban($"{loc["SERVER_BAN_PREV"]} {currentBan.Offense}", autoKickClient);
                    }
                    return(true);
                }

                Logger.WriteInfo($"Client {player} connecting...");

                await ExecuteEvent(new GameEvent(GameEvent.EventType.Connect, "", player, null, this));


                if (!Manager.GetApplicationSettings().Configuration().EnableClientVPNs&&
                    await VPNCheck.UsingVPN(player.IPAddressString, Manager.GetApplicationSettings().Configuration().IPHubAPIKey))
                {
                    await player.Kick(Utilities.CurrentLocalization.LocalizationSet["SERVER_KICK_VPNS_NOTALLOWED"], new Player()
                    {
                        ClientId = 1
                    });
                }

                return(true);
            }

            catch (Exception E)
            {
                Manager.GetLogger().WriteError($"Unable to add player {polledPlayer.Name}::{polledPlayer.NetworkId}");
                Manager.GetLogger().WriteDebug(E.StackTrace);
                return(false);
            }
        }
Beispiel #5
0
        override public async Task <bool> ProcessUpdatesAsync(CancellationToken cts)
        {
            this.cts = cts;
            //#if DEBUG == false
            try
            //#endif
            {
                // first start
                if (firstRun)
                {
                    await ExecuteEvent(new GameEvent(GameEvent.EventType.Start, "Server started", null, null, this));

                    firstRun = false;
                }

                if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
                {
                    return(true);
                }

                try
                {
                    // trying to reduce the polling rate as every 450ms is unnecessary
                    if ((DateTime.Now - LastPoll).TotalSeconds >= 10)
                    {
                        int polledPlayerCount = await PollPlayersAsync();

                        if (ConnectionErrors > 0)
                        {
                            Logger.WriteVerbose($"Connection has been reestablished with {IP}:{Port}");
                            Throttled = false;
                        }
                        ConnectionErrors = 0;
                        LastPoll         = DateTime.Now;
                    }
                }

                catch (NetworkException e)
                {
                    ConnectionErrors++;
                    if (ConnectionErrors == 1)
                    {
                        Logger.WriteError($"{e.Message} {IP}:{Port}, reducing polling rate");
                        Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
                        Throttled = true;
                    }
                    return(true);
                }

                LastMessage = DateTime.Now - start;
                lastCount   = DateTime.Now;

                if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000)
                {
                    foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
                    {
                        if (cts.IsCancellationRequested)
                        {
                            break;
                        }

                        await Plugin.OnTickAsync(this);
                    }
                    tickTime = DateTime.Now;
                }

                if ((lastCount - playerCountStart).TotalMinutes >= SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval)
                {
                    while (PlayerHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
                    {
                        PlayerHistory.Dequeue();
                    }
                    PlayerHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum));
                    playerCountStart = DateTime.Now;
                }

                if (LastMessage.TotalSeconds > Manager.GetApplicationSettings().Configuration().AutoMessagePeriod &&
                    BroadcastMessages.Count > 0 &&
                    ClientNum > 0)
                {
                    await Broadcast(Utilities.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]));

                    NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1;
                    start       = DateTime.Now;
                }

                if (LogFile == null)
                {
                    return(true);
                }

                if (l_size != LogFile.Length())
                {
                    lines = l_size != -1 ? await LogFile.Tail(12) : lines;

                    if (lines != oldLines)
                    {
                        l_size = LogFile.Length();
                        int end = (lines.Length == oldLines.Length) ? lines.Length - 1 : Math.Abs((lines.Length - oldLines.Length)) - 1;

                        for (count = 0; count < lines.Length; count++)
                        {
                            if (lines.Length < 1 && oldLines.Length < 1)
                            {
                                continue;
                            }

                            if (lines[count] == oldLines[oldLines.Length - 1])
                            {
                                continue;
                            }

                            if (lines[count].Length < 10) // it's not a needed line
                            {
                                continue;
                            }

                            else
                            {
                                GameEvent event_ = EventParser.GetEvent(this, lines[count]);
                                if (event_ != null)
                                {
                                    if (event_.Origin == null)
                                    {
                                        continue;
                                    }

                                    await ExecuteEvent(event_);
                                }
                            }
                        }
                    }
                }
                oldLines = lines;
                l_size   = LogFile.Length();
                if (Manager.ShutdownRequested())
                {
                    foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
                    {
                        await plugin.OnUnloadAsync();
                    }

                    for (int i = 0; i < Players.Count; i++)
                    {
                        await RemovePlayer(i);
                    }
                }
                return(true);
            }
            //#if !DEBUG
            catch (NetworkException)
            {
                Logger.WriteError($"Could not communicate with {IP}:{Port}");
                return(false);
            }

            catch (InvalidOperationException)
            {
                Logger.WriteWarning("Event could not parsed properly");
                Logger.WriteDebug($"Log Line: {lines[count]}");
                return(false);
            }

            catch (Exception E)
            {
                Logger.WriteError($"Encountered error on {IP}:{Port}");
                Logger.WriteDebug("Error Message: " + E.Message);
                Logger.WriteDebug("Error Trace: " + E.StackTrace);
                return(false);
            }
            //#endif
        }
Beispiel #6
0
        override public async Task <bool> ProcessUpdatesAsync(CancellationToken cts)
        {
            try
            {
                #region SHUTDOWN
                if (Manager.ShutdownRequested())
                {
                    foreach (var client in GetClientsAsList())
                    {
                        var e = new GameEvent()
                        {
                            Type   = GameEvent.EventType.PreDisconnect,
                            Origin = client,
                            Owner  = this,
                        };

                        Manager.GetEventHandler().AddEvent(e);
                        await e.WaitAsync();
                    }

                    foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
                    {
                        await plugin.OnUnloadAsync();
                    }

                    return(true);
                }
                #endregion

                try
                {
                    var polledClients = await PollPlayersAsync();

                    var waiterList = new List <GameEvent>();

                    foreach (var disconnectingClient in polledClients[1])
                    {
                        if (disconnectingClient.State == ClientState.Disconnecting)
                        {
                            continue;
                        }

                        var e = new GameEvent()
                        {
                            Type   = GameEvent.EventType.PreDisconnect,
                            Origin = disconnectingClient,
                            Owner  = this
                        };

                        Manager.GetEventHandler().AddEvent(e);
                        // wait until the disconnect event is complete
                        // because we don't want to try to fill up a slot that's not empty yet
                        waiterList.Add(e);
                    }
                    // wait for all the disconnect tasks to finish
                    await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));

                    waiterList.Clear();
                    // this are our new connecting clients
                    foreach (var client in polledClients[0])
                    {
                        // note: this prevents players in ZMBI state from being registered with no name
                        if (string.IsNullOrEmpty(client.Name))
                        {
                            continue;
                        }

                        var e = new GameEvent()
                        {
                            Type   = GameEvent.EventType.PreConnect,
                            Origin = client,
                            Owner  = this
                        };

                        Manager.GetEventHandler().AddEvent(e);
                        waiterList.Add(e);
                    }

                    // wait for all the connect tasks to finish
                    await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));

                    waiterList.Clear();
                    // these are the clients that have updated
                    foreach (var client in polledClients[2])
                    {
                        var e = new GameEvent()
                        {
                            Type   = GameEvent.EventType.Update,
                            Origin = client,
                            Owner  = this
                        };

                        Manager.GetEventHandler().AddEvent(e);
                        waiterList.Add(e);
                    }

                    await Task.WhenAll(waiterList.Select(e => e.WaitAsync(10 * 1000)));

                    if (ConnectionErrors > 0)
                    {
                        var _event = new GameEvent()
                        {
                            Type   = GameEvent.EventType.ConnectionRestored,
                            Owner  = this,
                            Origin = Utilities.IW4MAdminClient(this),
                            Target = Utilities.IW4MAdminClient(this)
                        };

                        Manager.GetEventHandler().AddEvent(_event);
                    }

                    ConnectionErrors = 0;
                    LastPoll         = DateTime.Now;
                }

                catch (NetworkException e)
                {
                    ConnectionErrors++;
                    if (ConnectionErrors == 3)
                    {
                        var _event = new GameEvent()
                        {
                            Type   = GameEvent.EventType.ConnectionLost,
                            Owner  = this,
                            Origin = Utilities.IW4MAdminClient(this),
                            Target = Utilities.IW4MAdminClient(this),
                            Extra  = e,
                            Data   = ConnectionErrors.ToString()
                        };

                        Manager.GetEventHandler().AddEvent(_event);
                    }
                    return(true);
                }

                LastMessage = DateTime.Now - start;
                lastCount   = DateTime.Now;

                // update the player history
                if ((lastCount - playerCountStart).TotalMinutes >= SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval)
                {
                    while (ClientHistory.Count > ((60 / SharedLibraryCore.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
                    {
                        ClientHistory.Dequeue();
                    }

                    ClientHistory.Enqueue(new SharedLibraryCore.Helpers.PlayerHistory(ClientNum));
                    playerCountStart = DateTime.Now;
                }

                // send out broadcast messages
                if (LastMessage.TotalSeconds > Manager.GetApplicationSettings().Configuration().AutoMessagePeriod &&
                    BroadcastMessages.Count > 0 &&
                    ClientNum > 0)
                {
                    string[] messages = (await this.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage])).Split(Environment.NewLine);

                    foreach (string message in messages)
                    {
                        Broadcast(message);
                    }

                    NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1;
                    start       = DateTime.Now;
                }

                return(true);
            }

            // this one is ok
            catch (ServerException e)
            {
                if (e is NetworkException && !Throttled)
                {
                    Logger.WriteError(loc["SERVER_ERROR_COMMUNICATION"].FormatExt($"{IP}:{Port}"));
                    Logger.WriteDebug(e.GetExceptionInfo());
                }

                return(false);
            }

            catch (Exception E)
            {
                Logger.WriteError(loc["SERVER_ERROR_EXCEPTION"].FormatExt($"[{IP}:{Port}]"));
                Logger.WriteDebug(E.GetExceptionInfo());
                return(false);
            }
        }
Beispiel #7
0
        /// <summary>
        /// Perform the server specific tasks when an event occurs
        /// </summary>
        /// <param name="E"></param>
        /// <returns></returns>
        override protected async Task <bool> ProcessEvent(GameEvent E)
        {
            if (E.Type == GameEvent.EventType.ConnectionLost)
            {
                var exception = E.Extra as Exception;
                Logger.WriteError(exception.Message);
                if (exception.Data["internal_exception"] != null)
                {
                    Logger.WriteDebug($"Internal Exception: {exception.Data["internal_exception"]}");
                }
                Logger.WriteInfo("Connection lost to server, so we are throttling the poll rate");
                Throttled = true;
            }

            if (E.Type == GameEvent.EventType.ConnectionRestored)
            {
                if (Throttled)
                {
                    Logger.WriteVerbose(loc["MANAGER_CONNECTION_REST"].FormatExt($"[{IP}:{Port}]"));
                }
                Logger.WriteInfo("Connection restored to server, so we are no longer throttling the poll rate");
                Throttled = false;
            }

            if (E.Type == GameEvent.EventType.ChangePermission)
            {
                var newPermission = (Permission)E.Extra;

                if (newPermission < Permission.Moderator)
                {
                    // remove banned or demoted privileged user
                    Manager.GetPrivilegedClients().Remove(E.Target.ClientId);
                }

                else
                {
                    if (Manager.GetPrivilegedClients().ContainsKey(E.Target.ClientId))
                    {
                        Manager.GetPrivilegedClients()[E.Target.ClientId] = E.Target;
                    }

                    else
                    {
                        Manager.GetPrivilegedClients().Add(E.Target.ClientId, E.Target);
                    }
                }

                Logger.WriteInfo($"{E.Origin} is setting {E.Target} to permission level {newPermission}");
                await Manager.GetClientService().UpdateLevel(newPermission, E.Target, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.PreConnect)
            {
                // we don't want to track bots in the database at all if ignore bots is requested
                if (E.Origin.IsBot && Manager.GetApplicationSettings().Configuration().IgnoreBots)
                {
                    return(false);
                }

                var existingClient = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));

                // they're already connected
                if (existingClient != null)
                {
                    Logger.WriteWarning($"detected preconnect for {E.Origin}, but they are already connected");
                    return(false);
                }

CONNECT:
                if (Clients[E.Origin.ClientNumber] == null)
                {
#if DEBUG == true
                    Logger.WriteDebug($"Begin PreConnect for {E.Origin}");
#endif
                    // we can go ahead and put them in so that they don't get re added
                    Clients[E.Origin.ClientNumber] = E.Origin;
                    await OnClientConnected(E.Origin);

                    ChatHistory.Add(new ChatInfo()
                    {
                        Name    = E.Origin.Name,
                        Message = "CONNECTED",
                        Time    = DateTime.UtcNow
                    });

                    if (E.Origin.Level > EFClient.Permission.Moderator)
                    {
                        E.Origin.Tell(string.Format(loc["SERVER_REPORT_COUNT"], E.Owner.Reports.Count));
                    }
                }

                // for some reason there's still a client in the spot
                else
                {
                    Logger.WriteWarning($"{E.Origin} is connecteding but {Clients[E.Origin.ClientNumber]} is currently in that client slot");
                    await OnClientDisconnected(Clients[E.Origin.ClientNumber]);

                    goto CONNECT;
                }
            }

            else if (E.Type == GameEvent.EventType.Flag)
            {
                // todo: maybe move this to a seperate function
                Penalty newPenalty = new Penalty()
                {
                    Type     = Penalty.PenaltyType.Flag,
                    Expires  = DateTime.UtcNow,
                    Offender = E.Target,
                    Offense  = E.Data,
                    Punisher = E.Origin,
                    When     = DateTime.UtcNow,
                    Link     = E.Target.AliasLink
                };

                var addedPenalty = await Manager.GetPenaltyService().Create(newPenalty);

                E.Target.SetLevel(Permission.Flagged, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.Unflag)
            {
                var unflagPenalty = new Penalty()
                {
                    Type     = Penalty.PenaltyType.Unflag,
                    Expires  = DateTime.UtcNow,
                    Offender = E.Target,
                    Offense  = E.Data,
                    Punisher = E.Origin,
                    When     = DateTime.UtcNow,
                    Link     = E.Target.AliasLink
                };

                await Manager.GetPenaltyService().Create(unflagPenalty);

                E.Target.SetLevel(Permission.User, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.Report)
            {
                Reports.Add(new Report()
                {
                    Origin = E.Origin,
                    Target = E.Target,
                    Reason = E.Data
                });
            }

            else if (E.Type == GameEvent.EventType.TempBan)
            {
                await TempBan(E.Data, (TimeSpan)E.Extra, E.Target, E.Origin);;
            }

            else if (E.Type == GameEvent.EventType.Ban)
            {
                bool isEvade = E.Extra != null ? (bool)E.Extra : false;
                await Ban(E.Data, E.Target, E.Origin, isEvade);
            }

            else if (E.Type == GameEvent.EventType.Unban)
            {
                await Unban(E.Data, E.Target, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.Kick)
            {
                await Kick(E.Data, E.Target, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.Warn)
            {
                await Warn(E.Data, E.Target, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.Disconnect)
            {
                ChatHistory.Add(new ChatInfo()
                {
                    Name    = E.Origin.Name,
                    Message = "DISCONNECTED",
                    Time    = DateTime.UtcNow
                });

                await new MetaService().AddPersistentMeta("LastMapPlayed", CurrentMap.Alias, E.Origin);
                await new MetaService().AddPersistentMeta("LastServerPlayed", E.Owner.Hostname, E.Origin);
            }

            else if (E.Type == GameEvent.EventType.PreDisconnect)
            {
                // predisconnect comes from minimal rcon polled players and minimal log players
                // so we need to disconnect the "full" version of the client
                var client = GetClientsAsList().FirstOrDefault(_client => _client.Equals(E.Origin));

                if (client != null)
                {
#if DEBUG == true
                    Logger.WriteDebug($"Begin PreDisconnect for {client}");
#endif
                    await OnClientDisconnected(client);

#if DEBUG == true
                    Logger.WriteDebug($"End PreDisconnect for {client}");
#endif
                }

                else if (client?.State != ClientState.Disconnecting)
                {
                    Logger.WriteWarning($"Client {E.Origin} detected as disconnecting, but could not find them in the player list");
                    Logger.WriteDebug($"Expected {E.Origin} but found {GetClientsAsList().FirstOrDefault(_client => _client.ClientNumber == E.Origin.ClientNumber)}");
                    return(false);
                }
            }

            else if (E.Type == GameEvent.EventType.Update)
            {
#if DEBUG == true
                Logger.WriteDebug($"Begin Update for {E.Origin}");
#endif
                await OnClientUpdate(E.Origin);
            }

            if (E.Type == GameEvent.EventType.Say)
            {
                E.Data = E.Data.StripColors();

                if (E.Data?.Length > 0)
                {
                    string message = E.Data;
                    if (E.Data.IsQuickMessage())
                    {
                        try
                        {
                            message = Manager.GetApplicationSettings().Configuration()
                                      .QuickMessages
                                      .First(_qm => _qm.Game == GameName)
                                      .Messages[E.Data.Substring(1)];
                        }
                        catch { }
                    }

                    ChatHistory.Add(new ChatInfo()
                    {
                        Name    = E.Origin.Name,
                        Message = message,
                        Time    = DateTime.UtcNow
                    });
                }
            }

            if (E.Type == GameEvent.EventType.MapChange)
            {
                Logger.WriteInfo($"New map loaded - {ClientNum} active players");

                // iw4 doesn't log the game info
                if (E.Extra == null)
                {
                    var dict = await this.GetInfoAsync();

                    if (dict == null)
                    {
                        Logger.WriteWarning("Map change event response doesn't have any data");
                    }

                    else
                    {
                        Gametype = dict["gametype"].StripColors();
                        Hostname = dict["hostname"]?.StripColors();

                        string mapname = dict["mapname"]?.StripColors() ?? CurrentMap.Name;
                        CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
                        {
                            Alias = mapname, Name = mapname
                        };
                    }
                }

                else
                {
                    var dict = (Dictionary <string, string>)E.Extra;
                    Gametype   = dict["g_gametype"].StripColors();
                    Hostname   = dict["sv_hostname"].StripColors();
                    MaxClients = int.Parse(dict["sv_maxclients"]);

                    string mapname = dict["mapname"].StripColors();
                    CurrentMap = Maps.Find(m => m.Name == mapname) ?? new Map()
                    {
                        Alias = mapname,
                        Name  = mapname
                    };
                }
            }

            if (E.Type == GameEvent.EventType.MapEnd)
            {
                Logger.WriteInfo("Game ending...");
                SessionStart = DateTime.UtcNow;
            }

            if (E.Type == GameEvent.EventType.Tell)
            {
                await Tell(E.Message, E.Target);
            }

            if (E.Type == GameEvent.EventType.Broadcast)
            {
#if DEBUG == false
                // this is a little ugly but I don't want to change the abstract class
                if (E.Data != null)
                {
                    await E.Owner.ExecuteCommandAsync(E.Data);
                }
#endif
            }

            lock (ChatHistory)
            {
                while (ChatHistory.Count > Math.Ceiling(ClientNum / 2.0))
                {
                    ChatHistory.RemoveAt(0);
                }
            }

            // the last client hasn't fully disconnected yet
            // so there will still be at least 1 client left
            if (ClientNum < 2)
            {
                ChatHistory.Clear();
            }

            return(true);
        }
Beispiel #8
0
        public void Init()
        {
            #region WEBSERVICE
            // SharedLibrary.WebService.Init();
            //WebSvc = new WebService();
            //WebSvc.StartScheduler();
            #endregion

            #region PLUGINS
            SharedLibrary.Plugins.PluginImporter.Load(this);

            foreach (var Plugin in SharedLibrary.Plugins.PluginImporter.ActivePlugins)
            {
                try
                {
                    Plugin.OnLoadAsync(this);
                }

                catch (Exception e)
                {
                    Logger.WriteError($"An error occured loading plugin {Plugin.Name}");
                    Logger.WriteDebug($"Exception: {e.Message}");
                    Logger.WriteDebug($"Stack Trace: {e.StackTrace}");
                }
            }
            #endregion

            #region CONFIG
            var Configs = Directory.EnumerateFiles($"{Program.OperatingDirectory}config/servers").Where(x => x.Contains(".cfg"));

            if (Configs.Count() == 0)
            {
                ServerConfigurationGenerator.Generate();
            }

            foreach (var file in Configs)
            {
                var Conf = ServerConfiguration.Read(file);

                Task.Run(async() =>
                {
                    try
                    {
                        var ServerInstance = new IW4MServer(this, Conf);
                        await ServerInstance.Initialize();

                        lock (_servers)
                        {
                            _servers.Add(ServerInstance);
                        }

                        Logger.WriteVerbose($"Now monitoring {ServerInstance.Hostname}");

                        // this way we can keep track of execution time and see if problems arise.
                        var Status = new AsyncStatus(ServerInstance, UPDATE_FREQUENCY);
                        lock (TaskStatuses)
                        {
                            TaskStatuses.Add(Status);
                        }
                    }

                    catch (ServerException e)
                    {
                        Logger.WriteError($"Not monitoring server {Conf.IP}:{Conf.Port} due to uncorrectable errors");
                        if (e.GetType() == typeof(DvarException))
                        {
                            Logger.WriteDebug($"Could not get the dvar value for {(e as DvarException).Data["dvar_name"]} (ensure the server has a map loaded)");
                        }
                        else if (e.GetType() == typeof(NetworkException))
                        {
                            Logger.WriteDebug(e.Message);
                            Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
                        }
                    }
                });
            }
            #endregion

            #region COMMANDS
            if (ClientSvc.GetOwners().Result.Count == 0)
            {
                Commands.Add(new COwner());
            }

            Commands.Add(new CQuit());
            Commands.Add(new CKick());
            Commands.Add(new CSay());
            Commands.Add(new CTempBan());
            Commands.Add(new CBan());
            Commands.Add(new CWhoAmI());
            Commands.Add(new CList());
            Commands.Add(new CHelp());
            Commands.Add(new CFastRestart());
            Commands.Add(new CMapRotate());
            Commands.Add(new CSetLevel());
            Commands.Add(new CUsage());
            Commands.Add(new CUptime());
            Commands.Add(new CWarn());
            Commands.Add(new CWarnClear());
            Commands.Add(new CUnban());
            Commands.Add(new CListAdmins());
            Commands.Add(new CLoadMap());
            Commands.Add(new CFindPlayer());
            Commands.Add(new CListRules());
            Commands.Add(new CPrivateMessage());
            Commands.Add(new CReload());
            Commands.Add(new CFlag());
            Commands.Add(new CReport());
            Commands.Add(new CListReports());
            Commands.Add(new CListBanInfo());
            Commands.Add(new CListAlias());
            Commands.Add(new CExecuteRCON());
            Commands.Add(new CPlugins());
            Commands.Add(new CIP());
            Commands.Add(new CMask());
            Commands.Add(new CPruneAdmins());

            foreach (Command C in SharedLibrary.Plugins.PluginImporter.ActiveCommands)
            {
                Commands.Add(C);
            }
            #endregion

            Running = true;
        }
Beispiel #9
0
        override public async Task<bool> ProcessUpdatesAsync(CancellationToken cts)
        {
            this.cts = cts;
#if DEBUG == false
            try
#endif
            {
                // first start
                if (firstRun)
                {
                    await ExecuteEvent(new Event(Event.GType.Start, "Server started", null, null, this));
                    firstRun = false;
                }

                if ((DateTime.Now - LastPoll).TotalMinutes < 2 && ConnectionErrors >= 1)
                    return true;

                try
                {
                    int polledPlayerCount = await PollPlayersAsync();

                    if (ConnectionErrors > 0)
                    {
                        Logger.WriteVerbose($"Connection has been reestablished with {IP}:{Port}");
                        Throttled = false;
                    }
                    ConnectionErrors = 0;
                    LastPoll = DateTime.Now;
                }

                catch (SharedLibrary.Exceptions.NetworkException e)
                {
                    ConnectionErrors++;
                    if (ConnectionErrors == 1)
                    {
                        Logger.WriteError($"{e.Message} {IP}:{Port}, reducing polling rate");
                        Logger.WriteDebug($"Internal Exception: {e.Data["internal_exception"]}");
                        Throttled = true;
                    }
                    return true;
                }

                LastMessage = DateTime.Now - start;
                lastCount = DateTime.Now;

                if ((DateTime.Now - tickTime).TotalMilliseconds >= 1000)
                {
                    foreach (var Plugin in SharedLibrary.Plugins.PluginImporter.ActivePlugins)
                    {
                        if (cts.IsCancellationRequested)
                            break;

                        await Plugin.OnTickAsync(this);
                    }
                    tickTime = DateTime.Now;
                }

                if ((lastCount - playerCountStart).TotalMinutes >= SharedLibrary.Helpers.PlayerHistory.UpdateInterval)
                {
                    while (PlayerHistory.Count > ((60 / SharedLibrary.Helpers.PlayerHistory.UpdateInterval) * 12)) // 12 times a hour for 12 hours
                        PlayerHistory.Dequeue();
                    PlayerHistory.Enqueue(new SharedLibrary.Helpers.PlayerHistory(ClientNum));
                    playerCountStart = DateTime.Now;
                }

                if (LastMessage.TotalSeconds > MessageTime && BroadcastMessages.Count > 0 && ClientNum > 0)
                {
                    await Broadcast(Utilities.ProcessMessageToken(Manager.GetMessageTokens(), BroadcastMessages[NextMessage]));
                    NextMessage = NextMessage == (BroadcastMessages.Count - 1) ? 0 : NextMessage + 1;
                    start = DateTime.Now;
                }

                if (LogFile == null)
                    return true;

                if (l_size != LogFile.Length())
                {
                    // this should be the longest running task
                    await Task.FromResult(lines = LogFile.Tail(12));
                    if (lines != oldLines)
                    {
                        l_size = LogFile.Length();
                        int end = (lines.Length == oldLines.Length) ? lines.Length - 1 : Math.Abs((lines.Length - oldLines.Length)) - 1;

                        for (int count = 0; count < lines.Length; count++)
                        {
                            if (lines.Length < 1 && oldLines.Length < 1)
                                continue;

                            if (lines[count] == oldLines[oldLines.Length - 1])
                                continue;

                            if (lines[count].Length < 10) // it's not a needed line 
                                continue;

                            else
                            {
                                string[] game_event = lines[count].Split(';');
                                Event event_ = Event.ParseEventString(game_event, this);
                                if (event_ != null)
                                {
                                    if (event_.Origin == null)
                                        continue;

                                    await ExecuteEvent(event_);
                                }
                            }
                        }
                    }
                }
                oldLines = lines;
                l_size = LogFile.Length();
                if (!((ApplicationManager)Manager).Running)
                {
                    foreach (var plugin in SharedLibrary.Plugins.PluginImporter.ActivePlugins)
                        await plugin.OnUnloadAsync();

                    for (int i = 0; i < Players.Count; i++)
                        await RemovePlayer(i);
                }
                return true;
            }
#if DEBUG == false
            catch (SharedLibrary.Exceptions.NetworkException)
            {
                Logger.WriteError($"Could not communicate with {IP}:{Port}");
                return false;
            }

            catch (Exception E)
            {
                Logger.WriteError($"Encountered error on {IP}:{Port}");
                Logger.WriteDebug("Error Message: " + E.Message);
                Logger.WriteDebug("Error Trace: " + E.StackTrace);
                return false;
            }
#endif
        }
Beispiel #10
0
        override public async Task<bool> AddPlayer(Player polledPlayer)
        {
            if (polledPlayer.Ping == 999 || polledPlayer.Ping < 1 || polledPlayer.ClientNumber > (MaxClients) || polledPlayer.ClientNumber < 0)
            {
                //Logger.WriteDebug($"Skipping client not in connected state {P}");
                return true;
            }

            if (Players[polledPlayer.ClientNumber] != null &&
                Players[polledPlayer.ClientNumber].NetworkId == polledPlayer.NetworkId)
            {
                // update their ping & score 
                Players[polledPlayer.ClientNumber].Ping = polledPlayer.Ping;
                Players[polledPlayer.ClientNumber].Score = polledPlayer.Score;
                return true;
            }

            if (polledPlayer.Name.Length < 3)
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is too short");
                await this.ExecuteCommandAsync($"clientkick {polledPlayer.ClientNumber} \"Your name must contain atleast 3 characters.\"");
                return false;
            }

            if (Players.FirstOrDefault(p => p != null && p.Name == polledPlayer.Name) != null)
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is already in use");
                await this.ExecuteCommandAsync($"clientkick {polledPlayer.ClientNumber} \"Your name is being used by someone else.\"");
                return false;
            }

            if (polledPlayer.Name == "Unknown Soldier" ||
                polledPlayer.Name == "UnknownSoldier")
            {
                Logger.WriteDebug($"Kicking {polledPlayer} because their name is generic");
                await this.ExecuteCommandAsync($"clientkick {polledPlayer.ClientNumber} \"Please change your name using /name.\"");
                return false;
            }

            Logger.WriteDebug($"Client slot #{polledPlayer.ClientNumber} now reserved");

            try
            {
                Player player = null;
                var client = await Manager.GetClientService().GetUnique(polledPlayer.NetworkId);

                // first time client is connecting to server
                if (client == null)
                {
                    Logger.WriteDebug($"Client {polledPlayer} first time connecting");
                    player = (await Manager.GetClientService().Create(polledPlayer)).AsPlayer();
                }

                // client has connected in the past
                else
                {
                    client.Connections += 1;
                    var existingAlias = client.AliasLink.Children
                        .FirstOrDefault(a => a.Name == polledPlayer.Name && a.IPAddress == polledPlayer.IPAddress);

                    if (existingAlias == null)
                    {
                        Logger.WriteDebug($"Client {polledPlayer} has connected previously under a different ip/name");
                        client.CurrentAlias = new SharedLibrary.Database.Models.EFAlias()
                        {
                            IPAddress = polledPlayer.IPAddress,
                            Name = polledPlayer.Name,
                        };
                        // we need to update their new ip and name to the virtual property
                        client.Name = polledPlayer.Name;
                        client.IPAddress = polledPlayer.IPAddress;

                        await Manager.GetClientService().Update(client);
                    }

                    else if (existingAlias.Name  == polledPlayer.Name)
                    {
                        client.CurrentAlias = existingAlias;
                        client.CurrentAliasId = existingAlias.AliasId;
                        await Manager.GetClientService().Update(client);
                    }
                    player = client.AsPlayer();
                }

                // Do the player specific stuff
                player.ClientNumber = polledPlayer.ClientNumber;
                player.Score = polledPlayer.Score;
                player.CurrentServer = this;
                Players[player.ClientNumber] = player;

                var activePenalties = await Manager.GetPenaltyService().GetActivePenaltiesAsync(player.AliasLinkId);
                var currentBan = activePenalties.FirstOrDefault(b => b.Expires > DateTime.UtcNow);

                if (currentBan != null)
                {
                    Logger.WriteInfo($"Banned client {player} trying to connect...");
                    var autoKickClient = (await Manager.GetClientService().Get(1)).AsPlayer();
                    autoKickClient.CurrentServer = this;

                    if (currentBan.Type == Penalty.PenaltyType.TempBan)
                        await this.ExecuteCommandAsync($"clientkick {player.ClientNumber} \"You are temporarily banned. ({(currentBan.Expires - DateTime.UtcNow).TimeSpanText()} left)\"");
                    else
                        await player.Kick($"Previously banned for {currentBan.Offense}", autoKickClient);

                    if (player.Level != Player.Permission.Banned && currentBan.Type == Penalty.PenaltyType.Ban)
                        await player.Ban($"Previously banned for {currentBan.Offense}", autoKickClient);
                    return true;
                }

                Logger.WriteInfo($"Client {player} connecting...");

                await ExecuteEvent(new Event(Event.GType.Connect, "", player, null, this));

                return true;
            }

            catch (Exception E)
            {
                Manager.GetLogger().WriteError($"Unable to add player {polledPlayer.Name}::{polledPlayer.NetworkId}");
                Manager.GetLogger().WriteDebug(E.StackTrace);
                return false;
            }
        }