Compare commits
5 Commits
023602e364
...
v4.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40795a2f49 | ||
|
|
e157e24236 | ||
|
|
dfb535e4b7 | ||
|
|
b98ae261d5 | ||
|
|
204fda4f59 |
32
Gui/DefaultWidgets.cs
Normal file
32
Gui/DefaultWidgets.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
namespace stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认组件注册入口
|
||||||
|
/// 在 Plugin.Awake() 末尾调用 RegisterAll()
|
||||||
|
/// 配置判断在此集中处理,各组件文件只提供纯 Create() 工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class DefaultWidgets
|
||||||
|
{
|
||||||
|
public static void RegisterAll()
|
||||||
|
{
|
||||||
|
// Header 组件
|
||||||
|
if (PlayerManager.ShowRoomPublicityLabel.Value)
|
||||||
|
RegisterManager.RegisterHeaderRow(InternalWidgets.RoomPublicityLabel.Create());
|
||||||
|
|
||||||
|
if (PlayerManager.ShowRoomPublicityButton.Value)
|
||||||
|
RegisterManager.RegisterHeaderRow(InternalWidgets.RoomPublicityButton.Create());
|
||||||
|
|
||||||
|
// 玩家行组件(始终注册)
|
||||||
|
RegisterManager.RegisterPlayerRow(InternalWidgets.PlayerInfoButton.Create());
|
||||||
|
|
||||||
|
// 玩家行组件(按配置决定)
|
||||||
|
if (PlayerManager.ShowRecordButton.Value)
|
||||||
|
RegisterManager.RegisterPlayerRow(InternalWidgets.RecordButton.Create());
|
||||||
|
|
||||||
|
if (PlayerManager.ShowKickButton.Value)
|
||||||
|
RegisterManager.RegisterPlayerRow(InternalWidgets.KickButton.Create());
|
||||||
|
|
||||||
|
if (PlayerManager.ShowNameToggleButton.Value)
|
||||||
|
RegisterManager.RegisterPlayerRow(InternalWidgets.NameToggleButton.Create());
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Gui/InternalWidgets/KickButton.cs
Normal file
25
Gui/InternalWidgets/KickButton.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using MultiplayerBasicExample;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 踢人按钮的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class KickButton
|
||||||
|
{
|
||||||
|
public static IPlayerRowWidget Create()
|
||||||
|
{
|
||||||
|
return new PlayerRowButtonWidget
|
||||||
|
{
|
||||||
|
Name = "Kick",
|
||||||
|
Condition = player =>
|
||||||
|
!PlayerManager.IsHost(player)
|
||||||
|
&& !PlayerManager.IsLocalPlayer(player),
|
||||||
|
GetText = _ => "Kick!",
|
||||||
|
OnClick = player =>
|
||||||
|
PlayerManager.KickPlayer(player.SteamId, MultiplayerManager.KickResponse.DidNotRecievePackages),
|
||||||
|
Width = 100f,
|
||||||
|
Style = PlayerManagerGUI.CommonButtonStyle,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Gui/InternalWidgets/NameToggleButton.cs
Normal file
20
Gui/InternalWidgets/NameToggleButton.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 曾用名切换按钮的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class NameToggleButton
|
||||||
|
{
|
||||||
|
public static IPlayerRowWidget Create()
|
||||||
|
{
|
||||||
|
return new PlayerRowButtonWidget
|
||||||
|
{
|
||||||
|
Name = "NameToggle",
|
||||||
|
Condition = player => player.HasRecordedName,
|
||||||
|
GetText = player => player.DisplayNameRecorded ? "显示现用名" : "显示曾用名",
|
||||||
|
OnClick = player => player.DisplayNameRecorded = !player.DisplayNameRecorded,
|
||||||
|
Width = 120f,
|
||||||
|
Style = PlayerManagerGUI.CommonButtonStyle,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Gui/InternalWidgets/PlayerInfoButton.cs
Normal file
33
Gui/InternalWidgets/PlayerInfoButton.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Steamworks;
|
||||||
|
using UnityEngine;
|
||||||
|
using stick.plugins.playermanager.Recorder;
|
||||||
|
using static stick.plugins.playermanager.Gui.PlayerManagerGUI;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家信息按钮的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class PlayerInfoButton
|
||||||
|
{
|
||||||
|
public static IPlayerRowWidget Create()
|
||||||
|
{
|
||||||
|
return new PlayerRowButtonWidget
|
||||||
|
{
|
||||||
|
Name = "PlayerInfo",
|
||||||
|
GetText = player => player.ToString(),
|
||||||
|
OnClick = player =>
|
||||||
|
{
|
||||||
|
GUIUtility.systemCopyBuffer = player.ToString();
|
||||||
|
SteamFriends.ActivateGameOverlayToUser("steamid", player.SteamId);
|
||||||
|
},
|
||||||
|
Width = 500f,
|
||||||
|
GetStyle = player => player.RecordStatus switch
|
||||||
|
{
|
||||||
|
PlayerRecordStatus.Blacklisted => RedButtonStyle,
|
||||||
|
PlayerRecordStatus.Recorded => YellowButtonStyle,
|
||||||
|
_ => player.IsFriend ? GreenButtonStyle : CommonButtonStyle,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
47
Gui/InternalWidgets/RecordButton.cs
Normal file
47
Gui/InternalWidgets/RecordButton.cs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录/拉黑按钮的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class RecordButton
|
||||||
|
{
|
||||||
|
public static IPlayerRowWidget Create()
|
||||||
|
{
|
||||||
|
return new PlayerRowButtonWidget
|
||||||
|
{
|
||||||
|
Name = "Record",
|
||||||
|
GetText = player => player.RecordStatus switch
|
||||||
|
{
|
||||||
|
PlayerRecordStatus.NotRecorded => "记录",
|
||||||
|
PlayerRecordStatus.Recorded => "拉黑",
|
||||||
|
PlayerRecordStatus.Blacklisted => "取消",
|
||||||
|
_ => "未知状态",
|
||||||
|
},
|
||||||
|
OnClick = player =>
|
||||||
|
{
|
||||||
|
switch (player.RecordStatus)
|
||||||
|
{
|
||||||
|
case PlayerRecordStatus.NotRecorded:
|
||||||
|
PlayerManager.playerRecorder.RecordPlayer(player);
|
||||||
|
break;
|
||||||
|
case PlayerRecordStatus.Recorded:
|
||||||
|
PlayerManager.blackListRecorder.RecordPlayer(player);
|
||||||
|
break;
|
||||||
|
case PlayerRecordStatus.Blacklisted:
|
||||||
|
PlayerManager.playerRecorder.CancelRecordPlayer(player);
|
||||||
|
PlayerManager.blackListRecorder.CancelRecordPlayer(player);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Debug.LogError($"未知的记录状态: {player.RecordStatus}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PlayerManager.OnLobbyMembersChanged();
|
||||||
|
},
|
||||||
|
Width = 100f,
|
||||||
|
Style = PlayerManagerGUI.CommonButtonStyle,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Gui/InternalWidgets/RoomPublicityButton.cs
Normal file
29
Gui/InternalWidgets/RoomPublicityButton.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using static stick.plugins.playermanager.Gui.PlayerManagerGUI;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换公开性按钮的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class RoomPublicityButton
|
||||||
|
{
|
||||||
|
public static IHeaderRow Create()
|
||||||
|
{
|
||||||
|
return new HeaderRow
|
||||||
|
{
|
||||||
|
Name = "RoomPublicityButton",
|
||||||
|
Condition = () => PlayerManager.IsHost(PlayerManager.LocalPlayer),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new HeaderButtonWidget
|
||||||
|
{
|
||||||
|
Name = "RoomPublicityButton.Content",
|
||||||
|
Text = "切换公开性",
|
||||||
|
Action = () => PlayerManager.ToggleLobbyPublicity(),
|
||||||
|
Width = 150f,
|
||||||
|
Style = CommonButtonStyle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
28
Gui/InternalWidgets/RoomPublicityLabel.cs
Normal file
28
Gui/InternalWidgets/RoomPublicityLabel.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using static stick.plugins.playermanager.Gui.PlayerManagerGUI;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui.InternalWidgets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 房间类型标签的纯工厂
|
||||||
|
/// </summary>
|
||||||
|
public static class RoomPublicityLabel
|
||||||
|
{
|
||||||
|
public static IHeaderRow Create()
|
||||||
|
{
|
||||||
|
return new HeaderRow
|
||||||
|
{
|
||||||
|
Name = "RoomPublicityLabel",
|
||||||
|
Condition = () => PlayerManager.IsHost(PlayerManager.LocalPlayer),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new HeaderLabelWidget
|
||||||
|
{
|
||||||
|
Name = "RoomPublicityLabel.Content",
|
||||||
|
Content = () => $"房间类型: {(PlayerManager.IsPublic ? "公开" : "好友可见")}",
|
||||||
|
Width = 200f,
|
||||||
|
Style = TitleStyle,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
85
Gui/LayoutCursor.cs
Normal file
85
Gui/LayoutCursor.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布局游标,统一管理 GUI 绘制时的行列定位
|
||||||
|
/// Header 区域纵向排列 Row,Row 内横向排列 Widget
|
||||||
|
/// 玩家区域每玩家一行,行内横向排列 Widget
|
||||||
|
/// </summary>
|
||||||
|
public class LayoutCursor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 左侧偏移量
|
||||||
|
/// </summary>
|
||||||
|
public float OffsetX { get; set; } = 10f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 顶部偏移量
|
||||||
|
/// </summary>
|
||||||
|
public float OffsetY { get; set; } = 20f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 行高
|
||||||
|
/// </summary>
|
||||||
|
public float LineHeight { get; set; } = 50f;
|
||||||
|
|
||||||
|
private float _currentX;
|
||||||
|
private int _currentLine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前行的 Y 坐标
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentY => OffsetY + LineHeight * _currentLine;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重置游标到初始状态
|
||||||
|
/// </summary>
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_currentX = OffsetX;
|
||||||
|
_currentLine = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始新的一行,重置列位置到 OffsetX
|
||||||
|
/// </summary>
|
||||||
|
public void BeginRow()
|
||||||
|
{
|
||||||
|
_currentX = OffsetX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在当前行获取下一列的 Rect,X 坐标自动推进
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">列宽</param>
|
||||||
|
/// <param name="height">行高</param>
|
||||||
|
public Rect NextColumn(float width, float height)
|
||||||
|
{
|
||||||
|
var rect = new Rect(_currentX, CurrentY, width, height);
|
||||||
|
_currentX += width;
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束当前行,移动到下一行
|
||||||
|
/// </summary>
|
||||||
|
public void EndRow()
|
||||||
|
{
|
||||||
|
_currentLine++;
|
||||||
|
_currentX = OffsetX;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 换行并返回一个从 OffsetX 开始的整行 Rect
|
||||||
|
/// 适用于独占整行的标题等
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="width">宽度</param>
|
||||||
|
/// <param name="height">高度</param>
|
||||||
|
public Rect NextLine(float width, float height)
|
||||||
|
{
|
||||||
|
_currentLine++;
|
||||||
|
_currentX = OffsetX;
|
||||||
|
return new Rect(OffsetX, CurrentY, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
261
Gui/PlayerManagerGUI.cs
Normal file
261
Gui/PlayerManagerGUI.cs
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using MultiplayerBasicExample;
|
||||||
|
using Steamworks;
|
||||||
|
using UnityEngine;
|
||||||
|
using stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家管理器GUI类
|
||||||
|
/// 负责纯绘制:迭代注册的 HeaderRow / PlayerRowWidget,使用 LayoutCursor 定位
|
||||||
|
/// 不包含任何业务逻辑或硬编码按钮
|
||||||
|
/// </summary>
|
||||||
|
public static class PlayerManagerGUI
|
||||||
|
{
|
||||||
|
#region GUI状态
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示大厅信息界面
|
||||||
|
/// </summary>
|
||||||
|
public static bool ShowLobbyInfo { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 滚动位置
|
||||||
|
/// </summary>
|
||||||
|
private static Vector2 ScrollPosition;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布局游标
|
||||||
|
/// </summary>
|
||||||
|
private static readonly LayoutCursor Cursor = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region GUIStyle缓存(internal 供 DefaultWidgets 引用)
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标题样式
|
||||||
|
/// </summary>
|
||||||
|
internal static GUIStyle TitleStyle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 普通按钮样式
|
||||||
|
/// </summary>
|
||||||
|
internal static GUIStyle CommonButtonStyle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 黄色按钮样式(已记录玩家)
|
||||||
|
/// </summary>
|
||||||
|
internal static GUIStyle YellowButtonStyle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 红色按钮样式(黑名单玩家)
|
||||||
|
/// </summary>
|
||||||
|
internal static GUIStyle RedButtonStyle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绿色按钮样式(好友)
|
||||||
|
/// </summary>
|
||||||
|
internal static GUIStyle GreenButtonStyle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 样式是否已初始化
|
||||||
|
/// </summary>
|
||||||
|
private static bool StylesInitialized;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 样式初始化
|
||||||
|
|
||||||
|
private static void InitializeStyles()
|
||||||
|
{
|
||||||
|
if (StylesInitialized) return;
|
||||||
|
|
||||||
|
TitleStyle = new GUIStyle(GUI.skin.label) { fontSize = 22 };
|
||||||
|
CommonButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
||||||
|
|
||||||
|
YellowButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
||||||
|
YellowButtonStyle.normal.textColor = Color.yellow;
|
||||||
|
|
||||||
|
RedButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
||||||
|
RedButtonStyle.normal.textColor = Color.red;
|
||||||
|
|
||||||
|
GreenButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
||||||
|
GreenButtonStyle.normal.textColor = Color.green;
|
||||||
|
|
||||||
|
StylesInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 主绘制方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制大厅信息GUI
|
||||||
|
/// </summary>
|
||||||
|
public static void DrawLobbyInfoGUI()
|
||||||
|
{
|
||||||
|
InitializeStyles();
|
||||||
|
Cursor.Reset();
|
||||||
|
|
||||||
|
if (PlayerManager.MatchmakingHandler.IsInsideLobby)
|
||||||
|
{
|
||||||
|
DrawCurrentLobbyInfo();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GUI.Label(
|
||||||
|
Cursor.NextLine(500f, 40f),
|
||||||
|
"当前不在大厅中",
|
||||||
|
TitleStyle
|
||||||
|
);
|
||||||
|
DrawLobbySearchInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 大厅信息绘制
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制当前大厅信息
|
||||||
|
/// 结构:标题行 → HeaderRow 区域(纵向) → 玩家行区域
|
||||||
|
/// </summary>
|
||||||
|
private static void DrawCurrentLobbyInfo()
|
||||||
|
{
|
||||||
|
// 固定:大厅标题(独占一行)
|
||||||
|
GUI.Label(
|
||||||
|
Cursor.NextLine(2000f, 40f),
|
||||||
|
$"当前大厅: {PlayerManager.CurrentLobbyID} 房主: {PlayerManager.LobbyOwner.CurrentName}",
|
||||||
|
TitleStyle
|
||||||
|
);
|
||||||
|
|
||||||
|
// HeaderRow 区域:纵向迭代每一行,行内横向迭代 Children
|
||||||
|
foreach (var row in RegisterManager.HeaderRows)
|
||||||
|
{
|
||||||
|
if (!row.ShouldDraw()) continue;
|
||||||
|
|
||||||
|
Cursor.BeginRow();
|
||||||
|
foreach (var child in row.Children)
|
||||||
|
{
|
||||||
|
if (child.ShouldDraw())
|
||||||
|
child.Draw(Cursor.NextColumn(child.Width, 40f));
|
||||||
|
}
|
||||||
|
Cursor.EndRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 玩家行区域:每玩家一行,行内横向迭代注册的 PlayerRowWidget
|
||||||
|
for (int i = 0; i < PlayerManager.lobbyMemberList.Count; i++)
|
||||||
|
{
|
||||||
|
PlayerInfo member = PlayerManager.lobbyMemberList[i];
|
||||||
|
if (!member.SteamId.IsValid()) continue;
|
||||||
|
|
||||||
|
Cursor.BeginRow();
|
||||||
|
foreach (var widget in RegisterManager.PlayerRowWidgets)
|
||||||
|
{
|
||||||
|
if (widget.ShouldDraw(member))
|
||||||
|
widget.Draw(Cursor.NextColumn(widget.Width, 40f), member);
|
||||||
|
}
|
||||||
|
Cursor.EndRow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 大厅搜索绘制
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制大厅搜索信息(不在大厅时显示)
|
||||||
|
/// </summary>
|
||||||
|
private static void DrawLobbySearchInfo()
|
||||||
|
{
|
||||||
|
// 搜索范围按钮
|
||||||
|
string areaText = GetAreaFilterText();
|
||||||
|
if (GUI.Button(
|
||||||
|
Cursor.NextLine(400f, 40f),
|
||||||
|
$"搜索范围 {areaText}",
|
||||||
|
CommonButtonStyle
|
||||||
|
))
|
||||||
|
{
|
||||||
|
CycleLobbyDistanceFilter();
|
||||||
|
PlayerManager.RequestLobbyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新按钮(同一行)
|
||||||
|
if (GUI.Button(
|
||||||
|
new Rect(Cursor.OffsetX + 420f, Cursor.CurrentY, 100f, 40f),
|
||||||
|
"刷新",
|
||||||
|
CommonButtonStyle
|
||||||
|
))
|
||||||
|
{
|
||||||
|
PlayerManager.RequestLobbyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 搜索结果数量
|
||||||
|
GUI.Label(
|
||||||
|
Cursor.NextLine(500f, 40f),
|
||||||
|
$"搜索到大厅数量: {PlayerManager.lobbyDataList.Count}",
|
||||||
|
TitleStyle
|
||||||
|
);
|
||||||
|
|
||||||
|
// 大厅列表滚动视图
|
||||||
|
DrawLobbyListScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取区域过滤文本
|
||||||
|
/// </summary>
|
||||||
|
private static string GetAreaFilterText()
|
||||||
|
{
|
||||||
|
return PlayerManager.lobbyDistanceFilter switch
|
||||||
|
{
|
||||||
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterClose => "当前地区",
|
||||||
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault => "当前及附近地区",
|
||||||
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterFar => "半球范围内",
|
||||||
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide => "世界",
|
||||||
|
_ => "未知"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 循环切换大厅距离过滤
|
||||||
|
/// </summary>
|
||||||
|
private static void CycleLobbyDistanceFilter()
|
||||||
|
{
|
||||||
|
PlayerManager.lobbyDistanceFilter = PlayerManager.lobbyDistanceFilter switch
|
||||||
|
{
|
||||||
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide => ELobbyDistanceFilter.k_ELobbyDistanceFilterClose,
|
||||||
|
_ => PlayerManager.lobbyDistanceFilter + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制大厅列表滚动视图
|
||||||
|
/// </summary>
|
||||||
|
private static void DrawLobbyListScrollView()
|
||||||
|
{
|
||||||
|
ScrollPosition = GUI.BeginScrollView(
|
||||||
|
new Rect(Cursor.OffsetX, Cursor.CurrentY + 40f, 525f, 405f),
|
||||||
|
ScrollPosition,
|
||||||
|
new Rect(0f, 0f, 505f, PlayerManager.lobbyDataList.Count * 50f)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = 0; i < PlayerManager.lobbyDataList.Count; i++)
|
||||||
|
{
|
||||||
|
CSteamID lobbyId = PlayerManager.lobbyDataList[i];
|
||||||
|
if (GUI.Button(
|
||||||
|
new Rect(Cursor.OffsetX, Cursor.LineHeight * i, 500f, 40f),
|
||||||
|
$"{lobbyId} 人数{SteamMatchmaking.GetNumLobbyMembers(lobbyId)}",
|
||||||
|
CommonButtonStyle
|
||||||
|
))
|
||||||
|
{
|
||||||
|
PlayerManager.JoinSpecificServer(lobbyId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GUI.EndScrollView();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
101
Gui/RegisterManager.cs
Normal file
101
Gui/RegisterManager.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件注册管理器
|
||||||
|
/// 提供 Header 行和玩家行组件的注册/反注册接口
|
||||||
|
/// </summary>
|
||||||
|
public static class RegisterManager
|
||||||
|
{
|
||||||
|
#region 注册表
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 行注册表(纵向排列)
|
||||||
|
/// </summary>
|
||||||
|
public static List<IHeaderRow> HeaderRows { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家行组件注册表(横向排列)
|
||||||
|
/// </summary>
|
||||||
|
public static List<IPlayerRowWidget> PlayerRowWidgets { get; } = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 注册方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册一个 Header 行,按注册顺序在 Header 区域纵向排列
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="row">Header 行实例</param>
|
||||||
|
public static void RegisterHeaderRow(IHeaderRow row)
|
||||||
|
{
|
||||||
|
HeaderRows.Add(row);
|
||||||
|
Debug.Log($"[PlayerManager] 注册 Header 行: {row.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册一个玩家行组件,按注册顺序在每个玩家行中横向排列
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="widget">玩家行组件实例</param>
|
||||||
|
public static void RegisterPlayerRow(IPlayerRowWidget widget)
|
||||||
|
{
|
||||||
|
PlayerRowWidgets.Add(widget);
|
||||||
|
Debug.Log($"[PlayerManager] 注册玩家行组件: {widget.Name}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 反注册方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按名称移除 Header 行
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">行名称</param>
|
||||||
|
/// <returns>是否成功移除</returns>
|
||||||
|
public static bool UnregisterHeaderRow(string name)
|
||||||
|
{
|
||||||
|
int removed = HeaderRows.RemoveAll(r => r.Name == name);
|
||||||
|
if (removed > 0)
|
||||||
|
Debug.Log($"[PlayerManager] 移除 Header 行: {name}");
|
||||||
|
return removed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按名称移除玩家行组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">组件名称</param>
|
||||||
|
/// <returns>是否成功移除</returns>
|
||||||
|
public static bool UnregisterPlayerRow(string name)
|
||||||
|
{
|
||||||
|
int removed = PlayerRowWidgets.RemoveAll(w => w.Name == name);
|
||||||
|
if (removed > 0)
|
||||||
|
Debug.Log($"[PlayerManager] 移除玩家行组件: {name}");
|
||||||
|
return removed > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 清空方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空所有注册的 Header 行
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearHeaderRows()
|
||||||
|
{
|
||||||
|
HeaderRows.Clear();
|
||||||
|
Debug.Log("[PlayerManager] 已清空所有 Header 行");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空所有注册的玩家行组件
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearPlayerRowWidgets()
|
||||||
|
{
|
||||||
|
PlayerRowWidgets.Clear();
|
||||||
|
Debug.Log("[PlayerManager] 已清空所有玩家行组件");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
207
Gui/Widget.cs
Normal file
207
Gui/Widget.cs
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using stick.plugins.playermanager.Recorder;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
|
#region Header 组件接口
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单个 Header 组件接口(Label 或 Button)
|
||||||
|
/// 不直接注册到 RegisterManager,而是放在 IHeaderRow.Children 中
|
||||||
|
/// </summary>
|
||||||
|
public interface IHeaderWidget
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 组件名称,用于日志和反注册
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否应该绘制此组件
|
||||||
|
/// </summary>
|
||||||
|
bool ShouldDraw();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件宽度
|
||||||
|
/// </summary>
|
||||||
|
float Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定 Rect 内绘制组件
|
||||||
|
/// </summary>
|
||||||
|
void Draw(Rect rect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 行接口
|
||||||
|
/// 一个 HeaderRow 占 Header 区域的一整行,其 Children 在行内横向排列
|
||||||
|
/// </summary>
|
||||||
|
public interface IHeaderRow
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 行名称,用于日志和反注册
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否应该绘制此行
|
||||||
|
/// </summary>
|
||||||
|
bool ShouldDraw();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 行内的子组件列表(Label / Button),按顺序横向排列
|
||||||
|
/// </summary>
|
||||||
|
IList<IHeaderWidget> Children { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 玩家行组件接口
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家行组件接口(Label 或 Button)
|
||||||
|
/// 注册到 RegisterManager 后,在每个玩家行中横向排列
|
||||||
|
/// </summary>
|
||||||
|
public interface IPlayerRowWidget
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 组件名称,用于日志和反注册
|
||||||
|
/// </summary>
|
||||||
|
string Name { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否应该为指定玩家绘制此组件
|
||||||
|
/// </summary>
|
||||||
|
bool ShouldDraw(PlayerInfo player);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件宽度
|
||||||
|
/// </summary>
|
||||||
|
float Width { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在指定 Rect 内为指定玩家绘制组件
|
||||||
|
/// </summary>
|
||||||
|
void Draw(Rect rect, PlayerInfo player);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Header 组件具体实现
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 标签组件
|
||||||
|
/// </summary>
|
||||||
|
public class HeaderLabelWidget : IHeaderWidget
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Func<bool> Condition { get; set; }
|
||||||
|
public Func<string> Content { get; set; }
|
||||||
|
public float Width { get; set; }
|
||||||
|
public GUIStyle Style { get; set; }
|
||||||
|
|
||||||
|
public bool ShouldDraw() => Condition == null || Condition();
|
||||||
|
|
||||||
|
public void Draw(Rect rect)
|
||||||
|
{
|
||||||
|
GUI.Label(rect, Content?.Invoke() ?? string.Empty, Style ?? GUI.skin.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 按钮组件
|
||||||
|
/// </summary>
|
||||||
|
public class HeaderButtonWidget : IHeaderWidget
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Func<bool> Condition { get; set; }
|
||||||
|
public string Text { get; set; }
|
||||||
|
public Action Action { get; set; }
|
||||||
|
public float Width { get; set; }
|
||||||
|
public GUIStyle Style { get; set; }
|
||||||
|
|
||||||
|
public bool ShouldDraw() => Condition == null || Condition();
|
||||||
|
|
||||||
|
public void Draw(Rect rect)
|
||||||
|
{
|
||||||
|
if (GUI.Button(rect, Text ?? string.Empty, Style ?? GUI.skin.button))
|
||||||
|
Action?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Header 行具体实现
|
||||||
|
/// 包含一组 IHeaderWidget,在 Header 区域占一整行
|
||||||
|
/// </summary>
|
||||||
|
public class HeaderRow : IHeaderRow
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Func<bool> Condition { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 行内的子组件列表,按添加顺序横向排列
|
||||||
|
/// </summary>
|
||||||
|
public List<IHeaderWidget> Children { get; set; } = new();
|
||||||
|
|
||||||
|
IList<IHeaderWidget> IHeaderRow.Children => Children;
|
||||||
|
|
||||||
|
public bool ShouldDraw() => Condition == null || Condition();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 玩家行组件具体实现
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家行按钮组件
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerRowButtonWidget : IPlayerRowWidget
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Func<PlayerInfo, bool> Condition { get; set; }
|
||||||
|
public Func<PlayerInfo, string> GetText { get; set; }
|
||||||
|
public Action<PlayerInfo> OnClick { get; set; }
|
||||||
|
public float Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 静态样式(当 GetStyle 为 null 或返回 null 时使用)
|
||||||
|
/// </summary>
|
||||||
|
public GUIStyle Style { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 动态样式获取(可选)。优先于 Style,用于根据玩家状态切换按钮颜色
|
||||||
|
/// </summary>
|
||||||
|
public Func<PlayerInfo, GUIStyle> GetStyle { get; set; }
|
||||||
|
|
||||||
|
public bool ShouldDraw(PlayerInfo player) => Condition == null || Condition(player);
|
||||||
|
|
||||||
|
public void Draw(Rect rect, PlayerInfo player)
|
||||||
|
{
|
||||||
|
GUIStyle style = GetStyle?.Invoke(player) ?? Style ?? GUI.skin.button;
|
||||||
|
if (GUI.Button(rect, GetText?.Invoke(player) ?? string.Empty, style))
|
||||||
|
OnClick?.Invoke(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家行标签组件
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerRowLabelWidget : IPlayerRowWidget
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Func<PlayerInfo, bool> Condition { get; set; }
|
||||||
|
public Func<PlayerInfo, string> GetText { get; set; }
|
||||||
|
public float Width { get; set; }
|
||||||
|
public GUIStyle Style { get; set; }
|
||||||
|
|
||||||
|
public bool ShouldDraw(PlayerInfo player) => Condition == null || Condition(player);
|
||||||
|
|
||||||
|
public void Draw(Rect rect, PlayerInfo player)
|
||||||
|
{
|
||||||
|
GUI.Label(rect, GetText?.Invoke(player) ?? string.Empty, Style ?? GUI.skin.label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
@@ -1,470 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using MultiplayerBasicExample;
|
|
||||||
using Steamworks;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 玩家管理器GUI类
|
|
||||||
/// 负责绘制大厅信息界面
|
|
||||||
/// </summary>
|
|
||||||
public static class PlayerManagerGUI
|
|
||||||
{
|
|
||||||
#region GUI状态
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否显示大厅信息界面
|
|
||||||
/// </summary>
|
|
||||||
public static bool ShowLobbyInfo { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 滚动位置
|
|
||||||
/// </summary>
|
|
||||||
private static Vector2 ScrollPosition;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GUI布局参数
|
|
||||||
/// </summary>
|
|
||||||
private static float OffsetX;
|
|
||||||
private static float OffsetY;
|
|
||||||
private static float LineHeight;
|
|
||||||
private static float LineIndex;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region GUIStyle缓存
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标题样式
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle TitleStyle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 普通按钮样式
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle CommonButtonStyle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 黄色按钮样式(已记录玩家)
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle YellowButtonStyle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 红色按钮样式(黑名单玩家)
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle RedButtonStyle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绿色按钮样式(好友)
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle GreenButtonStyle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 样式是否已初始化
|
|
||||||
/// </summary>
|
|
||||||
private static bool StylesInitialized;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 初始化
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化布局参数
|
|
||||||
/// </summary>
|
|
||||||
private static void InitializeLayout()
|
|
||||||
{
|
|
||||||
OffsetX = 10f;
|
|
||||||
OffsetY = 20f;
|
|
||||||
LineHeight = 50f;
|
|
||||||
LineIndex = 0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化GUIStyle
|
|
||||||
/// </summary>
|
|
||||||
private static void InitializeStyles()
|
|
||||||
{
|
|
||||||
if (StylesInitialized) return;
|
|
||||||
|
|
||||||
TitleStyle = new GUIStyle(GUI.skin.label) { fontSize = 22 };
|
|
||||||
CommonButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
|
||||||
|
|
||||||
YellowButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
|
||||||
YellowButtonStyle.normal.textColor = Color.yellow;
|
|
||||||
|
|
||||||
RedButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
|
||||||
RedButtonStyle.normal.textColor = Color.red;
|
|
||||||
|
|
||||||
GreenButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = 22, richText = false };
|
|
||||||
GreenButtonStyle.normal.textColor = Color.green;
|
|
||||||
|
|
||||||
StylesInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 布局辅助方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取新行的Y坐标
|
|
||||||
/// </summary>
|
|
||||||
private static float GetNewYPosition()
|
|
||||||
{
|
|
||||||
return OffsetY + LineHeight * ++LineIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取当前行的Y坐标
|
|
||||||
/// </summary>
|
|
||||||
private static float GetCurrentYPosition()
|
|
||||||
{
|
|
||||||
return OffsetY + LineHeight * LineIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 样式获取方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据玩家状态获取按钮样式
|
|
||||||
/// </summary>
|
|
||||||
private static GUIStyle GetButtonStyle(PlayerInfo player)
|
|
||||||
{
|
|
||||||
return player.RecordStatus switch
|
|
||||||
{
|
|
||||||
PlayerRecordStatus.Blacklisted => RedButtonStyle,
|
|
||||||
PlayerRecordStatus.Recorded => YellowButtonStyle,
|
|
||||||
_ => player.IsFriend ? GreenButtonStyle : CommonButtonStyle
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取记录按钮文本
|
|
||||||
/// </summary>
|
|
||||||
private static string GetRecordButtonText(PlayerInfo player)
|
|
||||||
{
|
|
||||||
return player.RecordStatus switch
|
|
||||||
{
|
|
||||||
PlayerRecordStatus.NotRecorded => "记录",
|
|
||||||
PlayerRecordStatus.Recorded => "拉黑",
|
|
||||||
PlayerRecordStatus.Blacklisted => "取消",
|
|
||||||
_ => "未知状态"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取曾用名切换按钮文本
|
|
||||||
/// </summary>
|
|
||||||
private static string GetNameToggleButtonText(PlayerInfo player)
|
|
||||||
{
|
|
||||||
return player.DisplayNameRecorded ? "显示现用名" : "显示曾用名";
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取房间公开性文本
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static string GetRoomPublicText()
|
|
||||||
{
|
|
||||||
return PlayerManager.IsPublic ? "公开" : "好友可见";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 按钮事件处理
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理房间公开切换按钮点击
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static void OnRoomPublicToggleButtonClick()
|
|
||||||
{
|
|
||||||
PlayerManager.ToggleLobbyPublicity();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理记录按钮点击
|
|
||||||
/// </summary>
|
|
||||||
private static void OnRecordButtonClick(PlayerInfo player)
|
|
||||||
{
|
|
||||||
switch (player.RecordStatus)
|
|
||||||
{
|
|
||||||
case PlayerRecordStatus.NotRecorded:
|
|
||||||
PlayerManager.playerRecorder.RecordPlayer(player);
|
|
||||||
break;
|
|
||||||
case PlayerRecordStatus.Recorded:
|
|
||||||
PlayerManager.blackListRecorder.RecordPlayer(player);
|
|
||||||
break;
|
|
||||||
case PlayerRecordStatus.Blacklisted:
|
|
||||||
PlayerManager.playerRecorder.CancelRecordPlayer(player);
|
|
||||||
PlayerManager.blackListRecorder.CancelRecordPlayer(player);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Debug.LogError($"未知的记录状态: {player.RecordStatus}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerManager.OnLobbyMembersChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理踢人按钮点击
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static void OnKickButtonClick(PlayerInfo player)
|
|
||||||
{
|
|
||||||
PlayerManager.KickPlayer(player.SteamId, MultiplayerManager.KickResponse.DidNotRecievePackages);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理曾用名切换按钮点击
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static void OnNameToggleButtonClick(PlayerInfo player)
|
|
||||||
{
|
|
||||||
player.DisplayNameRecorded = !player.DisplayNameRecorded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理切换大厅距离过滤按钮点击
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static void OnCycleLobbyDistanceFilterClick()
|
|
||||||
{
|
|
||||||
CycleLobbyDistanceFilter();
|
|
||||||
PlayerManager.RequestLobbyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理刷新大厅列表按钮点击
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private static void OnRefreshLobbyListClick()
|
|
||||||
{
|
|
||||||
PlayerManager.RequestLobbyList();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region GUI绘制方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制当前大厅信息
|
|
||||||
/// </summary>
|
|
||||||
private static void DrawCurrentLobbyInfo()
|
|
||||||
{
|
|
||||||
InitializeStyles();
|
|
||||||
|
|
||||||
// 绘制大厅标题
|
|
||||||
GUI.Label(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 2000f, 40f),
|
|
||||||
$"当前大厅: {PlayerManager.CurrentLobbyID} 房主: {PlayerManager.LobbyOwner.CurrentName}",
|
|
||||||
TitleStyle
|
|
||||||
);
|
|
||||||
|
|
||||||
if (PlayerManager.IsHost(PlayerManager.LocalPlayer))
|
|
||||||
{
|
|
||||||
// 绘制房间公开性
|
|
||||||
GUI.Label(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 200f, 40f),
|
|
||||||
$"房间类型: {GetRoomPublicText()}",
|
|
||||||
TitleStyle
|
|
||||||
);
|
|
||||||
|
|
||||||
// 绘制房间公开切换按钮
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(OffsetX + 220f, GetCurrentYPosition(), 150f, 40f),
|
|
||||||
"切换公开性",
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnRoomPublicToggleButtonClick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制大厅成员列表
|
|
||||||
for (int i = 0; i < PlayerManager.lobbyMemberList.Count; i++)
|
|
||||||
{
|
|
||||||
PlayerInfo member = PlayerManager.lobbyMemberList[i];
|
|
||||||
if (!member.SteamId.IsValid()) continue;
|
|
||||||
|
|
||||||
DrawPlayerRow(member);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制单个玩家行
|
|
||||||
/// </summary>
|
|
||||||
private static void DrawPlayerRow(PlayerInfo player)
|
|
||||||
{
|
|
||||||
// 玩家信息按钮
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 500f, 40f),
|
|
||||||
player.ToString(),
|
|
||||||
GetButtonStyle(player)
|
|
||||||
))
|
|
||||||
{
|
|
||||||
GUIUtility.systemCopyBuffer = player.ToString();
|
|
||||||
SteamFriends.ActivateGameOverlayToUser("steamid", player.SteamId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 记录按钮
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(520f, GetCurrentYPosition(), 100f, 40f),
|
|
||||||
GetRecordButtonText(player),
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnRecordButtonClick(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 踢人按钮(非房主且非本地玩家)
|
|
||||||
if (!PlayerManager.IsHost(player) && !PlayerManager.IsLocalPlayer(player))
|
|
||||||
{
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(630f, GetCurrentYPosition(), 100f, 40f),
|
|
||||||
"Kick!",
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnKickButtonClick(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 曾用名切换按钮
|
|
||||||
if (player.HasRecordedName)
|
|
||||||
{
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(740f, GetCurrentYPosition(), 120f, 40f),
|
|
||||||
GetNameToggleButtonText(player),
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnNameToggleButtonClick(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制大厅搜索信息
|
|
||||||
/// </summary>
|
|
||||||
private static void DrawLobbySearchInfo()
|
|
||||||
{
|
|
||||||
InitializeStyles();
|
|
||||||
|
|
||||||
// 搜索范围按钮
|
|
||||||
string areaText = GetAreaFilterText();
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 400f, 40f),
|
|
||||||
$"搜索范围 {areaText}",
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnCycleLobbyDistanceFilterClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新按钮
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(OffsetX + 420f, GetCurrentYPosition(), 100f, 40f),
|
|
||||||
"刷新",
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
OnRefreshLobbyListClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜索结果数量
|
|
||||||
GUI.Label(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 500f, 40f),
|
|
||||||
$"搜索到大厅数量: {PlayerManager.lobbyDataList.Count}",
|
|
||||||
TitleStyle
|
|
||||||
);
|
|
||||||
|
|
||||||
// 大厅列表滚动视图
|
|
||||||
DrawLobbyListScrollView();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取区域过滤文本
|
|
||||||
/// </summary>
|
|
||||||
private static string GetAreaFilterText()
|
|
||||||
{
|
|
||||||
return PlayerManager.lobbyDistanceFilter switch
|
|
||||||
{
|
|
||||||
ELobbyDistanceFilter.k_ELobbyDistanceFilterClose => "当前地区",
|
|
||||||
ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault => "当前及附近地区",
|
|
||||||
ELobbyDistanceFilter.k_ELobbyDistanceFilterFar => "半球范围内",
|
|
||||||
ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide => "世界",
|
|
||||||
_ => "未知"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 循环切换大厅距离过滤
|
|
||||||
/// </summary>
|
|
||||||
private static void CycleLobbyDistanceFilter()
|
|
||||||
{
|
|
||||||
PlayerManager.lobbyDistanceFilter = PlayerManager.lobbyDistanceFilter switch
|
|
||||||
{
|
|
||||||
ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide => ELobbyDistanceFilter.k_ELobbyDistanceFilterClose,
|
|
||||||
_ => PlayerManager.lobbyDistanceFilter + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制大厅列表滚动视图
|
|
||||||
/// </summary>
|
|
||||||
private static void DrawLobbyListScrollView()
|
|
||||||
{
|
|
||||||
ScrollPosition = GUI.BeginScrollView(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 525f, 405f),
|
|
||||||
ScrollPosition,
|
|
||||||
new Rect(0f, 0f, 505f, PlayerManager.lobbyDataList.Count * 50f)
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < PlayerManager.lobbyDataList.Count; i++)
|
|
||||||
{
|
|
||||||
CSteamID lobbyId = PlayerManager.lobbyDataList[i];
|
|
||||||
if (GUI.Button(
|
|
||||||
new Rect(OffsetX, LineHeight * i, 500f, 40f),
|
|
||||||
$"{lobbyId} 人数{SteamMatchmaking.GetNumLobbyMembers(lobbyId)}",
|
|
||||||
CommonButtonStyle
|
|
||||||
))
|
|
||||||
{
|
|
||||||
PlayerManager.JoinSpecificServer(lobbyId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GUI.EndScrollView();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 主绘制方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制大厅信息GUI
|
|
||||||
/// </summary>
|
|
||||||
public static void DrawLobbyInfoGUI()
|
|
||||||
{
|
|
||||||
InitializeLayout();
|
|
||||||
InitializeStyles();
|
|
||||||
|
|
||||||
if (PlayerManager.MatchmakingHandler.IsInsideLobby)
|
|
||||||
{
|
|
||||||
DrawCurrentLobbyInfo();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GUI.Label(
|
|
||||||
new Rect(OffsetX, GetNewYPosition(), 500f, 40f),
|
|
||||||
"当前不在大厅中",
|
|
||||||
TitleStyle
|
|
||||||
);
|
|
||||||
DrawLobbySearchInfo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
108
Plugin.cs
108
Plugin.cs
@@ -7,6 +7,8 @@ using Steamworks;
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using stick.plugins.playermanager.Recorder;
|
||||||
|
using stick.plugins.playermanager.Gui;
|
||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
@@ -23,12 +25,37 @@ public class PlayerManager : BaseUnityPlugin
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否屏蔽来自其他玩家的踢出请求
|
/// 是否屏蔽来自其他玩家的踢出请求
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static ConfigEntry<bool> _blockKicks;
|
internal static ConfigEntry<bool> _blockKicks;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 大厅信息开关快捷键
|
/// 大厅信息开关快捷键
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static ConfigEntry<KeyCode> _lobbyInfoToggleKeyCode;
|
internal static ConfigEntry<KeyCode> _lobbyInfoToggleKeyCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示踢人按钮
|
||||||
|
/// </summary>
|
||||||
|
internal static ConfigEntry<bool> ShowKickButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示记录/拉黑按钮
|
||||||
|
/// </summary>
|
||||||
|
internal static ConfigEntry<bool> ShowRecordButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示曾用名切换按钮
|
||||||
|
/// </summary>
|
||||||
|
internal static ConfigEntry<bool> ShowNameToggleButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示房间类型标签
|
||||||
|
/// </summary>
|
||||||
|
internal static ConfigEntry<bool> ShowRoomPublicityLabel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示切换公开性按钮
|
||||||
|
/// </summary>
|
||||||
|
internal static ConfigEntry<bool> ShowRoomPublicityButton;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -123,6 +150,7 @@ public class PlayerManager : BaseUnityPlugin
|
|||||||
{
|
{
|
||||||
InitializeConfiguration();
|
InitializeConfiguration();
|
||||||
ApplyPatches();
|
ApplyPatches();
|
||||||
|
DefaultWidgets.RegisterAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
@@ -160,6 +188,41 @@ public class PlayerManager : BaseUnityPlugin
|
|||||||
KeyCode.F2,
|
KeyCode.F2,
|
||||||
"Lobby信息开关快捷键"
|
"Lobby信息开关快捷键"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
ShowKickButton = Config.Bind(
|
||||||
|
"GUI",
|
||||||
|
"ShowKickButton",
|
||||||
|
true,
|
||||||
|
"是否显示踢人按钮"
|
||||||
|
);
|
||||||
|
|
||||||
|
ShowRecordButton = Config.Bind(
|
||||||
|
"GUI",
|
||||||
|
"ShowRecordButton",
|
||||||
|
true,
|
||||||
|
"是否显示记录/拉黑按钮"
|
||||||
|
);
|
||||||
|
|
||||||
|
ShowNameToggleButton = Config.Bind(
|
||||||
|
"GUI",
|
||||||
|
"ShowNameToggleButton",
|
||||||
|
true,
|
||||||
|
"是否显示曾用名切换按钮"
|
||||||
|
);
|
||||||
|
|
||||||
|
ShowRoomPublicityLabel = Config.Bind(
|
||||||
|
"GUI",
|
||||||
|
"ShowRoomPublicityLabel",
|
||||||
|
true,
|
||||||
|
"是否显示房间类型标签"
|
||||||
|
);
|
||||||
|
|
||||||
|
ShowRoomPublicityButton = Config.Bind(
|
||||||
|
"GUI",
|
||||||
|
"ShowRoomPublicityButton",
|
||||||
|
true,
|
||||||
|
"是否显示切换公开性按钮"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -203,26 +266,6 @@ public class PlayerManager : BaseUnityPlugin
|
|||||||
Debug.Log("大厅信息界面已打开");
|
Debug.Log("大厅信息界面已打开");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 修改lever朝向
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="angle">朝向角度</param>
|
|
||||||
/// <returns>修改成功返回true</returns>
|
|
||||||
private static bool SetHostLeverDirection(float angle)
|
|
||||||
{
|
|
||||||
if (HostLeverHandler is not null)
|
|
||||||
{
|
|
||||||
var rig = HostLeverHandler.lever.GetComponent<Rigidbody>();
|
|
||||||
if (rig is not null)
|
|
||||||
{
|
|
||||||
rig.MoveRotation(Quaternion.Euler(0, angle, 0));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 公共方法
|
#region 公共方法
|
||||||
@@ -254,24 +297,9 @@ public class PlayerManager : BaseUnityPlugin
|
|||||||
{
|
{
|
||||||
if (MultiplayerManager.IsServer)
|
if (MultiplayerManager.IsServer)
|
||||||
{
|
{
|
||||||
if (IsPublic)
|
var targetType = IsPublic ? ELobbyType.k_ELobbyTypePublic : ELobbyType.k_ELobbyTypeFriendsOnly;
|
||||||
{
|
MatchmakingHandler.SetNewLobbyType(targetType);
|
||||||
MatchmakingHandler.SetNewLobbyType(ELobbyType.k_ELobbyTypeFriendsOnly);
|
RawChangeLobbyTypeMI.Invoke(MatchmakingHandler, [targetType]);
|
||||||
if (!SetHostLeverDirection(30f))
|
|
||||||
{
|
|
||||||
// 如果lever不存在则直接调用原始方法
|
|
||||||
RawChangeLobbyTypeMI.Invoke(MatchmakingHandler, [ELobbyType.k_ELobbyTypeFriendsOnly]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MatchmakingHandler.SetNewLobbyType(ELobbyType.k_ELobbyTypePublic);
|
|
||||||
if (!SetHostLeverDirection(-30f))
|
|
||||||
{
|
|
||||||
// 如果lever不存在则直接调用原始方法
|
|
||||||
RawChangeLobbyTypeMI.Invoke(MatchmakingHandler, [ELobbyType.k_ELobbyTypePublic]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 黑名单记录器,用于记录需要屏蔽的玩家
|
/// 黑名单记录器,用于记录需要屏蔽的玩家
|
||||||
@@ -3,7 +3,7 @@ using System.Text.RegularExpressions;
|
|||||||
using Steamworks;
|
using Steamworks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 玩家信息类
|
/// 玩家信息类
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 玩家记录器,用于记录普通玩家信息
|
/// 玩家记录器,用于记录普通玩家信息
|
||||||
@@ -4,7 +4,7 @@ using MultiplayerBasicExample;
|
|||||||
using Steamworks;
|
using Steamworks;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager.Recorder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 记录器基类
|
/// 记录器基类
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<TargetFramework>net35</TargetFramework>
|
<TargetFramework>net35</TargetFramework>
|
||||||
<AssemblyName>stick.plugins.playermanager</AssemblyName>
|
<AssemblyName>stick.plugins.playermanager</AssemblyName>
|
||||||
<Product>PlayerManager</Product>
|
<Product>PlayerManager</Product>
|
||||||
<Version>3.1.1</Version>
|
<Version>4.0.0</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<RestoreAdditionalProjectSources>
|
<RestoreAdditionalProjectSources>
|
||||||
|
|||||||
Reference in New Issue
Block a user