增加举报功能

This commit is contained in:
wuwenbo 2026-05-26 23:59:19 +08:00
parent 31144a4a33
commit c8e9195fd7
4 changed files with 183 additions and 24 deletions

View File

@ -7,6 +7,7 @@
using System.Collections.Generic;
using TH1_Logic.Steam;
using UnityEngine;
namespace TH1_Logic.Net
@ -39,6 +40,9 @@ namespace TH1_Logic.Net
// 邀请好友
public void InviteFriend(ulong memberId);
// 举报房间
public bool ReportLobby(LobbyListInfo lobbyInfo);
// 踢出成员
public void KickMember(ulong memberId);
@ -177,6 +181,11 @@ namespace TH1_Logic.Net
}
public bool ReportLobby(LobbyListInfo lobbyInfo)
{
return false;
}
public void KickMember(ulong memberId)
{

View File

@ -40,6 +40,7 @@ namespace TH1_Logic.Steam
if (message.MessageType == P2PMsgType.HeartbeatReply) OnReceivedHeartbeatReply((HeartbeatReplyMessage)message);
if (message.MessageType == P2PMsgType.ChatMessage) OnReceivedChatMessage((ChatMessage)message);
if (message.MessageType == P2PMsgType.InviteMessage) OnReceivedInviteMessage((InviteMessage)message);
if (message.MessageType == P2PMsgType.LobbyReport) OnReceivedLobbyReport((LobbyReportMessage)message);
}
catch (System.Exception e)
{
@ -593,5 +594,19 @@ namespace TH1_Logic.Steam
}
}
// 房间外举报消息,只有被举报房间的房主处理
private void OnReceivedLobbyReport(LobbyReportMessage message)
{
if (message == null)
{
LogSystem.LogError($"消息解析失败: OnReceivedLobbyReport");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.NetworkMessageParseFailed);
return;
}
if (LobbyManager.Instance.Lobby is SteamLobbyManager steamLobby)
steamLobby.OnReceivedLobbyReport(message);
}
}
}

View File

