public void CheckForSkip() { if (HasSkipped || Server.ValidPlayers.Count == 0) { return; } if (SkipVotes.Count >= NeededVotesToSkipLevel) { HasSkipped = true; autoServer.AdvanceLevel(); foreach (var player in Server.ValidPlayers) { if (player.Car != null && !player.Car.Finished) { player.Car.BroadcastDNF(); } } if (Server.ModeStartTime + BadVoterDetectionCutoff > DistanceServerMain.NetworkTime) { // punishment end times are used to determine name/guid/ip pairs and delete all at once when the punishment time is over // we use uniqueOffset to force these times to be unique in all cases // this is *not* the proper way to do this but it works for this case and is simple. double uniqueOffset = 0.0; foreach (var voterInfo in LastMapVoters) { SetBadVoterUntil(voterInfo, DistanceServerMain.UnixTime + BadVoterPunishmentTime + uniqueOffset); uniqueOffset += 0.01; } } Server.SayChat(DistanceChat.Server("VoteCommands:SkipSuccess", $"Votes to skip the level have passed {(int)(SkipThreshold*100)}%. Skipping the level in 10 seconds.")); } }
internal void RespondLink() { var writer = new JsonFx.Json.JsonWriter(); if (!IsPrivateMode) { Context.Response.StatusCode = 401; Context.Response.StatusDescription = "Unauthorized"; Response = writer.Write(new { ErrorCode = 401, }); return; } var reader = new JsonFx.Json.JsonReader(); var data = (Dictionary <string, object>)reader.Read(Body); string guid = (string)data["Guid"]; Plugin.Links[SessionId] = guid; Response = writer.Write(new { Success = true, }); if (data.ContainsKey("SendIpWarning") && (bool)data["SendIpWarning"]) { var player = Plugin.Manager.Server.GetDistancePlayer(guid); if (player != null) { Plugin.Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("HttpServer:Link", "Your game session has been automatically linked with a web session. You can now vote and chat from the website.\nIf this wasn't you, type [00FFFF]/unlink[-]")); } } }
public void SayLocalChat(DistanceChat chat) { AddChat(chat); DistanceServerMain.GetEvent <Events.ClientToAllClients.ChatMessage>().Fire( UnityPlayer, new Distance::Events.ClientToAllClients.ChatMessage.Data(chat.Message) ); }
public void DeleteChatMessage(DistanceChat message, bool resendChat = false) { ChatLog.RemoveAll(item => item.ChatGuid == message.ChatGuid); if (resendChat) { ResendChat(); } }
public void DeleteChatMessage(DistanceChat message, bool resendChat = false) { ChatLog.RemoveAll(item => item.ChatGuid == message.ChatGuid); foreach (var distancePlayer in DistancePlayers.Values) { distancePlayer.DeleteChatMessage(message, resendChat); } }
public void SayChat(DistanceChat chat, bool addMessage = true) { AddChat(chat); DistanceServerMain.GetEvent <Events.ClientToAllClients.ChatMessage>().Fire( RPCMode.Others, new Distance::Events.ClientToAllClients.ChatMessage.Data(chat.Message) ); }
public void Update() { if (ServerStage == Stage.Started && UnityEngine.Network.time >= LevelEndTime - 60.0) { ServerStage = Stage.Timeout; var timeLeft = LevelEndTime - UnityEngine.Network.time; setCountdownTime(Server.ModeTime + timeLeft); Server.SayChat(DistanceChat.Server("AutoServer:Warning:Timeout", TimeoutMessageGetter(GenerateLevelTimeoutText(LevelTimeout - 60.0)))); } else if (ServerStage == Stage.Started && IdleTimeout > 0) { foreach (var player in Server.ValidPlayers) { if (player.Car != null && player.Car.CarDirectives != null && !player.Car.Finished) { var lastMoveData = player.Car.GetExternalData <LastMoveTimeData>(); if (!player.Car.CarDirectives.IsZero()) { lastMoveData.LastMoveTime = DistanceServerMain.UnixTime; } else if (DistanceServerMain.UnixTime - lastMoveData.LastMoveTime >= IdleTimeout) { lastMoveData.LastMoveTime = DistanceServerMain.UnixTime; player.Car.BroadcastDNF(); Server.SayChat(DistanceChat.Server("AutoServer:IdleSpectate", $"{player.Name} has been set to spectate mode for being idle")); } } } } else if (ServerStage == Stage.Timeout && UnityEngine.Network.time >= LevelEndTime) { NextLevel(); } else if (ServerStage == Stage.AllFinished && UnityEngine.Network.time - allFinishedAt >= 10) { NextLevel(); } if (ReportToMasterServer) { if (!masterServerDeregistered) { if (DistanceServerMain.UnixTime - lastMasterServerRegistration > MasterServerReRegisterFrequency) { Log.Debug("Deregistering from master server..."); masterServerDeregistered = true; Server.ReportToMasterServer = false; } } else if (DistanceServerMain.UnixTime - lastMasterServerRegistration > MasterServerReRegisterFrequency + 10.0) { Log.Debug("Re-reregistering to master server..."); Server.ReportToMasterServer = true; masterServerDeregistered = false; lastMasterServerRegistration = DistanceServerMain.UnixTime; } } }
public void SayLocalChat(NetworkPlayer player, DistanceChat chat) { DistancePlayer distancePlayer = null; DistancePlayers.TryGetValue(player.guid, out distancePlayer); if (distancePlayer != null) { distancePlayer.SayLocalChat(chat); } }
public System.Collections.IEnumerator GiveTagBubbleSoon(DistancePlayer fromPlayer, DistancePlayer player) { yield return(new UnityEngine.WaitForSeconds(1)); if (IsPlayerInMode(player) && TaggedPlayer == fromPlayer) { fromPlayer.GetExternalData <ReverseTagData>().SecondsTagged = 0.0; TagPlayer(player); } fromPlayer.SayLocalChat(DistanceChat.Server("ReverseTag:SinglePlayerTransition", "Transitioning out of single-player: your timer has been reset and your tag bubble taken!")); }
public void AddChat(DistanceChat chat) { ChatLog.Add(chat); ChatLogLineCount += chat.Lines.Length; if (ChatLogLineCount - ChatLog[0].Lines.Length >= 64) { ChatLogLineCount = ChatLogLineCount - ChatLog[0].Lines.Length; ChatLog.RemoveAt(0); } OnChatMessageEvent.Fire(chat); }
public override void Start() { Log.Info("Vanilla Functionality Plugin started!"); Server.OnPlayerValidatedEvent.Connect(player => { Server.AddChat(DistanceChat.Server("Vanilla:PlayerJoined", $"[FFE999]{player.Name} has joined the server!")); }); Server.OnPlayerDisconnectedEvent.Connect(player => { Server.AddChat(DistanceChat.Server("Vanilla:PlayerLeft", $"[FFE999]{player.Name} has left the game[-]")); // TODO: implement timeout message }); }
public void Update() { if (Plugin.ServerStage == BasicAutoServer.Stage.Started && !TimeoutStarted && UnityEngine.Network.time >= LevelEndTime - 60.0) { TimeoutStarted = true; var timeLeft = LevelEndTime - UnityEngine.Network.time; Plugin.SetCountdownTime(Plugin.Server.ModeTime + timeLeft); Plugin.Server.SayChat(DistanceChat.Server("AutoServer:Warning:Timeout", Plugin.TimeoutMessageGetter(Plugin.GenerateLevelTimeoutText(Plugin.LevelTimeout - 60.0)))); } if (Plugin.ServerStage == BasicAutoServer.Stage.Started && UnityEngine.Network.time >= LevelEndTime) { Plugin.FinishAllPlayersAndAdvanceLevel(); } }
public void AddChat(DistanceChat chat) { ChatLog.Add(chat); ChatLogLineCount += chat.Lines.Length; if (ChatLogLineCount - ChatLog[0].Lines.Length >= 64) { ChatLogLineCount = ChatLogLineCount - ChatLog[0].Lines.Length; ChatLog.RemoveAt(0); } foreach (var distancePlayer in DistancePlayers.Values) { distancePlayer.AddChat(chat); } OnChatMessageEvent.Fire(chat); }
System.Collections.IEnumerator DoLoadWorkshopLevels() { // Start players on campaign maps while the workshop maps load StartAutoServer(); NextLevel(); Server.SayChat(DistanceChat.Server("AutoServer:GeneratingPlaylist", "Generating playlist from Steam Workshop...")); Server.OnLevelStartInitiatedEvent.Connect(() => { if (currentLevelIndex == Playlist.Count - 1) { // Update the playlist on the last level DistanceServerMainStarter.Instance.StartCoroutine(UpdatePlaylist()); } }); // Load maps, but don't wait on them to finish -- it might error! DistanceServerMainStarter.Instance.StartCoroutine(UpdatePlaylist()); yield break; }
void StartAutoServer() { Server.CurrentLevel = Playlist[0]; StartLevel(); Server.OnUpdateEvent.Connect(Update); Server.OnPlayerDisconnectedEvent.Connect(player => { if (Server.ValidPlayers.Count == 0 && AdvanceWhenAllPlayersFinish) // && Server.HasModeStarted) { Server.SayChat(DistanceChat.Server("AutoServer:Advancing:Empty", "All players have left. Advancing level.")); AdvanceLevel(); } else { AttemptToAdvanceLevel(); } }); }
public void CheckForExtend() { if (Server.ValidPlayers.Count == 0 || autoServer.SprintMode == null) { return; } if (ExtendVotes.Count >= NeededVotesToExtendLevel) { var success = autoServer.SprintMode.ExtendTimeout(ExtendTime); if (success) { Server.SayChat(DistanceChat.Server("VoteCommands:ExtendSuccess", $"Votes to extend the level have passed {(int)(ExtendThreshold * 100)}%. Extending the level by {GetExtendTimeText()}")); } else { DelayedExtensions++; } ExtendVotes.Clear(); } }
void AttemptToAdvanceLevel() { if (Server.StartingLevel || !Server.HasModeStarted) { Log.Debug($"Mode not started, not advancing normally."); return; } if (Server.ValidPlayers.Count == 0) { Log.Debug($"No players, not advancing normally."); return; } var canAdvance = AdvanceWhenAllPlayersFinish; foreach (var player in Server.ValidPlayers) { if ((player.Car != null && !player.Car.Finished) || DistanceServerMain.UnixTime - player.RestartTime < 30) { canAdvance = false; break; } } if (canAdvance) { Log.Debug($"Advancing because all players with cars have finished."); Server.SayChat(DistanceChat.Server("AutoServer:Advancing:Finished", "All players finished. Advancing to the next level in 10 seconds.")); AdvanceLevel(); } else if (AdvanceWhenStartingPlayersFinish) { if (ServerStage == Stage.Started && GetUnfinishedStartingPlayersCount() == 0) { LevelEndTime = DistanceServerMain.NetworkTime + 60.0; ServerStage = Stage.Timeout; setCountdownTime(Server.ModeTime + 60.0); Server.SayChat(DistanceChat.Server("AutoServer:Warning:InitialFinished", StartingPlayersFinishedMessageGetter())); } } }
public void OnCheckIfLevelCanAdvance(Cancellable canceller) { if (canceller.IsCancelled) { return; } var canAdvance = Plugin.AdvanceWhenAllPlayersFinish; foreach (var player in Plugin.Server.ValidPlayers) { if ((player.Car != null && !player.Car.Finished) || DistanceServerMain.UnixTime - player.RestartTime < 30) { canAdvance = false; break; } } if (canAdvance) { Log.Debug($"Advancing because all players with cars have finished."); Plugin.Server.SayChat(DistanceChat.Server("AutoServer:Advancing:Finished", "All players finished. Advancing to the next level in 10 seconds.")); return; } canceller.Cancel(); if (Plugin.AdvanceWhenStartingPlayersFinish) { if (!TimeoutStarted && Plugin.GetUnfinishedStartingPlayersCount() == 0) { LevelEndTime = DistanceServerMain.NetworkTime + 60.0; TimeoutStarted = true; Plugin.SetCountdownTime(Plugin.Server.ModeTime + 60.0); Plugin.Server.SayChat(DistanceChat.Server("AutoServer:Warning:InitialFinished", Plugin.StartingPlayersFinishedMessageGetter())); } } }
public System.Collections.IEnumerator SearchForLevels(DistancePlayer searcher, string searchText, bool isVote, bool isAgainst) { var autoServer = Manager.GetPlugin <BasicAutoServer.BasicAutoServer>(); var byMatch = Regex.Match(searchText, @"by (.*)$"); string onlyBy = null; if (byMatch.Success) { searchText = Regex.Replace(searchText, @"\s*by (.*)$", ""); onlyBy = byMatch.Groups[1].ToString(); } var searches = new List <WorkshopSearch.DistanceSearchRetriever>(); if (RequiredTags.Count == 0) { searches.Add(new WorkshopSearch.DistanceSearchRetriever(new WorkshopSearch.DistanceSearchParameters() { Search = new WorkshopSearch.WorkshopSearchParameters() { AppId = WorkshopSearch.Workshop.DistanceAppId, SearchText = searchText, SearchType = WorkshopSearch.WorkshopSearchParameters.SearchTypeType.GameFiles, Sort = WorkshopSearch.WorkshopSearchParameters.SortType.Relevance, Days = -1, NumPerPage = 30, Page = 1, RequiredTags = new string[] { }, }, GameMode = autoServer.GameMode, MaxSearch = 5 * 30, MaxResults = 3, DistanceLevelFilter = (levels) => { if (onlyBy != null) { levels.RemoveAll(level => { return(!level.WorkshopItemResult.AuthorName.ToLower().Contains(onlyBy.ToLower())); }); } return(autoServer.FilterWorkshopLevels(levels)); } })); } else { foreach (var tag in RequiredTags) { searches.Add(new WorkshopSearch.DistanceSearchRetriever(new WorkshopSearch.DistanceSearchParameters() { Search = new WorkshopSearch.WorkshopSearchParameters() { AppId = WorkshopSearch.Workshop.DistanceAppId, SearchText = searchText, SearchType = WorkshopSearch.WorkshopSearchParameters.SearchTypeType.GameFiles, Sort = WorkshopSearch.WorkshopSearchParameters.SortType.Relevance, Days = -1, NumPerPage = 30, Page = 1, RequiredTags = new string[] { tag }, }, GameMode = autoServer.GameMode, MaxSearch = 5 * 30, MaxResults = 3, DistanceLevelFilter = (levels) => { if (onlyBy != null) { levels.RemoveAll(level => { return(!level.WorkshopItemResult.AuthorName.ToLower().Contains(onlyBy.ToLower())); }); } return(autoServer.FilterWorkshopLevels(levels)); } })); } } var items = new List <WorkshopSearch.DistanceSearchResultItem>(); foreach (var search in searches) { yield return(search.TaskCoroutine); if (search.HasError) { Server.SayLocalChat(searcher.UnityPlayer, DistanceChat.Server("VoteCommands:Feedback:SearchError", $"Error when searching for \"{searchText}\"")); Log.Error($"Error when searching for \"{searchText}\": {search.Error}"); yield break; } items.AddRange(search.Results); } if (items.Count == 0) { Server.SayLocalChat(searcher.UnityPlayer, DistanceChat.Server("VoteCommands:Feedback:SearchResult", $"No levels found for \"{searchText}\"" + (onlyBy != null ? $" by \"{onlyBy}\"" : ""))); yield break; } if (!isVote) { var result = $"Levels for \"{searchText}\":"; for (int i = 0; i < Math.Min(3, items.Count); i++) { var item = items[i].WorkshopItemResult; result += $"\n[00FF00]{item.ItemName}[-] by {item.AuthorName}" + (item.Rating == -1 ? "" : $" {item.Rating}/5"); } Server.SayLocalChat(searcher.UnityPlayer, DistanceChat.Server("VoteCommands:Feedback:SearchResult", result)); yield break; } else { var result = items[0].DistanceLevelResult; if (!isAgainst) { var data = new FilterLevelRealtimeEventData(result); OnFilterLevelRealtime.Fire(data); if (data.HardBlocklist) { Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:VoteHardBlocked", $"The level [00FF00]{result.Name}[-] by {items[0].WorkshopItemResult.AuthorName} is blocked because {data.Reason}.")); yield break; } PlayerVotes[searcher.UnityPlayerGuid] = result; PlayerVoteTimes[searcher.UnityPlayerGuid] = DistanceServerMain.UnixTime; Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:VoteSuccess", $"Set {searcher.Name}'s vote to [00FF00]{result.Name}[-] by {items[0].WorkshopItemResult.AuthorName}")); if (data.SoftBlocklist) { int count = PlayerVotes.Sum(pair => { if (pair.Value.RelativeLevelPath != result.RelativeLevelPath || Server.GetDistancePlayer(pair.Key) == null) { return(0); } return(1); }); int sub = 0; if (AgainstVotes.ContainsKey(result.RelativeLevelPath)) { sub = AgainstVotes[result.RelativeLevelPath].Sum(playerGuid => Server.GetDistancePlayer(playerGuid) != null ? 1 : 0); } count = count - sub; int needed = NeededVotesToExtendLevel - count; if (needed > 0) { Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:VoteSoftBlocked", $"The level [00FF00]{result.Name}[-] is soft-blocked and needs {needed} more votes to be played because {data.Reason}.")); } else { Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:VoteSuccessSoftBlock", $"The level [00FF00]{result.Name}[-] is soft-blocked but has met its required vote count and can now be played.")); } } } else { var key = result.RelativeLevelPath; if (!AgainstVotes.ContainsKey(key)) { AgainstVotes[key] = new HashSet <string>(); } if (AgainstVotes[key].Contains(searcher.UnityPlayerGuid)) { AgainstVotes[key].Remove(searcher.UnityPlayerGuid); if (AgainstVotes[key].Count == 0) { AgainstVotes.Remove(key); } Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:AgainstVoteRemoved", $"Cleared {searcher.Name}'s vote against [00FF00]{result.Name}[-] by {items[0].WorkshopItemResult.AuthorName}")); } else { AgainstVotes[key].Add(searcher.UnityPlayerGuid); Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:AgainstVoteAdded", $"Set {searcher.Name}'s vote against [00FF00]{result.Name}[-] by {items[0].WorkshopItemResult.AuthorName}")); } } yield break; } }
public override void Start() { Log.Info("Basic Auto Server started!"); Playlist.AddRange(OfficialPlaylist); Filters = new List <FilterWorkshopSearchDelegate>(); ReadSettings(); UnityEngine.MasterServer.dedicatedServer = ReportToMasterServerAsDedicatedServer; Server.MasterServerGameModeOverride = MasterServerGameModeOverride; Server.ServerName = ServerName; Server.MaxPlayers = MaxPlayers; Server.Port = Port; Server.UseNat = UseNat; if (ReportToMasterServerInitialDelay > 0) { Server.ReportToMasterServer = false; DistanceServerMainStarter.Instance.StartCoroutine(SetReportToMasterServerAfterDelay()); } else { Server.ReportToMasterServer = ReportToMasterServer; } lastMasterServerRegistration = DistanceServerMain.UnixTime; if (PrivateServerPassword != null) { UnityEngine.Network.incomingPassword = PrivateServerPassword; } Server.OnPlayerConnectedEvent.Connect(player => { player.Countdown = CountdownTime; player.OnCarAddedEvent.Connect(car => { car.AddExternalData(new LastMoveTimeData(DistanceServerMain.UnixTime)); OnCarAddedEvent.Fire(car); }); }); if (WelcomeMessage != null) { Server.OnPlayerValidatedEvent.Connect(player => { var message = WelcomeMessage; if (message.Contains("$linkcode") && LinkCodeGetter != null) { message = message.Replace("$linkcode", LinkCodeGetter(player)); } message = message.Replace("$player", player.Name); if (!Server.HasModeStarted || player.Car != null) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("AutoServer:Welcome", message)); } else { IEventConnection connection = null; connection = player.OnCarAddedEvent.Connect(car => { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("AutoServer:Welcome", message)); connection.Disconnect(); }); } }); } Server.OnLevelStartedEvent.Connect(() => { CountdownTime = -1.0; }); Server.OnModeStartedEvent.Connect(() => { StartingPlayerGuids.Clear(); foreach (var player in Server.ValidPlayers) { if (player.Car != null) { player.Car.GetExternalData <LastMoveTimeData>().LastMoveTime = DistanceServerMain.UnixTime; SetStartingPlayer(player.UnityPlayerGuid, true); } } if (ServerStage == Stage.Starting) { ServerStage = Stage.Started; } }); OnAdvancingToNextLevel.Connect(() => { if (TipMessages.Count > 0) { var tip = TipMessages[currentTip]; currentTip = (currentTip + 1) % TipMessages.Count; Server.SayChat(DistanceChat.Server("AutoServer:Tip", tip)); } }); DistanceServerMain.GetEvent <Events.Instanced.Finished>().Connect((instance, data) => { Log.WriteLine($"{((DistanceCar)instance).Player.Name} finished"); AttemptToAdvanceLevel(); }); if (LoadWorkshopLevels) { DistanceServerMainStarter.Instance.StartCoroutine(DoLoadWorkshopLevels()); } else { StartAutoServer(); } }
System.Collections.IEnumerator UpdatePlaylist() { HashSet <string> foundLevels = new HashSet <string>(); List <DistanceSearchRetriever> levelRetrievers = new List <DistanceSearchRetriever>(); var filePath = new System.IO.FileInfo(Manager.ServerDirectory.FullName + "/BasicAutoServer.json"); if (filePath.Exists) { try { var txt = System.IO.File.ReadAllText(filePath.FullName); var reader = new JsonFx.Json.JsonReader(); var dictionary = (Dictionary <string, object>)reader.Read(txt); if (dictionary.ContainsKey("Workshop")) { Log.Info("Using workshop level info stored in BasicAutoServer.json"); var levelSettings = (object[])dictionary["Workshop"]; foreach (var settingsObject in levelSettings) { var settings = (Dictionary <string, object>)settingsObject; var search = (string)settings["Search"]; var sort = (WorkshopSearchParameters.SortType)Enum.Parse(typeof(WorkshopSearchParameters.SortType), (string)settings["Sort"]); var days = (int)settings["Days"]; var tagsBase = (object[])settings["Tags"]; var tagsList = new List <string>() { }; foreach (object tagBase in tagsBase) { tagsList.Add((string)tagBase); } var count = (int)settings["Count"]; var searchParams = WorkshopSearchParameters.GameFiles( searchText: search, appId: Workshop.DistanceAppId, sort: sort, days: days, requiredTags: tagsList.ToArray(), numPerPage: 30 ); var retriever = new DistanceSearchRetriever(new DistanceSearchParameters() { Search = searchParams, DistanceLevelFilter = (levels) => { levels.RemoveAll(level => { return(foundLevels.Contains(level.DistanceLevelResult.RelativeLevelPath)); }); foreach (var level in levels) { foundLevels.Add(level.DistanceLevelResult.RelativeLevelPath); } var res = FilterWorkshopLevels(levels); return(res); }, MaxSearch = count, GameMode = GameMode }, false); levelRetrievers.Add(retriever); } } } catch (Exception e) { Log.Error($"Error retrieving workshop level settings:\n{e}"); } } if (levelRetrievers == null || levelRetrievers.Count == 0) { Log.Error("No workshop levels defined. Using default: official levels"); Playlist = OfficialPlaylist; yield break; } var levelListIndex = 0; foreach (var retriever in levelRetrievers) { retriever.StartCoroutine(); yield return(retriever.TaskCoroutine); if (retriever.HasError) { Log.Error($"Error retrieving levels: {retriever.Error}"); } foreach (var levelInfo in retriever.Results) { levelInfo.DistanceLevelResult.AddExternalData(new ListIndexExternalData(levelListIndex)); } levelListIndex++; } Log.Info($"Level Retrievers: {levelRetrievers.Count}"); var levelResults = levelRetrievers.ConvertAll(retriever => retriever.Results.ConvertAll(result => result.DistanceLevelResult)); var results = Combine(levelResults.ToArray()); var listStr = $"Levels ({results.Count}):"; foreach (var level in results) { listStr += $"\n{level.GetExternalData<ListIndexExternalData>().listIndex} {level.Name}"; } Log.Info(listStr); if (results.Count == 0) { Log.Error("Workshop search returned nothing, using default: official levels"); Playlist = OfficialPlaylist; hasLoadedWorkshopLevels = false; yield break; } Playlist = results; currentLevelIndex = -1; if (!hasLoadedWorkshopLevels) { hasLoadedWorkshopLevels = true; Server.SayChat(DistanceChat.Server("BasicAutoServer:LoadedLevels", "Workshop playlist generated. Skipping to workshop playlist...")); FinishAllPlayersAndAdvanceLevel(); } }
void ProcessChatMessage(DistanceChat data) { Log.DebugLine("VC PC", 0); if (data.SenderGuid == "server") { Log.DebugLine("VC PC", 1); return; } Log.DebugLine("VC PC", 2); var player = Server.GetDistancePlayer(data.SenderGuid); if (player == null) { Log.DebugLine("VC PC", 3); return; } Log.DebugLine("VC PC", 4); var isMuted = false; if (TempMuted.ContainsKey(data.SenderGuid)) { var mutedUntil = TempMuted[data.SenderGuid]; if (mutedUntil > DistanceServerMain.UnixTime) { isMuted = true; } else { TempMuted.Remove(data.SenderGuid); } } var playerMatch = Regex.Match(data.Message, @"^\[[0-9A-F]{6}\](.+)\[FFFFFF\]: (.*)$"); var message = playerMatch.Groups[2].ToString(); Match match; match = Regex.Match(message, @"^/help$"); if (match.Success) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Help:Simple", "[00FFFF]/vote /skip /extend /restart /not /clear[-]")); } match = Regex.Match(message, @"^/skip$"); if (match.Success && SkipThreshold < 100 && SkipThreshold != -1) { if (isMuted) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Muted", "You are not allowed to vote while muted")); } else if (!SkipVotes.Contains(player.UnityPlayerGuid)) { SkipVotes.Add(player.UnityPlayerGuid); Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:SkipVoteAdded", $"Added your vote to skip the level {SkipVotes.Count}/{NeededVotesToSkipLevel}")); CheckForSkip(); } else { SkipVotes.Remove(player.UnityPlayerGuid); Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:SkipVoteRemoved", $"Removed your vote to skip the level {SkipVotes.Count}/{NeededVotesToSkipLevel}")); } return; } match = Regex.Match(message, @"^/extend$"); if (match.Success && SkipThreshold < 100 && SkipThreshold != -1) { if (autoServer.SprintMode == null) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:BadGameMode", $"You can only extend in Sprint.")); return; } if (isMuted) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Muted", "You are not allowed to vote while muted")); } else if (!ExtendVotes.Contains(player.UnityPlayerGuid)) { ExtendVotes.Add(player.UnityPlayerGuid); Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:ExtendVoteRemoved", $"Added your vote to extend the level {ExtendVotes.Count}/{NeededVotesToExtendLevel}")); CheckForExtend(); } else { ExtendVotes.Remove(player.UnityPlayerGuid); Server.SayChat(DistanceChat.Server("VoteCommands:Feedback:ExtendVoteRemoved", $"Removed your vote to extend the level {ExtendVotes.Count}/{NeededVotesToExtendLevel}")); } return; } match = Regex.Match(message, @"^/restart$"); if (match.Success) { if (autoServer.SprintMode == null) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:BadGameMode", $"You can only restart in Sprint.")); return; } var restartOkay = false; if (autoServer.ServerStage == BasicAutoServer.BasicAutoServer.Stage.Started) { if (!autoServer.SprintMode.TimeoutStarted || autoServer.SprintMode.LevelEndTime - DistanceServerMain.NetworkTime > 30) { restartOkay = true; } } if (player.State != DistancePlayer.PlayerState.StartedMode || player.Car == null) { restartOkay = false; } if (Server.IsInLobby) { restartOkay = false; } if (!restartOkay) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:NoRestart", $"You cannot restart right now")); return; } Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Restart", $"Restarting the level, just for you...")); DistanceServerMainStarter.Instance.StartCoroutine(RestartPlayerAfter(player, 2)); return; } var isVote = true; var isAgainst = false; string levelSearchName = null; match = Regex.Match(message, @"^/vote (.*)$"); if (!match.Success) { match = Regex.Match(message, @"^/not (.*)$"); isAgainst = true; } if (!match.Success) { match = Regex.Match(message, @"^/search (.*)$"); isVote = false; } if (match.Success) { levelSearchName = match.Groups[1].ToString(); } else { if (Regex.Match(message, @"^/vote$").Success || Regex.Match(message, @"^/search$").Success || Regex.Match(message, @"^/not$").Success) { if (PlayerVotes.ContainsKey(player.UnityPlayerGuid)) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Help:CurrentVote", $"Your current vote is for [00FF00]{PlayerVotes[player.UnityPlayerGuid].Name}[-]")); } Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Help:Detailed", "[00FFFF]/search name[-] to search\n[00FFFF]/clear[-] to clear your vote\n[00FFFF]/vote name[-] to vote for a level\n[00FFFF]/not name[-] to vote against a level\n[00FFFF]/skip[-] to vote to skip the level")); } else if (Regex.Match(message, @"^/clear$").Success) { string levelName = ""; if (PlayerVotes.ContainsKey(player.UnityPlayerGuid)) { levelName = $" for [00FF00]{PlayerVotes[player.UnityPlayerGuid].Name}[-]"; } PlayerVotes.Remove(player.UnityPlayerGuid); PlayerVoteTimes.Remove(player.UnityPlayerGuid); Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Feedback:LevelVoteAdded", $"Removed your vote" + levelName)); } return; } if (isMuted && isVote) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("VoteCommands:Muted", "You are not allowed to vote while muted")); return; } DistanceServerMainStarter.Instance.StartCoroutine(SearchForLevels(player, levelSearchName, isVote, isAgainst)); }
void OnAdvancingToNextLevel() { LastMapVoters.Clear(); var levelLookup = new Dictionary <string, DistanceLevel>(); var validVotes = new Dictionary <string, int>(); foreach (var vote in PlayerVotes) { var distancePlayer = Server.GetDistancePlayer(vote.Key); if (distancePlayer == null) { if (!LeftAt.ContainsKey(vote.Key) || DistanceServerMain.UnixTime - LeftAt[vote.Key] > 5 * 60) { PlayerVotes.Remove(vote.Key); PlayerVoteTimes.Remove(vote.Key); LeftAt.Remove(vote.Key); } } else { var isBadVoter = IsBadVoter(distancePlayer, DistanceServerMain.UnixTime); var isLateVote = PlayerVoteTimes[vote.Key] > DistanceServerMain.UnixTime - VoteNotSafetyTime; if (isBadVoter && isLateVote) { Server.SayLocalChat(distancePlayer.UnityPlayer, DistanceChat.Server("VoteCommands:LateVote", "Your vote has been skipped this round because you voted late!\nYour vote will apply on the next round.")); } else { int count = 0; validVotes.TryGetValue(vote.Value.RelativeLevelPath, out count); validVotes[vote.Value.RelativeLevelPath] = count + 1; if (!levelLookup.ContainsKey(vote.Value.RelativeLevelPath)) { levelLookup[vote.Value.RelativeLevelPath] = vote.Value; } } } } foreach (var pair in RecentMaps.ToArray()) { if (Server.CurrentLevelId > pair.Value) { RecentMaps.Remove(pair.Key); } } var votesSum = 0; foreach (var vote in validVotes.ToArray()) { var data = new FilterLevelRealtimeEventData(levelLookup[vote.Key]); OnFilterLevelRealtime.Fire(data); if (data.HardBlocklist || (data.SoftBlocklist && vote.Value < NeededVotesToOverrideSoftBlocklist)) { validVotes.Remove(vote.Key); } else { var value = vote.Value; if (AgainstVotes.ContainsKey(vote.Key)) { var count = 0; foreach (var guid in AgainstVotes[vote.Key]) { if (Server.GetDistancePlayer(guid) != null) { count++; value--; } } } if (value <= 0) { validVotes.Remove(vote.Key); } else { validVotes[vote.Key] = value; votesSum += value; } } } foreach (var pair in LeftAt.ToArray()) { if (DistanceServerMain.UnixTime - pair.Value > 5 * 60) { LeftAt.Remove(pair.Key); foreach (var votePair in AgainstVotes.ToArray()) { votePair.Value.Remove(pair.Key); if (votePair.Value.Count == 0) { AgainstVotes.Remove(votePair.Key); } } } } if (validVotes.Count == 0) { return; } var choiceInt = new Random().Next(votesSum); var choiceSum = 0; DistanceLevel level = null; foreach (var pair in validVotes) { choiceSum += pair.Value; if (choiceInt < choiceSum) { level = levelLookup[pair.Key]; break; } } if (level == null) { return; } autoServer.SetNextLevel(level); var voteCount = 0; string firstPlayer = null; foreach (var vote in PlayerVotes.ToArray()) { var distancePlayer = Server.GetDistancePlayer(vote.Key); if (vote.Value.RelativeLevelPath == level.RelativeLevelPath && distancePlayer != null) { voteCount++; PlayerVotes.Remove(vote.Key); PlayerVoteTimes.Remove(vote.Key); LastMapVoters.Add(new MapVoterInfo(distancePlayer)); if (firstPlayer == null) { firstPlayer = vote.Key; } } } var nextLevelId = Server.CurrentLevelId + 1; EventCleaner conns = new EventCleaner(); conns.Add( Server.OnModeStartedEvent.Connect(() => { var chat = DistanceChat.Server("VoteCommands:ChosenLevel", $"Chosen level is [00FF00]{level.Name}[-], voted for by {Server.GetDistancePlayer(firstPlayer).Name}" + (voteCount > 1 ? $" and {voteCount - 1} others" : "")); Server.SayChat(chat); conns.Clean(); }), Server.OnLevelStartInitiatedEvent.Connect(() => { if (Server.CurrentLevelId != nextLevelId) { conns.Clean(); } }) ); }
public void Update() { if (Finished) { return; } if (Plugin.ServerStage != BasicAutoServer.Stage.Started) { return; } var playersInMode = GetModePlayers(); if (playersInMode.Count <= 1) { IsInSinglePlayerMode = true; MaxModeTime += UnityEngine.Time.deltaTime; } else if (IsInSinglePlayerMode) { IsInSinglePlayerMode = false; // When transitioning out of single-player mode, give the bubble to the new player to reset the timer: if (TaggedPlayer != null) { var nonTagged = playersInMode.Find((player) => TaggedPlayer != player); if (nonTagged != null) { TaggedPlayer.GetExternalData <ReverseTagData>().SecondsTagged = 0.0; DistanceServerMainStarter.Instance.StartCoroutine(GiveTagBubbleSoon(TaggedPlayer, nonTagged)); } } } if (TaggedPlayer != null && !IsPlayerInMode(TaggedPlayer)) { TagRandomLastPlacePlayer(); if (!IsPlayerInMode(TaggedPlayer)) { TagPlayer(null); } } if (TaggedPlayer != null && !IsInSinglePlayerMode) { TaggedPlayer.GetExternalData <ReverseTagData>().AddSecondsTagged(UnityEngine.Time.deltaTime, WinTime); } var leader = GetFirstPlacePlayer(); if (leader != Leader) { if (leader != null) { var colorHex = Distance::ColorEx.ColorToHexNGUI(Distance::ColorEx.PlayerRainbowColor(leader.Index)); var nameColored = $"[{colorHex}]{leader.Name}[-]"; var chat = DistanceChat.Server("Vanilla:TakenTheLead", $"{nameColored} has taken the lead!"); chat.ChatType = DistanceChat.ChatTypeEnum.ServerVanilla; Plugin.Server.SayChat(chat); } Leader = leader; } var leaderSecondsTagged = Leader == null ? 0.0 : Leader.GetExternalData <ReverseTagData>().SecondsTagged; if (leaderSecondsTagged >= WinTime || Plugin.Server.ModeTime >= MaxModeTime) { Finished = true; if (Leader != null) { var networkFinishedData = new Distance::Events.ReverseTag.Finished.Data(Leader.Index, leaderSecondsTagged); DistanceServerMain.GetEvent <Events.ServerToClient.ReverseTagFinished>().Fire(UnityEngine.RPCMode.Others, networkFinishedData); } Log.Debug($"Advancing level because win condition met: {leaderSecondsTagged} >= {WinTime} || {Plugin.Server.ModeTime} > {MaxModeTime}"); Plugin.AdvanceLevel(); } if (Finished) { return; } AdvanceLevelIfOnlyFinishedPlayers(); var secondsLeft = Math.Max(0.0, Math.Min(WinTime - leaderSecondsTagged, MaxModeTime - Plugin.Server.ModeTime)); if (secondsLeft <= 30 && !HasShown30SecondWarning) { HasShown30SecondWarning = true; var colorHex = Distance::ColorEx.ColorToHexNGUI(Distance::Colors.orangeRed); var chat = DistanceChat.Server("Vanilla:TimeWarning", $"[{colorHex}]30 seconds left![-]"); chat.ChatType = DistanceChat.ChatTypeEnum.ServerVanilla; Plugin.Server.SayChat(chat); } else if (secondsLeft <= 10 && !HasShown10SecondWarning) { HasShown10SecondWarning = true; var colorHex = Distance::ColorEx.ColorToHexNGUI(Distance::Colors.orangeRed); var chat = DistanceChat.Server("Vanilla:TimeWarning", $"[{colorHex}]10 seconds left![-]"); chat.ChatType = DistanceChat.ChatTypeEnum.ServerVanilla; Plugin.Server.SayChat(chat); } }
/// public void Init() { DistanceServerMain.GetEvent <Events.ClientToServer.SubmitPlayerInfo>().Connect((data, info) => { // ignore data.sender_, it should be sender var player = GetDistancePlayer(info.sender); player.Name = data.playerName_; player.ReceivedInfo = true; player.Index = ValidPlayers.Count; player.ValidatedAt = DistanceServerMain.UnixTime; ValidPlayers.Add(player); player.OnValidatedPreReplicationEvent.Fire(); OnPlayerValidatedPreReplicationEvent.Fire(player); DistanceServerMain.GetEvent <Events.ServerToClient.AddClient>().Fire( RPCMode.Others, player.GetAddClientData(true) ); player.OnValidatedEvent.Fire(); OnPlayerValidatedEvent.Fire(player); }); DistanceServerMain.GetEvent <Events.ClientToServer.CompletedRequest>().Connect((data, info) => { // ignore data.networkPlayer_, it should be sender var distancePlayer = GetDistancePlayer(info.sender); if (data.request_ == Distance::ServerRequest.SubmitClientInfo) // TODO: check if request was actually completed for security { distancePlayer.State = DistancePlayer.PlayerState.Initialized; SendPlayerToCurrentLocation(data.networkPlayer_); } else if (data.request_ == Distance::ServerRequest.LoadLobbyScene) { distancePlayer.Stuck = false; distancePlayer.State = DistancePlayer.PlayerState.LoadedLobbyScene; SendLobbyServerInfo(data.networkPlayer_); SendLevelInfo(data.networkPlayer_); RequestLevelCompatibilityInfo(data.networkPlayer_); RequestSubmitLobbyInfo(data.networkPlayer_); if (!IsInLobby) { distancePlayer.State = DistancePlayer.PlayerState.CantLoadLevelSoInLobby; } } else if (data.request_ == Distance::ServerRequest.SubmitLobbyInfo) { distancePlayer.State = DistancePlayer.PlayerState.SubmittedLobbyInfo; } else if (data.request_ == Distance::ServerRequest.LoadGameModeScene) { distancePlayer.Stuck = false; distancePlayer.State = DistancePlayer.PlayerState.LoadedGameModeScene; if (distancePlayer.LevelId != CurrentLevelId) { SendPlayerToLevel(distancePlayer.UnityPlayer); return; } // fire CreatePlayer for sender for all existing cars (see WelcomeClientToGameMode.Data) foreach (var player in ValidPlayers) { if (player.Car != null) { DistanceServerMain.GetEvent <Events.ServerToClient.CreatePlayer>().Fire( data.networkPlayer_, new Distance::Events.ServerToClient.CreatePlayer.Data(player.Car.GetPlayerInitializeData(), player.Car.GetPlayerLateInitializeData(), player.Index) ); if (!player.Car.Finished && player.Car.Alive) { var gameObject = player.Car.Networker; var transform = gameObject.transform; DistanceServerMain.GetEvent <Events.ServerToClient.CreateExistingCar>().Fire( data.networkPlayer_, new Distance::Events.ServerToClient.CreateExistingCar.Data(transform.position, transform.rotation, player.Car.WingsOpen, player.Index) ); } } } if (HasModeStarted) { // TODO: sync game mode things if already started -- different for every game mode (see SyncLateClientToGameMode.Data and SyncMode.Data) // NOTE: this should be handled entirely by the game mode programming, not by the base server programming OnNeedToSyncLateClientToGameMode.Fire(distancePlayer); if (distancePlayer.Countdown != -1.0) { DistanceServerMain.GetEvent <Events.ServerToClient.FinalCountdownActivate>().Fire( data.networkPlayer_, new Distance::Events.RaceMode.FinalCountdownActivate.Data(distancePlayer.Countdown, (int)(distancePlayer.Countdown - ModeTime)) ); } } RequestSubmitGameModeInfo(data.networkPlayer_); } else if (data.request_ == Distance::ServerRequest.SubmitGameModeInfo) { distancePlayer.State = DistancePlayer.PlayerState.SubmittedGameModeInfo; // If mode has not started, try to start it (check if all players have submitted info/loaded) // If mode has started, set state to StartedMode and fire StartClientLate // (see ServerLogic.OnEventCompletedRequest) if (StartingMode) { AttemptToStartMode(); } else { distancePlayer.State = DistancePlayer.PlayerState.StartedMode; DistanceServerMain.GetEvent <Events.ServerToClient.StartMode>().Fire( data.networkPlayer_, new Distance::Events.ServerToClient.StartMode.Data(ModeStartTime, true) ); } } }); DistanceServerMain.GetEvent <Events.ClientToAllClients.ChatMessage>().Connect((data, info) => { var chatTypeData = new Tuple <DistanceChat.ChatTypeEnum, string>(DistanceChat.ChatTypeEnum.Unknown, "Unknown"); var distancePlayer = GetDistancePlayer(info.sender); if (distancePlayer != null && distancePlayer.Valid) { chatTypeData = DistanceChat.DeduceChatType(data.message_, distancePlayer.Name); } var chat = new DistanceChat(data.message_) { SenderGuid = info.sender.guid, ChatType = chatTypeData.Item1, ChatDescription = chatTypeData.Item2, }; AddChat(chat); }); DistanceServerMain.GetEvent <Events.ClientToAllClients.SetReady>().Connect((data, info) => { // ignore data.player_, it should be sender GetDistancePlayer(info.sender).Ready = data.ready_; }); DistanceServerMain.GetEvent <Events.ClientToServer.SubmitLevelCompatabilityInfo>().Connect((data, info) => { // ignore data.player_, it should be sender var distancePlayer = GetDistancePlayer(info.sender); distancePlayer.LevelCompatibilityInfo = new LevelCompatibilityInfo(data); Log.Debug($"Level compatibility versions test: {data.levelVersion_} : {CurrentLevel.LevelVersion}"); // TODO: write proper level compat check code (the current computed version is incorrect, so version checking is ignored) DistanceServerMain.GetEvent <Events.ServerToClient.UpdatePlayerLevelCompatibilityStatus>().Fire( data.player_, new Distance::Events.ServerToClient.UpdatePlayerLevelCompatibilityStatus.Data(info.sender, distancePlayer.LevelCompatability) ); if (distancePlayer.State == DistancePlayer.PlayerState.WaitingForCompatibilityStatus) { SendPlayerToLevel(distancePlayer.UnityPlayer); } }); DistanceServerMain.GetEvent <Events.ClientToServer.SubmitPlayerData>().Connect((data, info) => { // ignore data.player_, it should be sender var distancePlayer = GetDistancePlayer(info.sender); distancePlayer.RestartTime = -1.0; distancePlayer.Car = new DistanceCar(distancePlayer, data.data_); distancePlayer.Car.MakeNetworker(); DistanceServerMain.GetEvent <Events.ServerToClient.CreatePlayer>().Fire( RPCMode.Others, new Distance::Events.ServerToClient.CreatePlayer.Data(distancePlayer.Car.GetPlayerInitializeData(), distancePlayer.Car.GetPlayerLateInitializeData(), distancePlayer.Index) ); }); }
public override void Start() { ReadSettings(); Log.Info($"Starting HTTP Info Server on port {Port}"); worker = new ThreadWorker <RequestInfo>(); worker.QueueResponses = false; Server.OnUpdateEvent.Connect(() => { worker.Respond(info => { info.Request.Respond(); return(info.Request); }); }); var listener = new HttpListener(); Server.OnDestroyEvent.Connect(() => { listener.Abort(); }); listener.Prefixes.Add($"http://*:{Port}/"); if (PortHttps >= 0 && PortHttps != Port) { listener.Prefixes.Add($"https://*:{PortHttps}/"); } listener.Start(); listener.BeginGetContext(listenerCallback, listener); Log.Debug($"Started HTTP(S) Info Server on port {Port}"); DistanceServerMain.GetEvent <Events.ClientToAllClients.ChatMessage>().Connect((data, info) => { var playerMatch = Regex.Match(data.message_, @"^\[[0-9A-F]{6}\](.*?)\[FFFFFF\]: (.*)$"); if (!playerMatch.Success) { return; } var playerName = Regex.Replace(playerMatch.Groups[1].ToString(), @"\[.*\]", "").ToLower(); var player = Server.ValidPlayers.Find(distPlayer => distPlayer.Name.ToLower() == Regex.Replace(playerMatch.Groups[1].ToString(), @"\[.*\]", "").ToLower()); if (player == null) { return; } var message = playerMatch.Groups[2].ToString(); Match match; match = Regex.Match(message, @"^/unlink$"); if (match.Success) { var keysToRemove = new List <string>(); foreach (var pair in Links) { if (pair.Value == player.UnityPlayerGuid) { keysToRemove.Add(pair.Key); } } foreach (var key in keysToRemove) { Links.Remove(key); } Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("HttpServer:Link", $"Your game session has been unlinked from {keysToRemove.Count} web session{(keysToRemove.Count == 1 ? "s" : "")}")); return; } match = Regex.Match(message, @"^/link (\w{6})$"); if (match.Success) { var code = match.Groups[1].ToString().ToUpper(); if (!CodesReverse.ContainsKey(code)) { var add = ""; if (HelpTextWebsite != null) { add = $"\nVisit {HelpTextWebsite.Replace("$linkcode", GetOrGenerateCode(player.UnityPlayerGuid))} to view and vote online."; } Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("HttpServer:Link", "Invalid link code!" + add)); } Links[CodesReverse[code]] = player.UnityPlayerGuid; CodesReverse.Remove(code); Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("HttpServer:Link", $"Your game session has been linked to a web session!\nType [00FFFF]/unlink[-] to undo this.")); return; } match = Regex.Match(message, @"^/link"); if (match.Success) { if (HelpTextWebsite != null) { Server.SayLocalChat(player.UnityPlayer, DistanceChat.Server("HttpServer:Link", $"Visit {HelpTextWebsite.Replace("$linkcode", GetOrGenerateCode(player.UnityPlayerGuid))} to view and vote online.")); } return; } }); Manager.Server.OnPlayerValidatedEvent.Connect(player => { GetOrGenerateCode(player.UnityPlayerGuid); }); var autoServer = Manager.GetPlugin <BasicAutoServer.BasicAutoServer>(); if (autoServer != null) { Log.Debug("Set LinkCodeGetter in BasicAutoServer"); autoServer.LinkCodeGetter = player => { return(GetOrGenerateCode(player.UnityPlayerGuid)); }; } Manager.Server.OnPlayerDisconnectedEvent.Connect(player => { Expiry[player.UnityPlayerGuid] = DistanceServerMain.UnixTime + 5 * 60; string keyToRemove = null; foreach (var pair in CodesForward) { if (pair.Value == player.UnityPlayerGuid) { keyToRemove = pair.Key; break; } } if (keyToRemove != null) { CodesForward.Remove(keyToRemove); } }); double lastUpdate = 0; Manager.Server.OnUpdateEvent.Connect(() => { var now = DistanceServerMain.UnixTime; if (now - lastUpdate >= 60) { lastUpdate = now; var KeysToRemove = new List <string>(); foreach (var pair in Links) { if (IsExpired(pair.Key, now) || IsExpired(pair.Value, now)) { KeysToRemove.Add(pair.Key); } } foreach (var key in KeysToRemove) { Links.Remove(key); } KeysToRemove.Clear(); foreach (var pair in CodesForward) { if (IsExpired(pair.Value, now)) { KeysToRemove.Add(pair.Key); } } foreach (var key in KeysToRemove) { CodesForward.Remove(key); } KeysToRemove.Clear(); foreach (var pair in CodesReverse) { if (IsExpired(pair.Value, now)) { KeysToRemove.Add(pair.Key); } } foreach (var key in KeysToRemove) { CodesReverse.Remove(key); } } }); Log.Debug($"Started handling code linking"); }
void ProcessChatMessage(DistanceChat data) { if (data.SenderGuid == "server") { return; } var player = Server.GetDistancePlayer(data.SenderGuid); if (player == null) { return; } var isMuted = false; if (TempMuted.ContainsKey(data.SenderGuid)) { var mutedUntil = TempMuted[data.SenderGuid]; if (mutedUntil > DistanceServerMain.UnixTime) { isMuted = true; } else { TempMuted.Remove(data.SenderGuid); } } if (!isMuted) { var playerMatch = Regex.Match(data.Message, @"^\[[0-9A-F]{6}\](.+)\[FFFFFF\]: (.*)$"); var message = playerMatch.Groups[2].ToString(); var safeMessage = Regex.Replace(ChatFilter.CharReplacement.ConvertTextToBase(message), @"[^a-zA-Z0-9 ]", ""); var isMessageBad = false; foreach (var word in FilteredWords) { if (Regex.IsMatch(message, word, RegexOptions.IgnoreCase) || Regex.IsMatch(safeMessage, word, RegexOptions.IgnoreCase)) { isMessageBad = true; LogMessage($"Player {player.UnityPlayerGuid} ({player.Name}) said a filtered word: {word} FROM {message} ({safeMessage})"); break; } } if (!isMessageBad) { return; } int currentLevel = -1; PlayerLevels.TryGetValue(player.UnityPlayerGuid, out currentLevel); currentLevel = Math.Min(PunishLevels.Count - 2, currentLevel); Log.Debug($"PunishLevels: {PunishLevels.Count}"); if (PunishLevels.Count == 0) { LogMessage($" Cannot punish because the next punishment level ({currentLevel + 1}) does not exist. (max {PunishLevels.Count - 1})"); WriteLogIfNecessary(); } else { currentLevel = currentLevel + 1; PlayerLevels[player.UnityPlayerGuid] = currentLevel; var punishment = PunishLevels[currentLevel]; TempMuted[player.UnityPlayerGuid] = DistanceServerMain.UnixTime + Math.Abs(punishment); var votePlugin = Manager.GetPlugin <VoteCommands.VoteCommands>(); if (votePlugin != null) { votePlugin.Mute(player.UnityPlayerGuid, TempMuted[player.UnityPlayerGuid]); } WritePersistentStateIfNecessary(); LogMessage($" Muting (shadow: {(punishment < 0 ? "yes" : "no")}) for {Math.Abs(punishment)} seconds (level {currentLevel})"); WriteLogIfNecessary(); if (punishment > 0) { player.SayLocalChat(DistanceChat.Server("ChatFilter:Muted", $"[FF0000]Be nice![-] You are muted for {Math.Abs(punishment)} seconds.")); } } } var isShadowMuted = false; int level = 0; PlayerLevels.TryGetValue(player.UnityPlayerGuid, out level); isShadowMuted = level < PunishLevels.Count && PunishLevels[level] < 0; if (isShadowMuted) { Server.DeleteChatMessage(data); foreach (var otherPlayer in Server.ValidPlayers) { if (otherPlayer != player) { otherPlayer.DeleteChatMessage(data, true); } } } else { Server.DeleteChatMessage(data, true); } data.Blocked = true; }