using System.Reflection; using System.Text; using HarmonyLib; using Sandbox.Engine.Multiplayer; using Sandbox.Game; using Sandbox.Game.Gui; using Sandbox.Game.GUI; using Sandbox.Game.Localization; using Sandbox.Game.Multiplayer; using Sandbox.Game.SessionComponents; using Sandbox.Game.World; using Sandbox.Graphics.GUI; using SeamlessClientPlugin.Messages; using VRage; using VRage.Audio; using VRage.Game; using VRage.Utils; using VRageMath; namespace SeamlessClientPlugin.Utilities; public class OnlinePlayers { private static readonly Harmony Patcher = new("OnlinePlayersPatcher"); public static List AllServers = []; public static int CurrentServer; private static string _currentServerName; public static int TotalPlayerCount; public static int CurrentPlayerCount; public static void Patch() { var recreateControls = typeof(MyGuiScreenPlayers).GetMethod("RecreateControls", BindingFlags.Instance | BindingFlags.Public); var updateCaption = typeof(MyGuiScreenPlayers).GetMethod("UpdateCaption", BindingFlags.Instance | BindingFlags.NonPublic); Patcher.Patch(recreateControls, new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsPrefix)))); Patcher.Patch(updateCaption, new HarmonyMethod(GetPatchMethod(nameof(UpdateCaption)))); //Patcher.Patch(recreateControls, postfix: new HarmonyMethod(GetPatchMethod(nameof(RecreateControlsSuffix)))); } public static bool RecreateControlsPrefix(MyGuiScreenPlayers __instance, bool constructor) { if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true; try { __instance.Controls.Clear(); __instance.Elements.Clear(); //__instance.Elements.Add(m_cl); __instance.FocusedControl = null; //__instance.m_firstUpdateServed = false; //__instance.m_screenCreation = DateTime.UtcNow; //__instance.m_gamepadHelpInitialized = false; //__instance.m_gamepadHelpLabel = null; //SeamlessClient.TryShow("A"); //__instance.RecreateControls(constructor); __instance.Size = new Vector2(0.937f, 0.913f); __instance.CloseButtonEnabled = true; //SeamlessClient.TryShow("A2"); //MyCommonTexts.ScreenCaptionPlayers //MyStringId ID = MyStringId.GetOrCompute("Test Caption"); __instance.m_caption = __instance.AddCaption(MyCommonTexts.ScreenCaptionPlayers, null, new Vector2(0f, 0.003f)); const float startX = -0.435f; const float startY = -0.36f; var myGuiControlSeparatorList = new MyGuiControlSeparatorList(); myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, startY), .83f); var start = new Vector2(startX, 0.358f); myGuiControlSeparatorList.AddHorizontal(start, 0.728f); myGuiControlSeparatorList.AddHorizontal(new Vector2(startX, 0.05f), 0.17f); __instance.Controls.Add(myGuiControlSeparatorList); var spacing = new Vector2(0f, 0.057f); var vector3 = new Vector2(startX, startY + 0.035f); //SeamlessClient.TryShow("B"); var mProfileButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Profile)); mProfileButton.ButtonClicked += __instance.profileButton_ButtonClicked; __instance.Controls.Add(mProfileButton); vector3 += spacing; __instance.m_profileButton = mProfileButton; var mPromoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Promote)); mPromoteButton.ButtonClicked += __instance.promoteButton_ButtonClicked; __instance.Controls.Add(mPromoteButton); vector3 += spacing; __instance.m_promoteButton = mPromoteButton; var mDemoteButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Demote)); mDemoteButton.ButtonClicked += __instance.demoteButton_ButtonClicked; __instance.Controls.Add(mDemoteButton); vector3 += spacing; __instance.m_demoteButton = mDemoteButton; var mKickButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Kick)); mKickButton.ButtonClicked += __instance.kickButton_ButtonClicked; __instance.Controls.Add(mKickButton); vector3 += spacing; __instance.m_kickButton = mKickButton; var mBanButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ban)); mBanButton.ButtonClicked += __instance.banButton_ButtonClicked; __instance.Controls.Add(mBanButton); vector3 += spacing; __instance.m_banButton = mBanButton; var mTradeButton = new MyGuiControlButton(vector3, MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MySpaceTexts.PlayersScreen_TradeBtn)); mTradeButton.SetTooltip(MyTexts.GetString(MySpaceTexts.PlayersScreen_TradeBtn_TTP)); mTradeButton.ButtonClicked += __instance.tradeButton_ButtonClicked; __instance.Controls.Add(mTradeButton); __instance.m_tradeButton = mTradeButton; //SeamlessClient.TryShow("C"); var vector4 = vector3 + new Vector2(-0.002f, mTradeButton.Size.Y + 0.03f); __instance.m_lobbyTypeCombo = new MyGuiControlCombobox(vector4, null, null, null, 3); var vector5 = vector4 + new Vector2(0f, 0.05f); vector5 += new Vector2(0f, 0.03f); __instance.m_maxPlayers = Sync.IsServer ? MyMultiplayerLobby.MAX_PLAYERS : 16; __instance.m_maxPlayersSlider = new MyGuiControlSlider(vector5, 2f, Math.Max(__instance.m_maxPlayers, 3), 0.177f, Sync.IsServer ? MySession.Static.MaxPlayers : MyMultiplayer.Static.MemberLimit, null, null, 1, 0.8f, 0f, "White", null, MyGuiControlSliderStyleEnum.Default, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, true); var mInviteButton = new MyGuiControlButton(new Vector2(startX, 0.25000026f), MyGuiControlButtonStyleEnum.Default, null, null, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, null, MyTexts.Get(MyCommonTexts.ScreenPlayers_Invite)); mInviteButton.ButtonClicked += __instance.inviteButton_ButtonClicked; __instance.Controls.Add(mInviteButton); __instance.m_inviteButton = mInviteButton; var vector6 = new Vector2(-startX - 0.034f, startY + 0.033f); var size = new Vector2(0.66f, 1.2f); var num2 = 18; var num3 = 0f; //SeamlessClient.TryShow("D"); var component = MySession.Static.GetComponent(); if (component.IsEnabled) { var vector7 = __instance.GetPositionAbsolute() + vector6 + new Vector2(0f - size.X, 0f); var mWarfareTimeRemaintingLabel = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f)) { Text = $"{MyTexts.GetString(MySpaceTexts.WarfareCounter_TimeRemaining)}: " }; __instance.Controls.Add(mWarfareTimeRemaintingLabel); __instance.m_warfare_timeRemainting_label = mWarfareTimeRemaintingLabel; var timeSpan = TimeSpan.FromMinutes(component.RemainingMinutes); var mWarfareTimeRemaintingTime = new MyGuiControlLabel(vector6 - new Vector2(size.X, 0f) + new Vector2( mWarfareTimeRemaintingLabel.Size.X, 0f)) { Text = timeSpan.ToString(timeSpan.TotalHours >= 1.0 ? @"hh\:mm\:ss" : "mm\\:ss") }; __instance.Controls.Add(mWarfareTimeRemaintingTime); __instance.m_warfare_timeRemainting_time = mWarfareTimeRemaintingTime; var num4 = 0.09f; var num5 = size.X / 3f - 2f * num3; var num6 = 0; var allFactions = MySession.Static.Factions.GetAllFactions(); foreach (var myFaction in allFactions) if ((myFaction.Name.StartsWith("Red") || myFaction.Name.StartsWith("Green") || myFaction.Name.StartsWith("Blue")) && myFaction.Name.EndsWith("Faction")) { __instance.Controls.Add(new MyGuiScreenPlayersWarfareTeamScoreTable( vector7 + new Vector2(num6 * (num5 + num3), mWarfareTimeRemaintingLabel.Size.Y + num3), num5, num4, myFaction.Name, myFaction.FactionIcon.Value.String, MyTexts.GetString(MySpaceTexts.WarfareCounter_EscapePod), myFaction.FactionId, false, true, myFaction.IsMember(MySession.Static.LocalHumanPlayer.Identity.IdentityId))); num6++; } vector6.Y += mWarfareTimeRemaintingLabel.Size.Y + num4 + num3 * 2f; num2 -= 3; } //SeamlessClient.TryShow("E"); __instance.m_playersTable = new MyGuiControlTable { Position = vector6, Size = size, OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_RIGHT_AND_VERTICAL_TOP, ColumnsCount = 7, //SeamlessClient.TryShow("F"); GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_PlayersList, VisibleRowsCount = num2 }; const float playerNameWidth = 0.2f; const float rankWidth = 0.1f; const float pingWidth = 0.08f; const float mutedWidth = 0.1f; const float steamIconWidth = 0.04f; const float serverWidth = 0.20f; const float factionNameWidth = 1f - playerNameWidth - rankWidth - mutedWidth - pingWidth - steamIconWidth - serverWidth; __instance.m_playersTable.SetCustomColumnWidths([ steamIconWidth, playerNameWidth, factionNameWidth, rankWidth, pingWidth, mutedWidth, serverWidth ]); //SeamlessClient.TryShow("G"); __instance.m_playersTable.SetColumnComparison(1, (a, b) => a.Text.CompareToIgnoreCase(b.Text)); __instance.m_playersTable.SetColumnName(1, MyTexts.Get(MyCommonTexts.ScreenPlayers_PlayerName)); __instance.m_playersTable.SetColumnComparison(2, (a, b) => a.Text.CompareToIgnoreCase(b.Text)); __instance.m_playersTable.SetColumnName(2, MyTexts.Get(MyCommonTexts.ScreenPlayers_FactionName)); __instance.m_playersTable.SetColumnName(5, new StringBuilder(MyTexts.GetString(MyCommonTexts.ScreenPlayers_Muted))); __instance.m_playersTable.SetColumnComparison(3, GameAdminCompare); __instance.m_playersTable.SetColumnName(3, MyTexts.Get(MyCommonTexts.ScreenPlayers_Rank)); __instance.m_playersTable.SetColumnComparison(4, GamePingCompare); __instance.m_playersTable.SetColumnName(4, MyTexts.Get(MyCommonTexts.ScreenPlayers_Ping)); var colName = new StringBuilder("Server"); __instance.m_playersTable.SetColumnName(6, colName); __instance.m_playersTable.SetColumnComparison(6, (a, b) => a.Text.CompareToIgnoreCase(b.Text)); //SeamlessClient.TryShow("H"); //m_PlayersTable_ItemSelected __instance.m_playersTable.ItemSelected += __instance.playersTable_ItemSelected; __instance.m_playersTable.UpdateTableSortHelpText(); __instance.Controls.Add(__instance.m_playersTable); var thisServerName = ""; TotalPlayerCount = 0; foreach (var server in AllServers) { var servername = server.ServerName; if (server.ServerId == CurrentServer) { thisServerName = servername; _currentServerName = servername; continue; } foreach (var player in server.Players) { AddPlayer(__instance, player.SteamId, servername, player.PlayerName, player.IdentityId); TotalPlayerCount++; } } CurrentPlayerCount = 0; foreach (var onlinePlayer in Sync.Players.GetOnlinePlayers()) { if (onlinePlayer.Id.SerialId != 0) continue; CurrentPlayerCount++; TotalPlayerCount++; AddPlayer(__instance, onlinePlayer.Id.SteamId, thisServerName); } //SeamlessClient.TryShow("I"); if (__instance.m_lastSelected != 0L) { var row2 = __instance.m_playersTable.Find(r => (ulong)r.UserData == __instance.m_lastSelected); if (row2 != null) __instance.m_playersTable.SelectedRow = row2; } __instance.UpdateButtonsEnabledState(); //UpdateButtonsEnabledState(); __instance.UpdateCaption(); var minSizeGui = MyGuiControlButton.GetVisualStyle(MyGuiControlButtonStyleEnum.Default).NormalTexture .MinSizeGui; var myGuiControlLabel = new MyGuiControlLabel(new Vector2(start.X, start.Y + minSizeGui.Y / 2f)) { Name = MyGuiScreenBase.GAMEPAD_HELP_LABEL_NAME }; __instance.Controls.Add(myGuiControlLabel); __instance.GamepadHelpTextId = MySpaceTexts.PlayersScreen_Help_Screen; __instance.FocusedControl = __instance.m_playersTable; //SeamlessClient.TryShow("J"); } catch (Exception ex) { SeamlessClient.TryShow(ex.ToString()); __instance.Controls.Clear(); return true; } return false; } private static bool AddPlayer(MyGuiScreenPlayers instance, ulong userId, string server, string playerName = null, long playerId = 0) { var table = instance.m_playersTable; var pings = instance.pings; playerName ??= MyMultiplayer.Static.GetMemberName(userId); if (string.IsNullOrEmpty(playerName)) return false; var row = new MyGuiControlTable.Row(userId); var memberServiceName = MyMultiplayer.Static.GetMemberServiceName(userId); var text = new StringBuilder(); MyGuiHighlightTexture? icon = new MyGuiHighlightTexture { Normal = $@"Textures\GUI\Icons\Services\{memberServiceName}.png", Highlight = $@"Textures\GUI\Icons\Services\{memberServiceName}.png", SizePx = new Vector2(24f, 24f) }; row.AddCell(new MyGuiControlTable.Cell(text, null, memberServiceName, Color.White, icon, MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_CENTER)); row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(playerName), playerName)); if (playerId == 0) playerId = Sync.Players.TryGetIdentityId(userId); var playerFaction = MySession.Static.Factions.GetPlayerFaction(playerId); var text2 = ""; var stringBuilder = new StringBuilder(); if (playerFaction != null) { text2 += playerFaction.Name; text2 = $"{text2} | {playerName}"; foreach (var member in playerFaction.Members) if ((member.Value.IsLeader || member.Value.IsFounder) && MySession.Static.Players.TryGetPlayerId(member.Value.PlayerId, out var result) && MySession.Static.Players.TryGetPlayerById(result, out var player)) { text2 = $"{text2} | {player.DisplayName}"; break; } stringBuilder.Append(MyStatControlText.SubstituteTexts(playerFaction.Name)); if (playerFaction.IsLeader(playerId)) stringBuilder.Append(" (").Append((object)MyTexts.Get(MyCommonTexts.Leader)).Append(")"); if (!string.IsNullOrEmpty(playerFaction.Tag)) stringBuilder.Insert(0, $"[{playerFaction.Tag}] "); } row.AddCell(new MyGuiControlTable.Cell(stringBuilder, null, text2)); var stringBuilder2 = new StringBuilder(); var userPromoteLevel = MySession.Static.GetUserPromoteLevel(userId); for (var i = 0; i < (int)userPromoteLevel; i++) stringBuilder2.Append('*'); row.AddCell(new MyGuiControlTable.Cell(stringBuilder2)); if (pings.TryGetValue(userId, out var ping)) row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(ping.ToString()))); else row.AddCell(new MyGuiControlTable.Cell(new StringBuilder("----"))); var cell = new MyGuiControlTable.Cell(new StringBuilder("")); row.AddCell(cell); if (userId != Sync.MyId) { var myGuiControlButton = new MyGuiControlButton { CustomStyle = instance.m_buttonSizeStyleSilent, Size = new Vector2(0.03f, 0.04f), CueEnum = GuiSounds.None }; myGuiControlButton.ButtonClicked += instance.OnToggleMutePressed; myGuiControlButton.UserData = userId; cell.Control = myGuiControlButton; table.Controls.Add(myGuiControlButton); instance.RefreshMuteIcons(); //RefreshMuteIcons(); } table.Add(row); instance.UpdateCaption(); row.AddCell(new MyGuiControlTable.Cell(new StringBuilder(server), "Server Name")); return false; } private static bool UpdateCaption(MyGuiScreenPlayers __instance) { if (MyMultiplayer.Static != null && MyMultiplayer.Static.IsLobby) return true; //string s = $"{MyTexts.Get(MyCommonTexts.ScreenCaptionServerName).ToString()} - SectorPlayers: ({ mm_playersTable.RowsCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ( {5} / 200 )"; __instance.m_caption.Text = $"Server: {_currentServerName} - Innstance Players ({CurrentPlayerCount} / {MySession.Static.MaxPlayers}) TotalPlayers: ({TotalPlayerCount})"; return false; } private static int GamePingCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) { if (!int.TryParse(a.Text.ToString(), out var result)) result = -1; if (!int.TryParse(b.Text.ToString(), out var result2)) result2 = -1; return result.CompareTo(result2); } private static int GameAdminCompare(MyGuiControlTable.Cell a, MyGuiControlTable.Cell b) { var steamId = (ulong)a.Row.UserData; var steamId2 = (ulong)b.Row.UserData; var userPromoteLevel = (int)MySession.Static.GetUserPromoteLevel(steamId); var userPromoteLevel2 = (int)MySession.Static.GetUserPromoteLevel(steamId2); return userPromoteLevel.CompareTo(userPromoteLevel2); } private static MethodInfo GetPatchMethod(string v) { return typeof(OnlinePlayers).GetMethod(v, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } }