Compare commits
13 Commits
413fc0b930
...
v3.1.1-rc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
652693883b | ||
|
|
8261d243e6 | ||
|
|
4de0778dc8 | ||
|
|
eb27008e4d | ||
|
|
0b3a12f64d | ||
|
|
f44815c600 | ||
|
|
ca1f7c0637 | ||
|
|
be50401453 | ||
|
|
c6574c53c8 | ||
|
|
9a0b40d3ee | ||
|
|
530036cc30 | ||
|
|
3a2062d808 | ||
|
|
45884570e6 |
46
.gitea/workflows/dotnet.yml
Normal file
46
.gitea/workflows/dotnet.yml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
name: Publish Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*' # 匹配 v 开头的标签,如 v1.0.0
|
||||||
|
# 或者使用 '*' 匹配所有标签
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: http://MonBianG.lan:3000/actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup .NET
|
||||||
|
uses: http://MonBianG.lan:3000/actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: 8.0.x
|
||||||
|
|
||||||
|
- name: Restore dependencies
|
||||||
|
run: dotnet restore
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: dotnet build --no-restore --configuration Release
|
||||||
|
|
||||||
|
# 使用 Gitea Release Action 创建 Release
|
||||||
|
- name: Create Release
|
||||||
|
uses: http://MonBianG.lan:3000/actions/gitea-release-action@main
|
||||||
|
with:
|
||||||
|
# 使用推送的标签名作为版本号
|
||||||
|
tag: ${{ github.ref_name }}
|
||||||
|
# 自动从标签名生成 Release 标题
|
||||||
|
name: Release ${{ github.ref_name }}
|
||||||
|
# 使用提交信息或自定义发布说明
|
||||||
|
body: |
|
||||||
|
## Changes in ${{ github.ref_name }}
|
||||||
|
|
||||||
|
Auto-generated release for tag ${{ github.ref_name }}.
|
||||||
|
# 上传构建产物
|
||||||
|
files: |
|
||||||
|
./bin/Release/net35/stick.plugins.*.dll
|
||||||
|
# 是否为预发布版本(如果是 alpha/beta/rc 标签)
|
||||||
|
prerelease: ${{ contains(github.ref_name, 'alpha') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'rc') }}
|
||||||
|
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
public class BlackListRecorder : RecoderBase
|
/// <summary>
|
||||||
|
/// 黑名单记录器,用于记录需要屏蔽的玩家
|
||||||
|
/// </summary>
|
||||||
|
public class BlackListRecorder : RecorderBase
|
||||||
{
|
{
|
||||||
protected override string FileName => "BlackList.txt";
|
protected override string FileName => "BlackList.txt";
|
||||||
protected override PlayerRecordStatus RecordStatus => PlayerRecordStatus.Blacklisted;
|
protected override PlayerRecordStatus RecordStatus => PlayerRecordStatus.Blacklisted;
|
||||||
|
|||||||
186
PlayerInfo.cs
186
PlayerInfo.cs
@@ -5,84 +5,134 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家信息类
|
||||||
|
/// 封装玩家的Steam ID、名称和记录状态
|
||||||
|
/// </summary>
|
||||||
public class PlayerInfo : IEquatable<PlayerInfo>
|
public class PlayerInfo : IEquatable<PlayerInfo>
|
||||||
{
|
{
|
||||||
// 即时获取的玩家名字,只读
|
#region 属性
|
||||||
public string PlayerName
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家的Steam ID
|
||||||
|
/// </summary>
|
||||||
|
public CSteamID SteamId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录的玩家名字(通常是曾用名)
|
||||||
|
/// </summary>
|
||||||
|
public string RecordedName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否显示记录的名称
|
||||||
|
/// </summary>
|
||||||
|
public bool DisplayNameRecorded { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否有记录的名称(与当前名称不同)
|
||||||
|
/// </summary>
|
||||||
|
public bool HasRecordedName => !string.IsNullOrEmpty(RecordedName) && RecordedName != CurrentName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为好友
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFriend => SteamFriends.HasFriend(SteamId, EFriendFlags.k_EFriendFlagImmediate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录状态
|
||||||
|
/// </summary>
|
||||||
|
public PlayerRecordStatus RecordStatus { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前玩家名称(实时获取)
|
||||||
|
/// </summary>
|
||||||
|
public string CurrentName
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (SteamManager.Initialized)
|
if (SteamManager.Initialized)
|
||||||
{
|
{
|
||||||
return SteamFriends.GetFriendPersonaName(steamId);
|
return SteamFriends.GetFriendPersonaName(SteamId);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return playerNameRecorded ?? "";
|
|
||||||
}
|
}
|
||||||
|
return RecordedName ?? string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 玩家名字,一般来说是曾用名
|
|
||||||
public string playerNameRecorded;
|
#endregion
|
||||||
public bool displayNameRecorded = false;
|
|
||||||
public bool HasRecordedName => !string.IsNullOrEmpty(playerNameRecorded) && playerNameRecorded != PlayerName;
|
#region 构造函数
|
||||||
public CSteamID steamId;
|
|
||||||
public bool IsFriend => SteamFriends.HasFriend(steamId, EFriendFlags.k_EFriendFlagImmediate);
|
/// <summary>
|
||||||
public PlayerRecordStatus RecordStatus = PlayerRecordStatus.NotRecorded;
|
/// 使用Steam ID和名称构造
|
||||||
|
/// </summary>
|
||||||
public PlayerInfo(CSteamID steamId, string playerName)
|
public PlayerInfo(CSteamID steamId, string playerName)
|
||||||
{
|
{
|
||||||
this.steamId = steamId;
|
SteamId = steamId;
|
||||||
this.playerNameRecorded = playerName;
|
RecordedName = playerName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用Steam ID构造,自动获取名称
|
||||||
|
/// </summary>
|
||||||
public PlayerInfo(CSteamID steamId)
|
public PlayerInfo(CSteamID steamId)
|
||||||
{
|
{
|
||||||
this.steamId = steamId;
|
SteamId = steamId;
|
||||||
playerNameRecorded = SteamFriends.GetFriendPersonaName(steamId);
|
RecordedName = SteamFriends.GetFriendPersonaName(steamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 使用字符串构造(格式: "SteamID 玩家名称")
|
||||||
|
/// </summary>
|
||||||
public PlayerInfo(string playerInfo)
|
public PlayerInfo(string playerInfo)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(playerInfo))
|
if (string.IsNullOrEmpty(playerInfo))
|
||||||
{
|
{
|
||||||
steamId = CSteamID.Nil;
|
SteamId = CSteamID.Nil;
|
||||||
playerNameRecorded = string.Empty;
|
RecordedName = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parse(playerInfo, out ulong steamIdValue, out string playerNameValue);
|
Parse(playerInfo, out ulong steamIdValue, out string playerNameValue);
|
||||||
steamId = new CSteamID(steamIdValue);
|
SteamId = new CSteamID(steamIdValue);
|
||||||
playerNameRecorded = playerNameValue;
|
RecordedName = playerNameValue;
|
||||||
|
|
||||||
// 会因为初始化过早而导致插件初始化失败
|
|
||||||
// playerName = SteamFriends.GetFriendPersonaName(steamId);
|
|
||||||
}
|
}
|
||||||
public static void Parse(string input, out ulong steamID, out string playerName)
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 解析方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 解析玩家信息字符串
|
||||||
|
/// </summary>
|
||||||
|
public static void Parse(string input, out ulong steamId, out string playerName)
|
||||||
{
|
{
|
||||||
// 查找第一个空格作为分隔符
|
|
||||||
int spaceIndex = input.IndexOf(' ');
|
int spaceIndex = input.IndexOf(' ');
|
||||||
|
|
||||||
if (spaceIndex == -1)
|
if (spaceIndex == -1)
|
||||||
{
|
{
|
||||||
// 没有空格的情况:整个字符串作为SteamID
|
steamId = ulong.Parse(input);
|
||||||
steamID = ulong.Parse(input);
|
|
||||||
playerName = string.Empty;
|
playerName = string.Empty;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分割SteamID和玩家名字
|
|
||||||
string steamIdPart = input.Substring(0, spaceIndex);
|
string steamIdPart = input.Substring(0, spaceIndex);
|
||||||
string namePart = input.Substring(spaceIndex + 1);
|
string namePart = input.Substring(spaceIndex + 1);
|
||||||
|
|
||||||
steamID = ulong.Parse(steamIdPart);
|
steamId = ulong.Parse(steamIdPart);
|
||||||
playerName = namePart; // 保留名字原始格式(含标签和空格)
|
playerName = namePart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryParse(string input, out ulong steamID, out string playerName)
|
/// <summary>
|
||||||
|
/// 尝试解析玩家信息字符串
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryParse(string input, out ulong steamId, out string playerName)
|
||||||
{
|
{
|
||||||
steamID = 0;
|
steamId = 0;
|
||||||
playerName = string.Empty;
|
playerName = string.Empty;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Parse(input, out steamID, out playerName);
|
Parse(input, out steamId, out playerName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
@@ -90,48 +140,82 @@ public class PlayerInfo : IEquatable<PlayerInfo>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void
|
|
||||||
MergeWith(PlayerInfo other)
|
#endregion
|
||||||
|
|
||||||
|
#region 合并方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并另一个玩家信息
|
||||||
|
/// </summary>
|
||||||
|
public void MergeWith(PlayerInfo other)
|
||||||
{
|
{
|
||||||
if (other is null) return;
|
if (other is null) return;
|
||||||
|
|
||||||
if (steamId != other.steamId)
|
if (SteamId != other.SteamId)
|
||||||
{
|
{
|
||||||
Debug.LogWarning($"Attempting to merge PlayerInfo with different steam IDs: {steamId} and {other.steamId}");
|
Debug.LogWarning($"尝试合并不同Steam ID的玩家信息: {SteamId} 和 {other.SteamId}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(other.playerNameRecorded))
|
if (!string.IsNullOrEmpty(other.RecordedName))
|
||||||
{
|
{
|
||||||
playerNameRecorded = other.playerNameRecorded;
|
RecordedName = other.RecordedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (other.RecordStatus > RecordStatus)
|
if (other.RecordStatus > RecordStatus)
|
||||||
{
|
{
|
||||||
RecordStatus = other.RecordStatus;
|
RecordStatus = other.RecordStatus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 生成记录进文件用的字符串
|
#endregion
|
||||||
|
|
||||||
|
#region 字符串转换
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转换为文件存储格式
|
||||||
|
/// </summary>
|
||||||
public string ToInfoString()
|
public string ToInfoString()
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(playerNameRecorded))
|
if (!string.IsNullOrEmpty(RecordedName))
|
||||||
{
|
{
|
||||||
return $"{steamId} {playerNameRecorded}";
|
return $"{SteamId} {RecordedName}";
|
||||||
}
|
}
|
||||||
return $"{steamId} {PlayerName}";
|
return $"{SteamId} {CurrentName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 转换为显示字符串
|
||||||
|
/// </summary>
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
if (displayNameRecorded && !string.IsNullOrEmpty(playerNameRecorded))
|
if (DisplayNameRecorded && !string.IsNullOrEmpty(RecordedName))
|
||||||
{
|
{
|
||||||
return $"{steamId} {playerNameRecorded}";
|
return $"{SteamId} {RecordedName}";
|
||||||
}
|
}
|
||||||
return $"{steamId} {PlayerName}";
|
return $"{SteamId} {CurrentName}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(PlayerInfo toCompare)
|
#endregion
|
||||||
=> toCompare is not null && steamId == toCompare.steamId;
|
|
||||||
|
#region 相等性比较
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断是否与另一个玩家信息相等
|
||||||
|
/// </summary>
|
||||||
|
public bool Equals(PlayerInfo other)
|
||||||
|
{
|
||||||
|
return other is not null && SteamId == other.SteamId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取哈希码
|
||||||
|
/// </summary>
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
=> steamId.GetHashCode();
|
{
|
||||||
|
return SteamId.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -5,248 +5,466 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
static public class PlayerManagerGUI
|
/// <summary>
|
||||||
|
/// 玩家管理器GUI类
|
||||||
|
/// 负责绘制大厅信息界面
|
||||||
|
/// </summary>
|
||||||
|
public static class PlayerManagerGUI
|
||||||
{
|
{
|
||||||
static public bool showLobbyInfo = false;
|
#region GUI状态
|
||||||
static Vector2 scrollPosition = Vector2.zero;
|
|
||||||
static float offsetX;
|
/// <summary>
|
||||||
static float offsetY;
|
/// 是否显示大厅信息界面
|
||||||
static float lineHeight;
|
/// </summary>
|
||||||
static float lineIndex;
|
public static bool ShowLobbyInfo { get; set; }
|
||||||
static GUIStyle TitleGuiStyle => new(GUI.skin.label) { fontSize = 22, };
|
|
||||||
static GUIStyle CommonButtonGuiStyle => new(GUI.skin.button) { fontSize = 22, richText = false };
|
/// <summary>
|
||||||
static GUIStyle YellowButtonGuiStyle => new(GUI.skin.button) { fontSize = 22, normal = { textColor = Color.yellow, }, richText = false };
|
/// 滚动位置
|
||||||
static GUIStyle RedButtonGuiStyle => new(GUI.skin.button) { fontSize = 22, normal = { textColor = Color.red, }, richText = false };
|
/// </summary>
|
||||||
static GUIStyle GreenButtonGuiStyle => new(GUI.skin.button) { fontSize = 22, normal = { textColor = Color.green, }, richText = false };
|
private static Vector2 ScrollPosition;
|
||||||
static void InitOffset()
|
|
||||||
|
/// <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;
|
OffsetX = 10f;
|
||||||
offsetY = 20f;
|
OffsetY = 20f;
|
||||||
lineHeight = 50f;
|
LineHeight = 50f;
|
||||||
lineIndex = 0f;
|
LineIndex = 0f;
|
||||||
}
|
}
|
||||||
static float GetNewYPos()
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化GUIStyle
|
||||||
|
/// </summary>
|
||||||
|
private static void InitializeStyles()
|
||||||
{
|
{
|
||||||
return offsetY + lineHeight * ++lineIndex;
|
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;
|
||||||
}
|
}
|
||||||
static float GetCurYPos()
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 布局辅助方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取新行的Y坐标
|
||||||
|
/// </summary>
|
||||||
|
private static float GetNewYPosition()
|
||||||
{
|
{
|
||||||
return offsetY + lineHeight * lineIndex;
|
return OffsetY + LineHeight * ++LineIndex;
|
||||||
}
|
}
|
||||||
static GUIStyle GetButtonStyle(PlayerInfo playerInfo)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前行的Y坐标
|
||||||
|
/// </summary>
|
||||||
|
private static float GetCurrentYPosition()
|
||||||
{
|
{
|
||||||
if (playerInfo.RecordStatus == PlayerRecordStatus.Blacklisted)
|
return OffsetY + LineHeight * LineIndex;
|
||||||
{
|
|
||||||
return RedButtonGuiStyle;
|
|
||||||
}
|
|
||||||
else if (playerInfo.RecordStatus == PlayerRecordStatus.Recorded)
|
|
||||||
{
|
|
||||||
return YellowButtonGuiStyle;
|
|
||||||
}
|
|
||||||
else if (playerInfo.IsFriend)
|
|
||||||
{
|
|
||||||
return GreenButtonGuiStyle;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return CommonButtonGuiStyle;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
static string GetRecordedNameButtonText(PlayerInfo playerInfo)
|
|
||||||
=> playerInfo.displayNameRecorded switch
|
#endregion
|
||||||
|
|
||||||
|
#region 样式获取方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据玩家状态获取按钮样式
|
||||||
|
/// </summary>
|
||||||
|
private static GUIStyle GetButtonStyle(PlayerInfo player)
|
||||||
|
{
|
||||||
|
return player.RecordStatus switch
|
||||||
{
|
{
|
||||||
true => "显示现用名",
|
PlayerRecordStatus.Blacklisted => RedButtonStyle,
|
||||||
false => "显示曾用名",
|
PlayerRecordStatus.Recorded => YellowButtonStyle,
|
||||||
|
_ => player.IsFriend ? GreenButtonStyle : CommonButtonStyle
|
||||||
};
|
};
|
||||||
static string GetRecordButtonText(PlayerInfo playerInfo)
|
}
|
||||||
=> playerInfo.RecordStatus switch
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取记录按钮文本
|
||||||
|
/// </summary>
|
||||||
|
private static string GetRecordButtonText(PlayerInfo player)
|
||||||
|
{
|
||||||
|
return player.RecordStatus switch
|
||||||
{
|
{
|
||||||
PlayerRecordStatus.NotRecorded => "记录",
|
PlayerRecordStatus.NotRecorded => "记录",
|
||||||
PlayerRecordStatus.Recorded => "拉黑",
|
PlayerRecordStatus.Recorded => "拉黑",
|
||||||
PlayerRecordStatus.Blacklisted => "取消",
|
PlayerRecordStatus.Blacklisted => "取消",
|
||||||
_ => "未知状态",
|
_ => "未知状态"
|
||||||
};
|
};
|
||||||
static void OnRecordButtonClick(PlayerInfo playerInfo)
|
}
|
||||||
{
|
|
||||||
|
|
||||||
//Debug.Log($"RecordStatus {playerInfo.RecordStatus}");
|
/// <summary>
|
||||||
switch (playerInfo.RecordStatus)
|
/// 获取曾用名切换按钮文本
|
||||||
|
/// </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:
|
case PlayerRecordStatus.NotRecorded:
|
||||||
PlayerManager.playerRecoder.RecordPlayer(playerInfo);
|
PlayerManager.playerRecorder.RecordPlayer(player);
|
||||||
break;
|
break;
|
||||||
case PlayerRecordStatus.Recorded:
|
case PlayerRecordStatus.Recorded:
|
||||||
PlayerManager.blackListRecorder.RecordPlayer(playerInfo);
|
PlayerManager.blackListRecorder.RecordPlayer(player);
|
||||||
break;
|
break;
|
||||||
case PlayerRecordStatus.Blacklisted:
|
case PlayerRecordStatus.Blacklisted:
|
||||||
PlayerManager.playerRecoder.CancelRecordPlayer(playerInfo);
|
PlayerManager.playerRecorder.CancelRecordPlayer(player);
|
||||||
PlayerManager.blackListRecorder.CancelRecordPlayer(playerInfo);
|
PlayerManager.blackListRecorder.CancelRecordPlayer(player);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Debug.LogError($"未知的记录状态: {playerInfo.RecordStatus}");
|
Debug.LogError($"未知的记录状态: {player.RecordStatus}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Debug.Log($"RecordStatus {playerInfo.RecordStatus}");
|
|
||||||
PlayerManager.OnLobbyMembersChanged();
|
PlayerManager.OnLobbyMembersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理踢人按钮点击
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
static void DrawCurrentLobbyInfo()
|
private static void OnKickButtonClick(PlayerInfo player)
|
||||||
{
|
{
|
||||||
// 当前Lobby信息
|
PlayerManager.KickPlayer(player.SteamId, MultiplayerManager.KickResponse.DidNotRecievePackages);
|
||||||
GUI.Label
|
}
|
||||||
(
|
|
||||||
new Rect(offsetX, GetNewYPos(), 2000f, 40f),
|
/// <summary>
|
||||||
$"当前Lobby:{PlayerManager.CurrentLobbyID} 房主:{PlayerManager.LobbyOwner.PlayerName}",
|
/// 处理曾用名切换按钮点击
|
||||||
TitleGuiStyle
|
/// </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++)
|
for (int i = 0; i < PlayerManager.lobbyMemberList.Count; i++)
|
||||||
{
|
{
|
||||||
PlayerInfo lobbyMember = PlayerManager.lobbyMemberList[i];
|
PlayerInfo member = PlayerManager.lobbyMemberList[i];
|
||||||
if (lobbyMember.steamId.IsValid())
|
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 (GUI.Button
|
}
|
||||||
(
|
}
|
||||||
new Rect(offsetX, GetNewYPos(), 500f, 40f),
|
|
||||||
lobbyMember.ToString(),
|
// 曾用名切换按钮
|
||||||
GetButtonStyle(lobbyMember)
|
if (player.HasRecordedName)
|
||||||
)
|
{
|
||||||
)
|
if (GUI.Button(
|
||||||
{
|
new Rect(740f, GetCurrentYPosition(), 120f, 40f),
|
||||||
GUIUtility.systemCopyBuffer = lobbyMember.ToString();
|
GetNameToggleButtonText(player),
|
||||||
SteamFriends.ActivateGameOverlayToUser("steamid", lobbyMember.steamId);
|
CommonButtonStyle
|
||||||
}
|
))
|
||||||
// 记录按钮
|
{
|
||||||
if (GUI.Button
|
OnNameToggleButtonClick(player);
|
||||||
(
|
|
||||||
new Rect(520f, GetCurYPos(), 100f, 40f),
|
|
||||||
GetRecordButtonText(lobbyMember),
|
|
||||||
CommonButtonGuiStyle
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
OnRecordButtonClick(lobbyMember);
|
|
||||||
}
|
|
||||||
// 踢人按钮
|
|
||||||
if (!PlayerManager.IsHost(lobbyMember) && !PlayerManager.IsLocalPlayer(lobbyMember))
|
|
||||||
{
|
|
||||||
if (GUI.Button
|
|
||||||
(
|
|
||||||
new Rect(630f, GetCurYPos(), 100f, 40f),
|
|
||||||
"Kick!",
|
|
||||||
CommonButtonGuiStyle
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
PlayerManager.KickPlayer(lobbyMember.steamId, MultiplayerManager.KickResponse.DidNotRecievePackages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 曾用名按钮
|
|
||||||
if (lobbyMember.HasRecordedName)
|
|
||||||
{
|
|
||||||
if (GUI.Button
|
|
||||||
(
|
|
||||||
new Rect(740f, GetCurYPos(), 120f, 40f),
|
|
||||||
GetRecordedNameButtonText(lobbyMember),
|
|
||||||
CommonButtonGuiStyle
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
lobbyMember.displayNameRecorded = !lobbyMember.displayNameRecorded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static void DrawLobbySearchInfo()
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制大厅搜索信息
|
||||||
|
/// </summary>
|
||||||
|
private static void DrawLobbySearchInfo()
|
||||||
{
|
{
|
||||||
string areaStr = "";
|
InitializeStyles();
|
||||||
switch (PlayerManager.elobbyDistanceFilter)
|
|
||||||
|
// 搜索范围按钮
|
||||||
|
string areaText = GetAreaFilterText();
|
||||||
|
if (GUI.Button(
|
||||||
|
new Rect(OffsetX, GetNewYPosition(), 400f, 40f),
|
||||||
|
$"搜索范围 {areaText}",
|
||||||
|
CommonButtonStyle
|
||||||
|
))
|
||||||
{
|
{
|
||||||
case ELobbyDistanceFilter.k_ELobbyDistanceFilterClose:
|
OnCycleLobbyDistanceFilterClick();
|
||||||
areaStr = "当前地区";
|
|
||||||
break;
|
|
||||||
case ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault:
|
|
||||||
areaStr = "当前及附近地区";
|
|
||||||
break;
|
|
||||||
case ELobbyDistanceFilter.k_ELobbyDistanceFilterFar:
|
|
||||||
areaStr = "半球范围内";
|
|
||||||
break;
|
|
||||||
case ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide:
|
|
||||||
areaStr = "世界";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (GUI.Button
|
|
||||||
(
|
// 刷新按钮
|
||||||
new Rect(offsetX, GetNewYPos(), 400f, 40f),
|
if (GUI.Button(
|
||||||
$"搜索范围 {areaStr}",
|
new Rect(OffsetX + 420f, GetCurrentYPosition(), 100f, 40f),
|
||||||
CommonButtonGuiStyle
|
"刷新",
|
||||||
)
|
CommonButtonStyle
|
||||||
)
|
))
|
||||||
{
|
{
|
||||||
if (PlayerManager.elobbyDistanceFilter == ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide)
|
OnRefreshLobbyListClick();
|
||||||
{
|
|
||||||
PlayerManager.elobbyDistanceFilter = ELobbyDistanceFilter.k_ELobbyDistanceFilterClose;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PlayerManager.elobbyDistanceFilter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (GUI.Button
|
|
||||||
(
|
|
||||||
new Rect(offsetX + 420f, GetCurYPos(), 100f, 40f),
|
|
||||||
"刷新",
|
|
||||||
CommonButtonGuiStyle
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
PlayerManager.RequestLobbyList();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 搜索结果数量
|
||||||
GUI.Label(
|
GUI.Label(
|
||||||
new Rect(offsetX, GetNewYPos(), 500f, 40f),
|
new Rect(OffsetX, GetNewYPosition(), 500f, 40f),
|
||||||
$"搜索到Lobby数量:{PlayerManager.lobbyDataList.Count}",
|
$"搜索到大厅数量: {PlayerManager.lobbyDataList.Count}",
|
||||||
TitleGuiStyle
|
TitleStyle
|
||||||
);
|
);
|
||||||
scrollPosition = GUI.BeginScrollView(
|
|
||||||
new Rect(offsetX, GetNewYPos(), 525f, 405f),
|
// 大厅列表滚动视图
|
||||||
scrollPosition,
|
DrawLobbyListScrollView();
|
||||||
new Rect(0f, 0f, 505f, PlayerManager.lobbyDataList.Count * 50)
|
}
|
||||||
);
|
|
||||||
for (int j = 0; j < PlayerManager.lobbyDataList.Count; j++)
|
/// <summary>
|
||||||
|
/// 获取区域过滤文本
|
||||||
|
/// </summary>
|
||||||
|
private static string GetAreaFilterText()
|
||||||
|
{
|
||||||
|
return PlayerManager.lobbyDistanceFilter switch
|
||||||
{
|
{
|
||||||
CSteamID lobbyData = PlayerManager.lobbyDataList[j];
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterClose => "当前地区",
|
||||||
if (GUI.Button
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault => "当前及附近地区",
|
||||||
(
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterFar => "半球范围内",
|
||||||
new Rect(offsetX, lineHeight * j, 500f, 40f),
|
ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide => "世界",
|
||||||
$"{lobbyData} 人数{SteamMatchmaking.GetNumLobbyMembers(lobbyData)}",
|
_ => "未知"
|
||||||
CommonButtonGuiStyle
|
};
|
||||||
)
|
}
|
||||||
)
|
|
||||||
|
/// <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(lobbyData);
|
PlayerManager.JoinSpecificServer(lobbyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GUI.EndScrollView();
|
GUI.EndScrollView();
|
||||||
}
|
}
|
||||||
static public void DrawLobbyInfoGUI()
|
|
||||||
{
|
|
||||||
InitOffset();
|
|
||||||
|
|
||||||
if (PlayerManager.IMatchmakingHandler.IsInsideLobby)
|
#endregion
|
||||||
|
|
||||||
|
#region 主绘制方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制大厅信息GUI
|
||||||
|
/// </summary>
|
||||||
|
public static void DrawLobbyInfoGUI()
|
||||||
|
{
|
||||||
|
InitializeLayout();
|
||||||
|
InitializeStyles();
|
||||||
|
|
||||||
|
if (PlayerManager.MatchmakingHandler.IsInsideLobby)
|
||||||
{
|
{
|
||||||
DrawCurrentLobbyInfo();
|
DrawCurrentLobbyInfo();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
GUI.Label(
|
GUI.Label(
|
||||||
new Rect(offsetX, GetNewYPos(), 500f, 40f),
|
new Rect(OffsetX, GetNewYPosition(), 500f, 40f),
|
||||||
"当前不在Lobby中",
|
"当前不在大厅中",
|
||||||
TitleGuiStyle
|
TitleStyle
|
||||||
);
|
);
|
||||||
|
DrawLobbySearchInfo();
|
||||||
}
|
}
|
||||||
DrawLobbySearchInfo();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
public class PlayerRecoder : RecoderBase
|
/// <summary>
|
||||||
|
/// 玩家记录器,用于记录普通玩家信息
|
||||||
|
/// </summary>
|
||||||
|
public class PlayerRecorder : RecorderBase
|
||||||
{
|
{
|
||||||
protected override string FileName => "CustomPlayerRecord.txt";
|
protected override string FileName => "CustomPlayerRecord.txt";
|
||||||
protected override PlayerRecordStatus RecordStatus => PlayerRecordStatus.Recorded;
|
protected override PlayerRecordStatus RecordStatus => PlayerRecordStatus.Recorded;
|
||||||
|
|||||||
368
Plugin.cs
368
Plugin.cs
@@ -6,124 +6,366 @@ using UnityEngine;
|
|||||||
using Steamworks;
|
using Steamworks;
|
||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家管理器插件主类
|
||||||
|
/// 提供大厅信息显示、玩家记录、黑名单管理等功能
|
||||||
|
/// </summary>
|
||||||
[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
|
[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
|
||||||
[BepInProcess("StickFight.exe")]
|
[BepInProcess("StickFight.exe")]
|
||||||
public class PlayerManager : BaseUnityPlugin
|
public class PlayerManager : BaseUnityPlugin
|
||||||
{
|
{
|
||||||
static ConfigEntry<KeyCode> lobbyInfoToggleKeyCode;
|
#region 配置项
|
||||||
static ConfigEntry<bool> blockKicks;
|
|
||||||
static CallResult<LobbyMatchList_t> onLobbyMatchListCallResult = CallResult<LobbyMatchList_t>.Create(null);
|
/// <summary>
|
||||||
public static List<CSteamID> lobbyDataList = [];
|
/// 是否屏蔽来自其他玩家的踢出请求
|
||||||
public static ELobbyDistanceFilter elobbyDistanceFilter = ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault;
|
/// </summary>
|
||||||
public static PlayerRecoder playerRecoder = new();
|
private static ConfigEntry<bool> _blockKicks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大厅信息开关快捷键
|
||||||
|
/// </summary>
|
||||||
|
private static ConfigEntry<KeyCode> _lobbyInfoToggleKeyCode;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Steam回调
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大厅列表查询回调
|
||||||
|
/// </summary>
|
||||||
|
private static readonly CallResult<LobbyMatchList_t> LobbyMatchListCallResult = CallResult<LobbyMatchList_t>.Create(null);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 公共属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家记录器实例
|
||||||
|
/// </summary>
|
||||||
|
public static PlayerRecorder playerRecorder = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 黑名单记录器实例
|
||||||
|
/// </summary>
|
||||||
public static BlackListRecorder blackListRecorder = new();
|
public static BlackListRecorder blackListRecorder = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前大厅成员列表
|
||||||
|
/// </summary>
|
||||||
public static List<PlayerInfo> lobbyMemberList = [];
|
public static List<PlayerInfo> lobbyMemberList = [];
|
||||||
|
|
||||||
public static MatchmakingHandler IMatchmakingHandler => MatchmakingHandler.Instance;
|
/// <summary>
|
||||||
public static CSteamID CurrentLobbyID => IMatchmakingHandler.CurrentLobby;
|
/// 搜索到的大厅列表
|
||||||
public static PlayerInfo LobbyOwner => new(SteamMatchmaking.GetLobbyOwner(CurrentLobbyID));// PlayerManager.cs 新增代码
|
/// </summary>
|
||||||
public static PlayerInfo LocalPlayer => new(SteamUser.GetSteamID());
|
public static List<CSteamID> lobbyDataList = [];
|
||||||
public static object lobbyMemberLock = new(); // 线程同步锁
|
|
||||||
|
|
||||||
void Awake()
|
/// <summary>
|
||||||
|
/// 大厅距离过滤设置
|
||||||
|
/// </summary>
|
||||||
|
public static ELobbyDistanceFilter lobbyDistanceFilter = ELobbyDistanceFilter.k_ELobbyDistanceFilterDefault;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 线程同步锁,保护大厅成员列表操作
|
||||||
|
/// </summary>
|
||||||
|
private static readonly object LobbyMemberLock = new();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 快捷访问属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大厅管理器实例
|
||||||
|
/// </summary>
|
||||||
|
public static MatchmakingHandler MatchmakingHandler => MatchmakingHandler.Instance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 原始的设置lobby类型方法
|
||||||
|
/// </summary>
|
||||||
|
public static MethodInfo RawChangeLobbyTypeMI => AccessTools.Method(
|
||||||
|
typeof(MatchmakingHandler),
|
||||||
|
"ChangeLobbyType"
|
||||||
|
);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// leverhandler实例
|
||||||
|
/// </summary>
|
||||||
|
public static HostLeverHandler HostLeverHandler => UnityEngine.Object.FindObjectOfType<HostLeverHandler>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 房间公开性
|
||||||
|
/// </summary>
|
||||||
|
public static bool IsPublic => HostLeverHandler?.isOn ?? MatchmakingHandler.LobbyType == ELobbyType.k_ELobbyTypePublic;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前大厅ID
|
||||||
|
/// </summary>
|
||||||
|
public static CSteamID CurrentLobbyID => MatchmakingHandler.CurrentLobby;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 大厅房主信息
|
||||||
|
/// </summary>
|
||||||
|
public static PlayerInfo LobbyOwner => new(SteamMatchmaking.GetLobbyOwner(CurrentLobbyID));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 本地玩家信息
|
||||||
|
/// </summary>
|
||||||
|
public static PlayerInfo LocalPlayer => new(SteamUser.GetSteamID());
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 生命周期方法
|
||||||
|
|
||||||
|
private void Awake()
|
||||||
{
|
{
|
||||||
// 绑定配置
|
InitializeConfiguration();
|
||||||
blockKicks = Config.Bind<bool>("General", "BlockKicks", true, "是否屏蔽来自其他玩家的踢出请求");
|
ApplyPatches();
|
||||||
lobbyInfoToggleKeyCode = Config.Bind<KeyCode>("LobbyInfo", "ToggleKeyCode", KeyCode.F2, "Lobby信息开关快捷键");
|
}
|
||||||
// patch
|
|
||||||
if (blockKicks.Value)
|
private void Update()
|
||||||
|
{
|
||||||
|
HandleInput();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGUI()
|
||||||
|
{
|
||||||
|
if (PlayerManagerGUI.ShowLobbyInfo)
|
||||||
|
{
|
||||||
|
PlayerManagerGUI.DrawLobbyInfoGUI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 私有方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化配置项
|
||||||
|
/// </summary>
|
||||||
|
private void InitializeConfiguration()
|
||||||
|
{
|
||||||
|
_blockKicks = Config.Bind(
|
||||||
|
"General",
|
||||||
|
"BlockKicks",
|
||||||
|
true,
|
||||||
|
"是否屏蔽来自其他玩家的踢出请求"
|
||||||
|
);
|
||||||
|
|
||||||
|
_lobbyInfoToggleKeyCode = Config.Bind(
|
||||||
|
"LobbyInfo",
|
||||||
|
"ToggleKeyCode",
|
||||||
|
KeyCode.F2,
|
||||||
|
"Lobby信息开关快捷键"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 应用Harmony补丁
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyPatches()
|
||||||
|
{
|
||||||
|
if (_blockKicks.Value)
|
||||||
{
|
{
|
||||||
Harmony.CreateAndPatchAll(typeof(IgnoreKick));
|
Harmony.CreateAndPatchAll(typeof(IgnoreKick));
|
||||||
}
|
}
|
||||||
Harmony.CreateAndPatchAll(typeof(ApplyBlacklist));
|
Harmony.CreateAndPatchAll(typeof(ApplyBlacklist));
|
||||||
Harmony.CreateAndPatchAll(typeof(ListenForChange));
|
Harmony.CreateAndPatchAll(typeof(ListenForChange));
|
||||||
}
|
}
|
||||||
void Update()
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理输入
|
||||||
|
/// </summary>
|
||||||
|
private void HandleInput()
|
||||||
{
|
{
|
||||||
if (Input.GetKeyDown(lobbyInfoToggleKeyCode.Value))
|
if (Input.GetKeyDown(_lobbyInfoToggleKeyCode.Value))
|
||||||
{
|
{
|
||||||
if (PlayerManagerGUI.showLobbyInfo)
|
ToggleLobbyInfoGUI();
|
||||||
{
|
|
||||||
PlayerManagerGUI.showLobbyInfo = false;
|
|
||||||
Debug.Log("The GUI is closed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RequestLobbyList();
|
|
||||||
PlayerManagerGUI.showLobbyInfo = true;
|
|
||||||
Debug.Log("The GUI is opened");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void OnGUI()
|
|
||||||
|
/// <summary>
|
||||||
|
/// 切换大厅信息GUI显示状态
|
||||||
|
/// </summary>
|
||||||
|
private void ToggleLobbyInfoGUI()
|
||||||
{
|
{
|
||||||
if (PlayerManagerGUI.showLobbyInfo)
|
if (PlayerManagerGUI.ShowLobbyInfo)
|
||||||
{
|
{
|
||||||
PlayerManagerGUI.DrawLobbyInfoGUI();
|
PlayerManagerGUI.ShowLobbyInfo = false;
|
||||||
|
Debug.Log("大厅信息界面已关闭");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RequestLobbyList();
|
||||||
|
PlayerManagerGUI.ShowLobbyInfo = true;
|
||||||
|
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
|
||||||
|
|
||||||
|
#region 公共方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定玩家是否为房主
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player">要检查的玩家</param>
|
||||||
|
/// <returns>如果是房主返回true</returns>
|
||||||
public static bool IsHost(PlayerInfo player)
|
public static bool IsHost(PlayerInfo player)
|
||||||
{
|
{
|
||||||
return player.Equals(LobbyOwner);
|
return player.Equals(LobbyOwner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 判断指定玩家是否为本地玩家
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="player">要检查的玩家</param>
|
||||||
|
/// <returns>如果是本地玩家返回true</returns>
|
||||||
public static bool IsLocalPlayer(PlayerInfo player)
|
public static bool IsLocalPlayer(PlayerInfo player)
|
||||||
{
|
{
|
||||||
return player.Equals(LocalPlayer);
|
return player.Equals(LocalPlayer);
|
||||||
}
|
}
|
||||||
public static void RequestLobbyList()
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修改大厅公开性
|
||||||
|
/// </summary>
|
||||||
|
public static void ToggleLobbyPublicity()
|
||||||
{
|
{
|
||||||
SteamMatchmaking.AddRequestLobbyListDistanceFilter(elobbyDistanceFilter);
|
if (MultiplayerManager.IsServer)
|
||||||
SteamAPICall_t hAPICall2 = SteamMatchmaking.RequestLobbyList();
|
|
||||||
onLobbyMatchListCallResult.Set(hAPICall2, new CallResult<LobbyMatchList_t>.APIDispatchDelegate(OnRequestLobbyList));
|
|
||||||
}
|
|
||||||
static void OnRequestLobbyList(LobbyMatchList_t param, bool bIOFailure)
|
|
||||||
{
|
|
||||||
lobbyDataList = [];
|
|
||||||
uint nLobbiesMatching = param.m_nLobbiesMatching;
|
|
||||||
for (int i = 0; i < nLobbiesMatching; i++)
|
|
||||||
{
|
{
|
||||||
CSteamID lobbyByIndex = SteamMatchmaking.GetLobbyByIndex(i);
|
if (IsPublic)
|
||||||
if (SteamMatchmaking.GetLobbyData(lobbyByIndex, StickFightConstants.VERSION_KEY) == StickFightConstants.VERSION_VALUE)
|
|
||||||
{
|
{
|
||||||
lobbyDataList.Add(lobbyByIndex);
|
MatchmakingHandler.SetNewLobbyType(ELobbyType.k_ELobbyTypeFriendsOnly);
|
||||||
|
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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 请求大厅列表
|
||||||
|
/// </summary>
|
||||||
|
public static void RequestLobbyList()
|
||||||
|
{
|
||||||
|
Debug.Log("正在请求大厅列表...");
|
||||||
|
SteamMatchmaking.AddRequestLobbyListDistanceFilter(lobbyDistanceFilter);
|
||||||
|
SteamAPICall_t apiCall = SteamMatchmaking.RequestLobbyList();
|
||||||
|
LobbyMatchListCallResult.Set(apiCall, new CallResult<LobbyMatchList_t>.APIDispatchDelegate(OnRequestLobbyList));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 处理大厅列表查询结果
|
||||||
|
/// </summary>
|
||||||
|
private static void OnRequestLobbyList(LobbyMatchList_t result, bool ioFailure)
|
||||||
|
{
|
||||||
|
Debug.Log("大厅列表请求完成: " + result.m_nLobbiesMatching + " 个大厅");
|
||||||
|
lobbyDataList = [];
|
||||||
|
uint lobbyCount = result.m_nLobbiesMatching;
|
||||||
|
|
||||||
|
for (uint i = 0; i < lobbyCount; i++)
|
||||||
|
{
|
||||||
|
CSteamID lobbyId = SteamMatchmaking.GetLobbyByIndex((int)i);
|
||||||
|
if (SteamMatchmaking.GetLobbyData(lobbyId, StickFightConstants.VERSION_KEY) == StickFightConstants.VERSION_VALUE)
|
||||||
|
{
|
||||||
|
lobbyDataList.Add(lobbyId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Log("过滤掉不兼容版本的大厅: " + lobbyId);
|
||||||
|
Debug.Log("大厅 " + lobbyId + " 版本: " + SteamMatchmaking.GetLobbyData(lobbyId, StickFightConstants.VERSION_KEY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加入指定大厅
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lobby">要加入的大厅ID</param>
|
||||||
public static void JoinSpecificServer(CSteamID lobby)
|
public static void JoinSpecificServer(CSteamID lobby)
|
||||||
{
|
{
|
||||||
Traverse.Create(MatchmakingHandler.Instance).Field("mCurrentConnectionType").SetValue(ConnectionType.Specific);
|
Traverse.Create(MatchmakingHandler.Instance)
|
||||||
|
.Field("mCurrentConnectionType")
|
||||||
|
.SetValue(ConnectionType.Specific);
|
||||||
|
|
||||||
MatchmakingHandler.Instance.JoinServer(lobby, GameManager.Instance.mMultiplayerManager.OnServerJoined);
|
MatchmakingHandler.Instance.JoinServer(lobby, GameManager.Instance.mMultiplayerManager.OnServerJoined);
|
||||||
OnLobbyMembersChanged();
|
OnLobbyMembersChanged();
|
||||||
}
|
}
|
||||||
public static void KickPlayer(CSteamID clientID, MultiplayerManager.KickResponse response)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 踢出指定玩家
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="clientId">被踢玩家的Steam ID</param>
|
||||||
|
/// <param name="response">踢出原因响应</param>
|
||||||
|
public static void KickPlayer(CSteamID clientId, MultiplayerManager.KickResponse response)
|
||||||
{
|
{
|
||||||
byte[] data = [(byte)response];
|
byte[] data = [(byte)response];
|
||||||
P2PPackageHandler.Instance.SendP2PPacketToUser(clientID, data, P2PPackageHandler.MsgType.KickPlayer, EP2PSend.k_EP2PSendReliable, 0);
|
P2PPackageHandler.Instance.SendP2PPacketToUser(
|
||||||
|
clientId,
|
||||||
|
data,
|
||||||
|
P2PPackageHandler.MsgType.KickPlayer,
|
||||||
|
EP2PSend.k_EP2PSendReliable,
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当大厅成员变化时更新成员列表
|
||||||
|
/// </summary>
|
||||||
public static void OnLobbyMembersChanged()
|
public static void OnLobbyMembersChanged()
|
||||||
{
|
{
|
||||||
Debug.Log("OnLobbyMembersChanged被触发");
|
Debug.Log("大厅成员列表已更新");
|
||||||
// 使用锁保护集合操作
|
|
||||||
lock (lobbyMemberLock)
|
lock (LobbyMemberLock)
|
||||||
{
|
{
|
||||||
// 清空当前成员列表
|
|
||||||
lobbyMemberList.Clear();
|
lobbyMemberList.Clear();
|
||||||
|
|
||||||
// 重新构建当前成员列表
|
int memberLimit = SteamMatchmaking.GetLobbyMemberLimit(CurrentLobbyID);
|
||||||
int lobbyMemberLimit = SteamMatchmaking.GetLobbyMemberLimit(CurrentLobbyID);
|
for (int i = 0; i < memberLimit; i++)
|
||||||
for (int i = 0; i < lobbyMemberLimit; i++)
|
|
||||||
{
|
{
|
||||||
PlayerInfo lobbyMember = new(SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobbyID, i));
|
PlayerInfo member = new(SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobbyID, i));
|
||||||
// 合并记录状态
|
|
||||||
// 注意这里先合并普通记录再合并黑名单的顺序是必要的
|
// 合并记录状态:先合并普通记录,再合并黑名单
|
||||||
// 因为黑名单记录的曾用名必然比普通记录的曾用名更新
|
// 黑名单的曾用名比普通记录的曾用名更新
|
||||||
playerRecoder.CheckAndMerge(lobbyMember);
|
playerRecorder.CheckAndMerge(member);
|
||||||
blackListRecorder.CheckAndMerge(lobbyMember);
|
blackListRecorder.CheckAndMerge(member);
|
||||||
lobbyMemberList.Add(lobbyMember);
|
|
||||||
|
lobbyMemberList.Add(member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
214
RecorderBase.cs
214
RecorderBase.cs
@@ -6,81 +6,193 @@ using UnityEngine;
|
|||||||
|
|
||||||
namespace stick.plugins.playermanager;
|
namespace stick.plugins.playermanager;
|
||||||
|
|
||||||
abstract public class RecoderBase
|
/// <summary>
|
||||||
|
/// 记录器基类
|
||||||
|
/// 提供玩家记录和黑名单功能的通用实现
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RecorderBase
|
||||||
{
|
{
|
||||||
protected List<PlayerInfo> playerList = [];
|
#region 字段
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家信息集合(用于快速查找)
|
||||||
|
/// </summary>
|
||||||
|
protected HashSet<PlayerInfo> playerSet = new HashSet<PlayerInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 玩家信息列表(用于有序遍历和文件写入)
|
||||||
|
/// </summary>
|
||||||
|
protected List<PlayerInfo> playerList = new List<PlayerInfo>();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 抽象属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录文件名
|
||||||
|
/// </summary>
|
||||||
protected abstract string FileName { get; }
|
protected abstract string FileName { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录状态类型
|
||||||
|
/// </summary>
|
||||||
protected abstract PlayerRecordStatus RecordStatus { get; }
|
protected abstract PlayerRecordStatus RecordStatus { get; }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录文件完整路径
|
||||||
|
/// </summary>
|
||||||
protected string FilePath => Path.Combine(Application.dataPath, FileName);
|
protected string FilePath => Path.Combine(Application.dataPath, FileName);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 构造函数
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 构造函数,初始化时加载已有记录
|
||||||
|
/// </summary>
|
||||||
|
protected RecorderBase()
|
||||||
|
{
|
||||||
|
RefreshPlayerRecordList();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 公共方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查玩家是否已被记录
|
||||||
|
/// </summary>
|
||||||
public bool IsPlayerRecorded(PlayerInfo playerInfo)
|
public bool IsPlayerRecorded(PlayerInfo playerInfo)
|
||||||
{
|
{
|
||||||
foreach (PlayerInfo info in playerList)
|
return playerInfo != null && playerSet.Contains(playerInfo);
|
||||||
{
|
|
||||||
if (info.Equals(playerInfo)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查玩家是否已被记录(通过Steam ID)
|
||||||
|
/// </summary>
|
||||||
public bool IsPlayerRecorded(CSteamID playerId)
|
public bool IsPlayerRecorded(CSteamID playerId)
|
||||||
{
|
{
|
||||||
PlayerInfo playerInfo = new(playerId);
|
PlayerInfo playerInfo = new PlayerInfo(playerId);
|
||||||
foreach (PlayerInfo info in playerList)
|
return playerSet.Contains(playerInfo);
|
||||||
{
|
|
||||||
if (info.Equals(playerInfo)) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录玩家
|
||||||
|
/// </summary>
|
||||||
public void RecordPlayer(PlayerInfo playerInfo)
|
public void RecordPlayer(PlayerInfo playerInfo)
|
||||||
{
|
{
|
||||||
|
if (playerInfo == null) return;
|
||||||
|
|
||||||
playerInfo.RecordStatus = RecordStatus;
|
playerInfo.RecordStatus = RecordStatus;
|
||||||
playerList.Add(playerInfo);
|
|
||||||
using StreamWriter streamWriter = new(FilePath, true);
|
if (playerSet.Add(playerInfo))
|
||||||
streamWriter.WriteLine(playerInfo);
|
{
|
||||||
|
playerList.Add(playerInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
AppendToFile(playerInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取消记录玩家
|
||||||
|
/// </summary>
|
||||||
|
public void CancelRecordPlayer(PlayerInfo playerInfo)
|
||||||
|
{
|
||||||
|
if (playerInfo == null) return;
|
||||||
|
|
||||||
|
if (playerSet.Remove(playerInfo))
|
||||||
|
{
|
||||||
|
playerList.Remove(playerInfo);
|
||||||
|
WriteBackToFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查并合并玩家记录信息
|
||||||
|
/// </summary>
|
||||||
|
public void CheckAndMerge(PlayerInfo playerInfo)
|
||||||
|
{
|
||||||
|
if (playerInfo == null) return;
|
||||||
|
|
||||||
|
foreach (PlayerInfo existingInfo in playerSet)
|
||||||
|
{
|
||||||
|
if (existingInfo.Equals(playerInfo))
|
||||||
|
{
|
||||||
|
playerInfo.MergeWith(existingInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 保护方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 刷新玩家记录列表(从文件加载)
|
||||||
|
/// </summary>
|
||||||
protected void RefreshPlayerRecordList()
|
protected void RefreshPlayerRecordList()
|
||||||
{
|
{
|
||||||
if (!File.Exists(FilePath))
|
if (!File.Exists(FilePath))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
playerSet.Clear();
|
||||||
playerList.Clear();
|
playerList.Clear();
|
||||||
using StreamReader streamReader = new(FilePath);
|
|
||||||
string item;
|
using (StreamReader reader = new StreamReader(FilePath))
|
||||||
while ((item = streamReader.ReadLine()) != null)
|
|
||||||
{
|
{
|
||||||
PlayerInfo playerInfo = new(item)
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
{
|
{
|
||||||
RecordStatus = RecordStatus
|
if (string.IsNullOrEmpty(line)) continue;
|
||||||
};
|
|
||||||
playerList.Add(playerInfo);
|
PlayerInfo playerInfo = new PlayerInfo(line)
|
||||||
}
|
{
|
||||||
}
|
RecordStatus = RecordStatus
|
||||||
protected void WriteBackToFile()
|
};
|
||||||
{
|
|
||||||
using StreamWriter streamWriter = new(FilePath, false);
|
if (playerSet.Add(playerInfo))
|
||||||
foreach (PlayerInfo info in playerList)
|
{
|
||||||
{
|
playerList.Add(playerInfo);
|
||||||
streamWriter.WriteLine(info.ToInfoString());
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
public void CancelRecordPlayer(PlayerInfo playerInfo)
|
|
||||||
{
|
|
||||||
playerList.Remove(playerInfo);
|
|
||||||
WriteBackToFile();
|
|
||||||
}
|
|
||||||
protected RecoderBase()
|
|
||||||
{
|
|
||||||
RefreshPlayerRecordList();
|
|
||||||
}
|
|
||||||
public void CheckAndMerge(PlayerInfo playerInfo)
|
|
||||||
{
|
|
||||||
if (playerInfo is null) return;
|
|
||||||
for (int i = 0; i < playerList.Count; i++)
|
|
||||||
{
|
|
||||||
if (playerList[i].Equals(playerInfo))
|
|
||||||
{
|
|
||||||
playerInfo.MergeWith(playerList[i]);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将玩家信息写入文件
|
||||||
|
/// </summary>
|
||||||
|
protected void WriteBackToFile()
|
||||||
|
{
|
||||||
|
using (StreamWriter writer = new StreamWriter(FilePath, false))
|
||||||
|
{
|
||||||
|
foreach (PlayerInfo info in playerList)
|
||||||
|
{
|
||||||
|
writer.WriteLine(info.ToInfoString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 私有方法
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 追加玩家信息到文件
|
||||||
|
/// </summary>
|
||||||
|
private void AppendToFile(PlayerInfo playerInfo)
|
||||||
|
{
|
||||||
|
using (StreamWriter writer = new StreamWriter(FilePath, true))
|
||||||
|
{
|
||||||
|
writer.WriteLine(playerInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
@@ -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.0.4</Version>
|
<Version>3.1.1</Version>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
<RestoreAdditionalProjectSources>
|
<RestoreAdditionalProjectSources>
|
||||||
|
|||||||
Reference in New Issue
Block a user