@ -86,6 +86,12 @@ namespace TH1_Logic.Steam
private const string LobbyHasPasswordKey = "HasPassword";
private const string LobbyPasswordKey = "Password";
private const string LobbyIsPublicKey = "IsPublic";
private const string LobbyOwnerSteamIdKey = "OwnerSteamId";
private const string LobbyReportCountKey = "ReportCount";
private const string LobbyRoomNameKey = "RoomName";
private const string LobbyGameStateKey = "GameState";
private const int LobbyReportRenameThreshold = 5;
private const string ReportedLobbyDefaultRoomName = "Default";
private string _pendingLobbyPassword = "";
private bool _pendingLobbyIsPublic = true;
private CSteamID _pendingJoinLobby = CSteamID.Nil;
@ -96,6 +102,7 @@ namespace TH1_Logic.Steam
// 房间列表
private List<LobbyListInfo> _lobbyListInfos;
public List<LobbyListInfo> LobbyListInfos => _lobbyListInfos;
private readonly HashSet<ulong> _lobbyReporters = new HashSet<ulong>();
// 事件委托
public event System.Action<CSteamID> OnLobbyCreatedEvent; // 房间创建成功
@ -988,11 +995,13 @@ namespace TH1_Logic.Steam
OwnerId = _selfID.m_SteamID,
OwnerName = SteamMatchmaking.GetLobbyData(CurrentLobby, "Owner"),
RoomName = SteamMatchmaking.GetLobbyData(CurrentLobby, "RoomName"),
RoomName = SteamMatchmaking.GetLobbyData(CurrentLobby, LobbyRoomNameKey),
Version = SteamMatchmaking.GetLobbyData(CurrentLobby, "Version"),
CurrentPlayers = SteamMatchmaking.GetNumLobbyMembers(CurrentLobby),
MaxPlayers = SteamMatchmaking.GetLobbyMemberLimit(CurrentLobby),
GameState = GetLobbyGameStateFromData(CurrentLobby),
HasPassword = LobbyHasPassword(CurrentLobby),
ReportCount = GetLobbyReportCountFromData(CurrentLobby),
};
var data = new InviteMessage();
data.LobbyInfo = lobbyInfo;
@ -1017,6 +1026,98 @@ namespace TH1_Logic.Steam
LogSystem.LogInfo($"Game invite sent to: {targetSteamId}");
return true;
}
public bool ReportLobby(LobbyListInfo lobbyInfo)
{
if (lobbyInfo == null || lobbyInfo.LobbyId == 0)
{
LogSystem.LogError("Report lobby failed: invalid lobby info");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
if (lobbyInfo.OwnerId == 0)
{
LogSystem.LogError($"Report lobby failed: missing owner Steam ID, lobby={lobbyInfo.LobbyId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
var selfId = GetSelfMemberId();
if (selfId == 0)
{
LogSystem.LogError("Report lobby failed: missing self Steam ID");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
if (lobbyInfo.OwnerId == selfId)
{
LogSystem.LogInfo($"Report lobby skipped: cannot report own lobby, lobby={lobbyInfo.LobbyId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
var targetId = new CSteamID(lobbyInfo.OwnerId);
if (!targetId.IsValid())
{
LogSystem.LogError($"Report lobby failed: invalid owner Steam ID, owner={lobbyInfo.OwnerId}, lobby={lobbyInfo.LobbyId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
var data = new LobbyReportMessage
{
LobbyId = lobbyInfo.LobbyId,
ReporterId = selfId,
Version = ConfigManager.Instance.VersionCfg.CurVersionInfo.Version,
};
var bytes = MemoryPackSerializer.Serialize<BaseMessage>(data);
if (!SimpleP2P.Instance.SendToWithOutConnect(targetId, bytes))
{
LogSystem.LogError($"Report lobby failed: send to owner failed, lobby={lobbyInfo.LobbyId}, owner={lobbyInfo.OwnerId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
return false;
}
LogSystem.LogInfo($"Report lobby sent: lobby={lobbyInfo.LobbyId}, owner={lobbyInfo.OwnerId}, reporter={selfId}");
return true;
}
public void OnReceivedLobbyReport(LobbyReportMessage message)
{
if (message == null) return;
if (!IsLobbyOwner()) return;
if (!CurrentLobby.IsValid() || message.LobbyId != CurrentLobby.m_SteamID)
{
LogSystem.LogWarning($"Ignore lobby report for unmatched lobby: current={CurrentLobby.m_SteamID}, report={message.LobbyId}");
return;
}
if (message.ReporterId == 0 || message.ReporterId == GetSelfMemberId())
{
LogSystem.LogWarning($"Ignore invalid lobby report: lobby={message.LobbyId}, reporter={message.ReporterId}");
return;
}
if (_lobbyReporters.Contains(message.ReporterId))
{
LogSystem.LogInfo($"Ignore duplicate lobby report: lobby={message.LobbyId}, reporter={message.ReporterId}");
return;
}
_lobbyReporters.Add(message.ReporterId);
var reportCount = GetLobbyReportCountFromData(CurrentLobby) + 1;
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyReportCountKey, reportCount.ToString());
LogSystem.LogInfo($"Lobby report received: lobby={message.LobbyId}, reporter={message.ReporterId}, count={reportCount}");
if (reportCount <= LobbyReportRenameThreshold) return;
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyRoomNameKey, ReportedLobbyDefaultRoomName);
RoomName = ReportedLobbyDefaultRoomName;
EventManager.Publish(new UpdateUIOutsideMultiplayLobbyList());
LogSystem.LogInfo($"Lobby report count exceeded threshold, room name reset: lobby={message.LobbyId}, count={reportCount}");
}
// 搜索房间
public void SearchPublicLobbies(ELobbyDistanceFilter filter = ELobbyDistanceFilter.k_ELobbyDistanceFilterWorldwide,
@ -1052,20 +1153,9 @@ namespace TH1_Logic.Steam
for (int i = 0; i < data.m_nLobbiesMatching; i++)
{
var lobbyId = SteamMatchmaking.GetLobbyByIndex(i);
var ownerId = SteamMatchmaking.GetLobbyOwner(lobbyId);
_lobbyListInfos.Add(new LobbyListInfo
{
LobbyId = lobbyId.m_SteamID,
OwnerId = ownerId.m_SteamID,
OwnerName = SteamMatchmaking.GetLobbyData(lobbyId, "Owner"),
RoomName = SteamMatchmaking.GetLobbyData(lobbyId, "RoomName"),
Version = SteamMatchmaking.GetLobbyData(lobbyId, "Version"),
CurrentPlayers = SteamMatchmaking.GetNumLobbyMembers(lobbyId),
MaxPlayers = SteamMatchmaking.GetLobbyMemberLimit(lobbyId),
GameState = int.Parse(SteamMatchmaking.GetLobbyData(lobbyId, "GameState")),
HasPassword = LobbyHasPassword(lobbyId),
});
var lobbyInfo = new LobbyListInfo { LobbyId = lobbyId.m_SteamID };
RefreshLobbyListInfo(lobbyInfo, lobbyId);
_lobbyListInfos.Add(lobbyInfo);
}
// 触发UI刷新事件
@ -1078,16 +1168,43 @@ namespace TH1_Logic.Steam
foreach (var lobbyInfo in _lobbyListInfos)
{
var cSteamId = new CSteamID(lobbyInfo.LobbyId);
lobbyInfo.OwnerName = SteamMatchmaking.GetLobbyData(cSteamId, "Owner");
lobbyInfo.RoomName = SteamMatchmaking.GetLobbyData(cSteamId, "RoomName");
lobbyInfo.Version = SteamMatchmaking.GetLobbyData(cSteamId, "Version");
lobbyInfo.CurrentPlayers = SteamMatchmaking.GetNumLobbyMembers(cSteamId);
lobbyInfo.MaxPlayers = SteamMatchmaking.GetLobbyMemberLimit(cSteamId);
lobbyInfo.GameState = int.Parse(SteamMatchmaking.GetLobbyData(cSteamId, "GameState"));
lobbyInfo.HasPassword = LobbyHasPassword(cSteamId);
RefreshLobbyListInfo(lobbyInfo, cSteamId);
}
}
private void RefreshLobbyListInfo(LobbyListInfo lobbyInfo, CSteamID lobbyId)
{
if (lobbyInfo == null) return;
lobbyInfo.LobbyId = lobbyId.m_SteamID;
lobbyInfo.OwnerId = GetLobbyOwnerSteamIdFromData(lobbyId);
lobbyInfo.OwnerName = SteamMatchmaking.GetLobbyData(lobbyId, "Owner");
lobbyInfo.RoomName = SteamMatchmaking.GetLobbyData(lobbyId, LobbyRoomNameKey);
lobbyInfo.Version = SteamMatchmaking.GetLobbyData(lobbyId, "Version");
lobbyInfo.CurrentPlayers = SteamMatchmaking.GetNumLobbyMembers(lobbyId);
lobbyInfo.MaxPlayers = SteamMatchmaking.GetLobbyMemberLimit(lobbyId);
lobbyInfo.GameState = GetLobbyGameStateFromData(lobbyId);
lobbyInfo.HasPassword = LobbyHasPassword(lobbyId);
lobbyInfo.ReportCount = GetLobbyReportCountFromData(lobbyId);
}
private static ulong GetLobbyOwnerSteamIdFromData(CSteamID lobbyId)
{
var ownerIdData = SteamMatchmaking.GetLobbyData(lobbyId, LobbyOwnerSteamIdKey);
return ulong.TryParse(ownerIdData, out var ownerId) ? ownerId : 0;
}
private static int GetLobbyReportCountFromData(CSteamID lobbyId)
{
var reportCountData = SteamMatchmaking.GetLobbyData(lobbyId, LobbyReportCountKey);
return int.TryParse(reportCountData, out var reportCount) && reportCount > 0 ? reportCount : 0;
}
private static int GetLobbyGameStateFromData(CSteamID lobbyId)
{
var gameStateData = SteamMatchmaking.GetLobbyData(lobbyId, LobbyGameStateKey);
return int.TryParse(gameStateData, out var gameState) ? gameState : 0;
}
// 过滤房间名称,只保留中文、英文字母、数字和常用符号
public static string FilterRoomName(string input, int maxLength = 20)
{
@ -1221,6 +1338,7 @@ namespace TH1_Logic.Steam
}
CurrentLobby = new CSteamID(data.m_ulSteamIDLobby);
_lobbyReporters.Clear();
if (!TrySteamApi("OnLobbyCreatedCallback.GetSteamID", SteamUser.GetSteamID, out CachedOwner))
{
ResetLobbyState();
@ -1232,11 +1350,13 @@ namespace TH1_Logic.Steam
// 设置房间基础数据
SteamMatchmaking.SetLobbyData(CurrentLobby, "Game", "TOHOTOPIA");
SteamMatchmaking.SetLobbyData(CurrentLobby, "Owner", SelfName);
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyOwnerSteamIdKey, _selfID.m_SteamID.ToString());
SteamMatchmaking.SetLobbyData(CurrentLobby, "Version", ConfigManager.Instance.VersionCfg.CurVersionInfo.Version);
//SteamMatchmaking.SetLobbyData(CurrentLobby, "RoomName", FilterRoomName(SelfName + MultilingualManager.Instance.GetMultilingualText(Table.Instance.TextDataAssets.OutsideMultiplayRoomNameSuffix)));
SteamMatchmaking.SetLobbyData(CurrentLobby, "RoomName", SelfName + MultilingualManager.Instance.GetMultilingualText(Table.Instance.TextDataAssets.OutsideMultiplayRoomNameSuffix));
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyRoomNameKey, SelfName + MultilingualManager.Instance.GetMultilingualText(Table.Instance.TextDataAssets.OutsideMultiplayRoomNameSuffix));
SteamMatchmaking.SetLobbyData(CurrentLobby, "RoomCode", GenerateRoomCode());
SteamMatchmaking.SetLobbyData(CurrentLobby, "GameState", "0");
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyGameStateKey, "0");
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyReportCountKey, "0");
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyHasPasswordKey, string.IsNullOrEmpty(_pendingLobbyPassword) ? "false" : "true");
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyPasswordKey, _pendingLobbyPassword);
SteamMatchmaking.SetLobbyData(CurrentLobby, LobbyIsPublicKey, _pendingLobbyIsPublic ? "true" : "false");
@ -1474,6 +1594,7 @@ namespace TH1_Logic.Steam
_pendingLobbyPassword = "";
_pendingLobbyIsPublic = true;
ClearPendingJoinLobby();
_lobbyReporters.Clear();
// 清除Rich Presence
SteamFriends.ClearRichPresence();
@ -1886,5 +2007,6 @@ namespace TH1_Logic.Steam
public int MaxPlayers;
public int GameState;
public bool HasPassword;
public int ReportCount;
}
}

View File

@ -59,6 +59,8 @@ namespace TH1_Logic.Steam
InviteMessage = 16,
// 网络压测工具消息
NetworkStress = 17,
// 房间举报
LobbyReport = 18,
}
public enum NetworkStressMessageKind : byte
@ -89,6 +91,7 @@ namespace TH1_Logic.Steam
[MemoryPackUnion(15, typeof(ChatMessage))]
[MemoryPackUnion(16, typeof(InviteMessage))]
[MemoryPackUnion(17, typeof(NetworkStressMessage))]
[MemoryPackUnion(18, typeof(LobbyReportMessage))]
public abstract partial class BaseMessage
{
public abstract P2PMsgType MessageType { get; }
@ -231,6 +234,16 @@ namespace TH1_Logic.Steam
}
[MemoryPackable]
public partial class LobbyReportMessage : BaseMessage
{
public override P2PMsgType MessageType => P2PMsgType.LobbyReport;
public ulong LobbyId { get; set; }
public ulong ReporterId { get; set; }
public string Version { get; set; }
}
[MemoryPackable]
public partial class NetworkStressMessage : BaseMessage
{