mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-13 11:40:27 +00:00
feat: PartyRoomGameLogTrackerProvider
This commit is contained in:
parent
f551ccfbde
commit
3e9f82ecdf
@ -15,8 +15,8 @@ class LogAnalyzeLineData {
|
||||
|
||||
// 格式化后的字段
|
||||
final String? victimId; // 受害者ID (actor_death)
|
||||
final String? killerId; // 击杀者ID (actor_death)
|
||||
final String? location; // 位置信息 (request_location_inventory)
|
||||
final String? area; // 区域信息
|
||||
final String? playerName; // 玩家名称 (player_login)
|
||||
|
||||
const LogAnalyzeLineData({
|
||||
@ -26,8 +26,8 @@ class LogAnalyzeLineData {
|
||||
this.dateTime,
|
||||
this.tag,
|
||||
this.victimId,
|
||||
this.killerId,
|
||||
this.location,
|
||||
this.area,
|
||||
this.playerName,
|
||||
});
|
||||
|
||||
@ -76,10 +76,10 @@ class GameLogAnalyzer {
|
||||
|
||||
// 致命碰撞解析
|
||||
static final _fatalCollisionPatterns = {
|
||||
'zone': RegExp(r'\[Part:[^\]]*?Zone:\s*([^,\]]+)'),
|
||||
'vehicle': RegExp(r'Fatal Collision occured for vehicle\s+(\S+)'),
|
||||
'zone': RegExp(r'Zone:\s*([^,\]]+)'),
|
||||
'player_pilot': RegExp(r'PlayerPilot:\s*(\d)'),
|
||||
'hit_entity': RegExp(r'hitting entity:\s*(\w+)'),
|
||||
'hit_entity_vehicle': RegExp(r'hitting entity:[^\[]*\[Zone:\s*([^\s-]+)'),
|
||||
'distance': RegExp(r'Distance:\s*([\d.]+)'),
|
||||
};
|
||||
|
||||
@ -93,10 +93,9 @@ class GameLogAnalyzer {
|
||||
|
||||
// 角色死亡解析
|
||||
static final _actorDeathPattern = RegExp(
|
||||
r"CActor::Kill: '([^']+)'.*?" // 受害者ID
|
||||
r"in zone '([^']+)'.*?" // 死亡位置区域
|
||||
r"killed by '([^']+)'.*?" // 击杀者ID
|
||||
r"with damage type '([^']+)'", // 伤害类型
|
||||
r"Actor '([^']+)'.*?" // 受害者ID
|
||||
r"ejected from zone '([^']+)'.*?" // 原载具/区域
|
||||
r"to zone '([^']+)'", // 目标区域
|
||||
);
|
||||
|
||||
// 角色名称解析
|
||||
@ -217,7 +216,7 @@ class GameLogAnalyzer {
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "Actor Death":
|
||||
case "[ActorState] Dead":
|
||||
data = _parseActorDeath(line, playerName, shouldCount, (isKill, isDeath, isSelfKill) {
|
||||
if (isSelfKill) {
|
||||
selfKillCount++;
|
||||
@ -372,10 +371,10 @@ class GameLogAnalyzer {
|
||||
static String? _safeExtract(RegExp pattern, String line) => pattern.firstMatch(line)?.group(1)?.trim();
|
||||
|
||||
static LogAnalyzeLineData? _parseFatalCollision(String line) {
|
||||
final vehicle = _safeExtract(_fatalCollisionPatterns['vehicle']!, line) ?? unknownValue;
|
||||
final zone = _safeExtract(_fatalCollisionPatterns['zone']!, line) ?? unknownValue;
|
||||
final playerPilot = (_safeExtract(_fatalCollisionPatterns['player_pilot']!, line) ?? '0') == '1';
|
||||
final hitEntity = _safeExtract(_fatalCollisionPatterns['hit_entity']!, line) ?? unknownValue;
|
||||
final hitEntityVehicle = _safeExtract(_fatalCollisionPatterns['hit_entity_vehicle']!, line) ?? unknownValue;
|
||||
final distance = double.tryParse(_safeExtract(_fatalCollisionPatterns['distance']!, line) ?? '') ?? 0.0;
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
@ -385,7 +384,7 @@ class GameLogAnalyzer {
|
||||
zone,
|
||||
playerPilot ? '✅' : '❌',
|
||||
hitEntity,
|
||||
hitEntityVehicle,
|
||||
vehicle,
|
||||
distance.toStringAsFixed(2),
|
||||
),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
@ -436,27 +435,25 @@ class GameLogAnalyzer {
|
||||
final match = _actorDeathPattern.firstMatch(line);
|
||||
if (match != null) {
|
||||
final victimId = match.group(1) ?? unknownValue;
|
||||
final zone = match.group(2) ?? unknownValue;
|
||||
final killerId = match.group(3) ?? unknownValue;
|
||||
final damageType = match.group(4) ?? unknownValue;
|
||||
final fromZone = match.group(2) ?? unknownValue;
|
||||
final toZone = match.group(3) ?? unknownValue;
|
||||
|
||||
if (shouldCount) {
|
||||
if (victimId.trim() == killerId.trim()) {
|
||||
onDeath(false, false, true); // 自杀
|
||||
} else {
|
||||
final isDeath = victimId.trim() == playerName;
|
||||
final isKill = killerId.trim() == playerName;
|
||||
onDeath(isKill, isDeath, false);
|
||||
final isDeath = victimId.trim() == playerName;
|
||||
if (isDeath) {
|
||||
onDeath(false, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
return LogAnalyzeLineData(
|
||||
type: "actor_death",
|
||||
title: S.current.log_analyzer_filter_character_death,
|
||||
data: S.current.log_analyzer_death_details(victimId, damageType, killerId, zone),
|
||||
data: S.current.log_analyzer_death_details(victimId, fromZone, toZone),
|
||||
dateTime: _getLogLineDateTimeString(line),
|
||||
victimId: victimId, // 格式化字段
|
||||
killerId: killerId, // 格式化字段
|
||||
location: fromZone,
|
||||
area: toZone,
|
||||
victimId: victimId,
|
||||
playerName: playerName,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -130,8 +130,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m46(v0, v1, v2, v3, v4) =>
|
||||
"Area: ${v0} Player driving: ${v1} Collision entity: ${v2} \nCollision vehicle: ${v3} Collision distance: ${v4} ";
|
||||
|
||||
static String m47(v0, v1, v2, v3) =>
|
||||
"Victim ID: ${v0} Cause of death: ${v1} \nKiller ID: ${v2} \nArea: ${v3}";
|
||||
static String m47(v0, v2, v3) =>
|
||||
"Victim ID: ${v0} \nLocation: ${v2} \nArea: ${v3}";
|
||||
|
||||
static String m48(v0) => "Detailed information: ${v0}";
|
||||
|
||||
|
||||
@ -119,8 +119,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m46(v0, v1, v2, v3, v4) =>
|
||||
"エリア:${v0} プレイヤー操縦:${v1} 衝突エンティティ:${v2} \n衝突ビークル:${v3} 衝突距離:${v4} ";
|
||||
|
||||
static String m47(v0, v1, v2, v3) =>
|
||||
"被害者ID:${v0} 死因:${v1} \n殺害者ID:${v2} \nエリア:${v3}";
|
||||
static String m47(v0, v2, v3) => "被害者ID:${v0} \n位置:${v2} \nエリア:${v3}";
|
||||
|
||||
static String m48(v0) => "詳細情報:${v0}";
|
||||
|
||||
|
||||
@ -124,8 +124,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m46(v0, v1, v2, v3, v4) =>
|
||||
"Зона: ${v0} Управление игроком: ${v1} Объект столкновения: ${v2} \nТехника столкновения: ${v3} Дистанция столкновения: ${v4} ";
|
||||
|
||||
static String m47(v0, v1, v2, v3) =>
|
||||
"ID жертвы: ${v0} Причина смерти: ${v1} \nID убийцы: ${v2} \nЗона: ${v3}";
|
||||
static String m47(v0, v2, v3) =>
|
||||
"ID жертвы: ${v0} \nID убийцы: ${v2} \nЗона: ${v3}";
|
||||
|
||||
static String m48(v0) => "Подробная информация: ${v0}";
|
||||
|
||||
|
||||
@ -119,8 +119,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m46(v0, v1, v2, v3, v4) =>
|
||||
"区域:${v0} 玩家驾驶:${v1} 碰撞实体:${v2} \n碰撞载具: ${v3} 碰撞距离:${v4} ";
|
||||
|
||||
static String m47(v0, v1, v2, v3) =>
|
||||
"受害者ID:${v0} 死因:${v1} \n击杀者ID:${v2} \n区域:${v3}";
|
||||
static String m47(v0, v2, v3) => "受害者ID:${v0} \n位置:${v2} \n区域:${v3}";
|
||||
|
||||
static String m48(v0) => "详细信息:${v0}";
|
||||
|
||||
@ -1406,7 +1405,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tools_rsi_launcher_enhance_init_msg2":
|
||||
MessageLookupByLibrary.simpleMessage("正在从网络获取增强数据..."),
|
||||
"tools_rsi_launcher_enhance_msg_error":
|
||||
MessageLookupByLibrary.simpleMessage("获取增强数据失败,可能是网络问题或当前版本不支持"),
|
||||
MessageLookupByLibrary.simpleMessage("当前版本暂不支持,請等待適配..."),
|
||||
"tools_rsi_launcher_enhance_msg_error_get_launcher_info_error":
|
||||
MessageLookupByLibrary.simpleMessage("读取启动器信息失败!"),
|
||||
"tools_rsi_launcher_enhance_msg_error_get_launcher_info_error_with_args":
|
||||
|
||||
@ -115,8 +115,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m46(v0, v1, v2, v3, v4) =>
|
||||
"區域:${v0} 玩家駕駛:${v1} 碰撞實體:${v2} \n碰撞載具: ${v3} 碰撞距離:${v4} ";
|
||||
|
||||
static String m47(v0, v1, v2, v3) =>
|
||||
"受害者ID:${v0} 死因:${v1} \n擊殺者ID:${v2} \n區域:${v3}";
|
||||
static String m47(v0, v2, v3) => "受害者ID:${v0} \n位置:${v2} \n區域:${v3}";
|
||||
|
||||
static String m48(v0) => "詳細資訊:${v0}";
|
||||
|
||||
@ -1397,7 +1396,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"tools_rsi_launcher_enhance_init_msg2":
|
||||
MessageLookupByLibrary.simpleMessage("正在從網路取得增強資料..."),
|
||||
"tools_rsi_launcher_enhance_msg_error":
|
||||
MessageLookupByLibrary.simpleMessage("增強資料取得失敗,可能是網路問題或目前版本不支援"),
|
||||
MessageLookupByLibrary.simpleMessage("目前版本不支援"),
|
||||
"tools_rsi_launcher_enhance_msg_error_get_launcher_info_error":
|
||||
MessageLookupByLibrary.simpleMessage("讀取啟動器資訊失敗!"),
|
||||
"tools_rsi_launcher_enhance_msg_error_get_launcher_info_error_with_args":
|
||||
|
||||
@ -5808,18 +5808,13 @@ class S {
|
||||
);
|
||||
}
|
||||
|
||||
/// `Victim ID: {v0} Cause of death: {v1} \nKiller ID: {v2} \nArea: {v3}`
|
||||
String log_analyzer_death_details(
|
||||
Object v0,
|
||||
Object v1,
|
||||
Object v2,
|
||||
Object v3,
|
||||
) {
|
||||
/// `Victim ID: {v0} \nLocation: {v2} \nArea: {v3}`
|
||||
String log_analyzer_death_details(Object v0, Object v2, Object v3) {
|
||||
return Intl.message(
|
||||
'Victim ID: $v0 Cause of death: $v1 \nKiller ID: $v2 \nArea: $v3',
|
||||
'Victim ID: $v0 \nLocation: $v2 \nArea: $v3',
|
||||
name: 'log_analyzer_death_details',
|
||||
desc: '',
|
||||
args: [v0, v1, v2, v3],
|
||||
args: [v0, v2, v3],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -1141,7 +1141,7 @@
|
||||
"@log_analyzer_disintegration": {},
|
||||
"log_analyzer_vehicle_damage_details": "Vehicle model: {v0} \nArea: {v1} \nDamage level: {v2} ({v3}) Responsible party: {v4}",
|
||||
"@log_analyzer_vehicle_damage_details": {},
|
||||
"log_analyzer_death_details": "Victim ID: {v0} Cause of death: {v1} \nKiller ID: {v2} \nArea: {v3}",
|
||||
"log_analyzer_death_details": "Victim ID: {v0} \nLocation: {v2} \nArea: {v3}",
|
||||
"@log_analyzer_death_details": {},
|
||||
"log_analyzer_player_login": "Player {v0} logged in...",
|
||||
"@log_analyzer_player_login": {},
|
||||
|
||||
@ -1139,7 +1139,7 @@
|
||||
"@log_analyzer_disintegration": {},
|
||||
"log_analyzer_vehicle_damage_details": "ビークルモデル:{v0} \nエリア:{v1} \n損傷レベル:{v2} ({v3}) 責任者:{v4}",
|
||||
"@log_analyzer_vehicle_damage_details": {},
|
||||
"log_analyzer_death_details": "被害者ID:{v0} 死因:{v1} \n殺害者ID:{v2} \nエリア:{v3}",
|
||||
"log_analyzer_death_details": "被害者ID:{v0} \n位置:{v2} \nエリア:{v3}",
|
||||
"@log_analyzer_death_details": {},
|
||||
"log_analyzer_player_login": "プレイヤー {v0} ログイン中...",
|
||||
"@log_analyzer_player_login": {},
|
||||
|
||||
@ -1139,7 +1139,7 @@
|
||||
"@log_analyzer_disintegration": {},
|
||||
"log_analyzer_vehicle_damage_details": "Модель техники: {v0} \nЗона: {v1} \nУровень повреждения: {v2} ({v3}) Виновник: {v4}",
|
||||
"@log_analyzer_vehicle_damage_details": {},
|
||||
"log_analyzer_death_details": "ID жертвы: {v0} Причина смерти: {v1} \nID убийцы: {v2} \nЗона: {v3}",
|
||||
"log_analyzer_death_details": "ID жертвы: {v0} \nID убийцы: {v2} \nЗона: {v3}",
|
||||
"@log_analyzer_death_details": {},
|
||||
"log_analyzer_player_login": "Игрок {v0} входит в игру...",
|
||||
"@log_analyzer_player_login": {},
|
||||
|
||||
@ -772,7 +772,7 @@
|
||||
"tools_rsi_launcher_enhance_title": "RSI 启动器增强",
|
||||
"tools_rsi_launcher_enhance_msg_version": "启动器内部版本信息:{v0}",
|
||||
"tools_rsi_launcher_enhance_msg_patch_status": "补丁状态:{v0}",
|
||||
"tools_rsi_launcher_enhance_msg_error": "获取增强数据失败,可能是网络问题或当前版本不支持",
|
||||
"tools_rsi_launcher_enhance_msg_error": "当前版本暂不支持,請等待適配...",
|
||||
"tools_rsi_launcher_enhance_title_localization": "RSI 启动器本地化",
|
||||
"tools_rsi_launcher_enhance_subtitle_localization": "为 RSI 启动器增加多语言支持。",
|
||||
"tools_rsi_launcher_enhance_title_download_booster": "RSI 启动器下载增强",
|
||||
@ -890,7 +890,7 @@
|
||||
"log_analyzer_soft_death": "软死亡",
|
||||
"log_analyzer_disintegration": "解体",
|
||||
"log_analyzer_vehicle_damage_details": "载具型号:{v0} \n区域:{v1} \n损毁等级:{v2} ({v3}) 责任方:{v4}",
|
||||
"log_analyzer_death_details": "受害者ID:{v0} 死因:{v1} \n击杀者ID:{v2} \n区域:{v3}",
|
||||
"log_analyzer_death_details": "受害者ID:{v0} \n位置:{v2} \n区域:{v3}",
|
||||
"log_analyzer_player_login": "玩家 {v0} 登录 ...",
|
||||
"log_analyzer_view_local_inventory": "查看本地库存",
|
||||
"log_analyzer_player_location": "玩家ID:{v0} 位置:{v1}",
|
||||
|
||||
@ -907,7 +907,7 @@
|
||||
"@tools_rsi_launcher_enhance_msg_version": {},
|
||||
"tools_rsi_launcher_enhance_msg_patch_status": "補丁狀態:{v0}",
|
||||
"@tools_rsi_launcher_enhance_msg_patch_status": {},
|
||||
"tools_rsi_launcher_enhance_msg_error": "增強資料取得失敗,可能是網路問題或目前版本不支援",
|
||||
"tools_rsi_launcher_enhance_msg_error": "目前版本不支援",
|
||||
"@tools_rsi_launcher_enhance_msg_error": {},
|
||||
"tools_rsi_launcher_enhance_title_localization": "RSI 啟動器本地化",
|
||||
"@tools_rsi_launcher_enhance_title_localization": {},
|
||||
@ -1141,7 +1141,7 @@
|
||||
"@log_analyzer_disintegration": {},
|
||||
"log_analyzer_vehicle_damage_details": "載具型號:{v0} \n區域:{v1} \n損毀等級:{v2} ({v3}) 責任方:{v4}",
|
||||
"@log_analyzer_vehicle_damage_details": {},
|
||||
"log_analyzer_death_details": "受害者ID:{v0} 死因:{v1} \n擊殺者ID:{v2} \n區域:{v3}",
|
||||
"log_analyzer_death_details": "受害者ID:{v0} \n位置:{v2} \n區域:{v3}",
|
||||
"@log_analyzer_death_details": {},
|
||||
"log_analyzer_player_login": "玩家 {v0} 登入 ...",
|
||||
"@log_analyzer_player_login": {},
|
||||
|
||||
@ -75,7 +75,11 @@ class PartyRoom extends _$PartyRoom {
|
||||
Box? _confBox;
|
||||
StreamSubscription<partroom.RoomEvent>? _eventStreamSubscription;
|
||||
Timer? _heartbeatTimer;
|
||||
Timer? _reconnectTimer;
|
||||
bool _disposed = false;
|
||||
int _reconnectAttempts = 0;
|
||||
static const int _maxReconnectAttempts = 5;
|
||||
static const Duration _reconnectDelay = Duration(seconds: 3);
|
||||
|
||||
@override
|
||||
PartyRoomFullState build() {
|
||||
@ -398,6 +402,7 @@ class PartyRoom extends _$PartyRoom {
|
||||
await _stopHeartbeat();
|
||||
await _stopEventStream();
|
||||
|
||||
_reconnectAttempts = 0;
|
||||
_dismissRoom();
|
||||
|
||||
dPrint('[PartyRoom] Left room: $roomUuid');
|
||||
@ -421,6 +426,7 @@ class PartyRoom extends _$PartyRoom {
|
||||
await _stopHeartbeat();
|
||||
await _stopEventStream();
|
||||
|
||||
_reconnectAttempts = 0;
|
||||
_dismissRoom();
|
||||
|
||||
dPrint('[PartyRoom] Dismissed room: $roomUuid');
|
||||
@ -754,24 +760,106 @@ class PartyRoom extends _$PartyRoom {
|
||||
|
||||
_eventStreamSubscription = stream.listen(
|
||||
(event) {
|
||||
// 重置重连计数器,因为连接正常
|
||||
_reconnectAttempts = 0;
|
||||
_handleRoomEvent(event);
|
||||
},
|
||||
onError: (error) {
|
||||
dPrint('[PartyRoom] Event stream error: $error');
|
||||
// 发生错误时尝试重连
|
||||
_scheduleReconnect(roomUuid);
|
||||
},
|
||||
onDone: () {
|
||||
dPrint('[PartyRoom] Event stream closed');
|
||||
// 流关闭时尝试重连
|
||||
_scheduleReconnect(roomUuid);
|
||||
},
|
||||
);
|
||||
|
||||
dPrint('[PartyRoom] Event stream started');
|
||||
// 成功启动,重置重连计数
|
||||
_reconnectAttempts = 0;
|
||||
} catch (e) {
|
||||
dPrint('[PartyRoom] StartEventStream error: $e');
|
||||
// 启动失败时尝试重连
|
||||
_scheduleReconnect(roomUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 调度重连
|
||||
void _scheduleReconnect(String roomUuid) {
|
||||
// 如果已经销毁或不在房间内,不重连
|
||||
if (_disposed || state.room.roomUuid == null || state.room.roomUuid != roomUuid) {
|
||||
dPrint('[PartyRoom] Skip reconnect: disposed=$_disposed, roomUuid=${state.room.roomUuid}');
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果已经有重连任务在进行,不重复调度
|
||||
if (_reconnectTimer?.isActive ?? false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查重连次数
|
||||
if (_reconnectAttempts >= _maxReconnectAttempts) {
|
||||
dPrint('[PartyRoom] Max reconnect attempts reached ($_maxReconnectAttempts)');
|
||||
return;
|
||||
}
|
||||
|
||||
_reconnectAttempts++;
|
||||
dPrint(
|
||||
'[PartyRoom] Scheduling reconnect attempt $_reconnectAttempts/$_maxReconnectAttempts in ${_reconnectDelay.inSeconds}s',
|
||||
);
|
||||
|
||||
_reconnectTimer = Timer(_reconnectDelay, () async {
|
||||
await _attemptReconnect(roomUuid);
|
||||
});
|
||||
}
|
||||
|
||||
/// 尝试重连
|
||||
Future<void> _attemptReconnect(String roomUuid) async {
|
||||
if (_disposed || state.room.roomUuid == null || state.room.roomUuid != roomUuid) {
|
||||
dPrint('[PartyRoom] Abort reconnect: no longer in room');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
dPrint('[PartyRoom] Attempting to reconnect event stream...');
|
||||
|
||||
// 先检查自己是否还在房间内
|
||||
final client = state.client.roomClient;
|
||||
if (client == null) {
|
||||
dPrint('[PartyRoom] Reconnect failed: client not available');
|
||||
_scheduleReconnect(roomUuid);
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用 getMyRoom 检查是否还在房间内
|
||||
final response = await client.getMyRoom(partroom.GetMyRoomRequest(), options: _getAuthCallOptions());
|
||||
|
||||
if (!response.hasRoom() || response.room.roomUuid != roomUuid) {
|
||||
dPrint('[PartyRoom] Reconnect failed: no longer in room');
|
||||
// 不在房间内,清理状态
|
||||
await _stopHeartbeat();
|
||||
await _stopEventStream();
|
||||
_reconnectAttempts = 0;
|
||||
_dismissRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认还在房间内,重新启动事件流
|
||||
dPrint('[PartyRoom] Still in room, restarting event stream');
|
||||
await _startEventStream(roomUuid);
|
||||
} catch (e) {
|
||||
dPrint('[PartyRoom] Reconnect attempt failed: $e');
|
||||
// 重连失败,继续调度下一次重连
|
||||
_scheduleReconnect(roomUuid);
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止事件流监听
|
||||
Future<void> _stopEventStream() async {
|
||||
_reconnectTimer?.cancel();
|
||||
_reconnectTimer = null;
|
||||
await _eventStreamSubscription?.cancel();
|
||||
_eventStreamSubscription = null;
|
||||
dPrint('[PartyRoom] Event stream stopped');
|
||||
@ -794,6 +882,21 @@ class PartyRoom extends _$PartyRoom {
|
||||
case partroom.RoomEventType.MEMBER_JOINED:
|
||||
case partroom.RoomEventType.MEMBER_LEFT:
|
||||
case partroom.RoomEventType.MEMBER_KICKED:
|
||||
if (event.type == partroom.RoomEventType.MEMBER_KICKED) {
|
||||
// 判断被踢的是不是自己
|
||||
if (event.member.gameUserId == state.auth.userInfo?.gameUserId) {
|
||||
// 离开房间
|
||||
_dismissRoom();
|
||||
return;
|
||||
}
|
||||
}
|
||||
//
|
||||
if (event.type == partroom.RoomEventType.MEMBER_LEFT) {
|
||||
if (event.member.gameUserId == state.auth.userInfo?.gameUserId) {
|
||||
// 判断离开的是不是自己
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 刷新成员列表
|
||||
if (state.room.roomUuid != null) {
|
||||
getRoomMembers(state.room.roomUuid!);
|
||||
|
||||
@ -44,7 +44,7 @@ final class PartyRoomProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$partyRoomHash() => r'02cdd156995799411eb47107d5c197f43e78629e';
|
||||
String _$partyRoomHash() => r'd7182854c8caf5bb362c45a4e6e2ab40c2ef1b09';
|
||||
|
||||
/// PartyRoom Provider
|
||||
|
||||
|
||||
@ -105,6 +105,12 @@ class PartyRoomUIModel extends _$PartyRoomUIModel {
|
||||
playTime: currentGameStartTime != gameStartTime ? gameStartTime : null,
|
||||
);
|
||||
}
|
||||
|
||||
if (next.deathEvents?.isNotEmpty ?? false) {
|
||||
for (final event in next.deathEvents!) {
|
||||
ref.read(partyRoomProvider.notifier).sendSignal("special_death", params: {"location": event.$1, "area": event.$2});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 处理连接状态变化
|
||||
|
||||
@ -41,7 +41,7 @@ final class PartyRoomUIModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$partyRoomUIModelHash() => r'a0b6c3632ff33f2d58882f9bc1ab58c69c2487f4';
|
||||
String _$partyRoomUIModelHash() => r'add4703c9129465718a7850ea09025aa1ff35358';
|
||||
|
||||
abstract class _$PartyRoomUIModel extends $Notifier<PartyRoomUIState> {
|
||||
PartyRoomUIState build();
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
@ -18,8 +19,7 @@ sealed class PartyRoomGameLogTrackerProviderState with _$PartyRoomGameLogTracker
|
||||
@Default(0) int kills,
|
||||
@Default(0) int deaths,
|
||||
DateTime? gameStartTime,
|
||||
@Default([]) List<String> killedIds, // 本次迭代新增的击杀ID
|
||||
@Default([]) List<String> deathIds, // 本次迭代新增的死亡ID
|
||||
List<(String, String)>? deathEvents,
|
||||
}) = _PartyRoomGameLogTrackerProviderState;
|
||||
}
|
||||
|
||||
@ -32,6 +32,7 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
|
||||
|
||||
@override
|
||||
PartyRoomGameLogTrackerProviderState build({required DateTime startTime}) {
|
||||
startTime = DateTime.now();
|
||||
dPrint("[PartyRoomGameLogTrackerProvider] init $startTime");
|
||||
ref.onDispose(() {
|
||||
_disposed = true;
|
||||
@ -65,16 +66,9 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
|
||||
}
|
||||
} catch (e) {
|
||||
// 游戏未启动或发生错误
|
||||
state = state.copyWith(
|
||||
location: '<游戏未启动>',
|
||||
gameStartTime: null,
|
||||
kills: 0,
|
||||
deaths: 0,
|
||||
killedIds: [],
|
||||
deathIds: [],
|
||||
);
|
||||
state = state.copyWith(location: '<游戏未启动>', gameStartTime: null, kills: 0, deaths: 0);
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 5));
|
||||
await Future.delayed(const Duration(seconds: 10));
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,9 +84,7 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
|
||||
// 从统计数据中直接获取最新位置(全量查找的结果)
|
||||
final location = statistics.latestLocation == null ? '<主菜单>' : '[${statistics.latestLocation}]';
|
||||
|
||||
// 计算基于 _lastQueryTime 之后的增量 ID
|
||||
final newKilledIds = <String>[];
|
||||
final newDeathIds = <String>[];
|
||||
List<(String, String)> deathEvents = [];
|
||||
|
||||
if (_lastQueryTime != null) {
|
||||
// 遍历所有 actor_death 事件
|
||||
@ -125,24 +117,15 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
|
||||
}
|
||||
}
|
||||
|
||||
// 使用格式化字段,不再重新解析
|
||||
final victimId = data.victimId;
|
||||
final killerId = data.killerId;
|
||||
|
||||
if (victimId != null && killerId != null && victimId != killerId) {
|
||||
// 如果玩家是击杀者,记录被击杀的ID
|
||||
if (killerId == statistics.playerName) {
|
||||
newKilledIds.add(victimId);
|
||||
}
|
||||
|
||||
// 如果玩家是受害者,记录击杀者ID
|
||||
if (victimId == statistics.playerName) {
|
||||
newDeathIds.add(killerId);
|
||||
}
|
||||
if (data.playerName == data.victimId) {
|
||||
// 玩家死亡,增加记录
|
||||
deathEvents.add((data.location ?? "-", data.area ?? "-"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// debugPrint(
|
||||
// "[PartyRoomGameLogTrackerProvider] location $location killCount ${statistics.killCount} deathCount ${statistics.deathCount}",
|
||||
// );
|
||||
// 更新状态,只存储本次迭代的增量数据
|
||||
state = state.copyWith(
|
||||
location: location,
|
||||
@ -151,10 +134,7 @@ class PartyRoomGameLogTrackerProvider extends _$PartyRoomGameLogTrackerProvider
|
||||
deaths: statistics.deathCount,
|
||||
// 从 startTime 开始的总计数
|
||||
gameStartTime: statistics.gameStartTime,
|
||||
// 全量查找的游戏开始时间
|
||||
killedIds: newKilledIds,
|
||||
// 只存储本次迭代的增量
|
||||
deathIds: newDeathIds, // 只存储本次迭代的增量
|
||||
deathEvents: deathEvents,
|
||||
);
|
||||
|
||||
// 更新查询时间为本次查询的时刻
|
||||
|
||||
@ -12,10 +12,9 @@ part of 'game_log_tracker_provider.dart';
|
||||
// dart format off
|
||||
T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$PartyRoomGameLogTrackerProviderState {
|
||||
mixin _$PartyRoomGameLogTrackerProviderState implements DiagnosticableTreeMixin {
|
||||
|
||||
String get location; int get kills; int get deaths; DateTime? get gameStartTime; List<String> get killedIds;// 本次迭代新增的击杀ID
|
||||
List<String> get deathIds;
|
||||
String get location; int get kills; int get deaths; DateTime? get gameStartTime; List<(String, String)>? get deathEvents;
|
||||
/// Create a copy of PartyRoomGameLogTrackerProviderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -23,19 +22,25 @@ mixin _$PartyRoomGameLogTrackerProviderState {
|
||||
$PartyRoomGameLogTrackerProviderStateCopyWith<PartyRoomGameLogTrackerProviderState> get copyWith => _$PartyRoomGameLogTrackerProviderStateCopyWithImpl<PartyRoomGameLogTrackerProviderState>(this as PartyRoomGameLogTrackerProviderState, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'PartyRoomGameLogTrackerProviderState'))
|
||||
..add(DiagnosticsProperty('location', location))..add(DiagnosticsProperty('kills', kills))..add(DiagnosticsProperty('deaths', deaths))..add(DiagnosticsProperty('gameStartTime', gameStartTime))..add(DiagnosticsProperty('deathEvents', deathEvents));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PartyRoomGameLogTrackerProviderState&&(identical(other.location, location) || other.location == location)&&(identical(other.kills, kills) || other.kills == kills)&&(identical(other.deaths, deaths) || other.deaths == deaths)&&(identical(other.gameStartTime, gameStartTime) || other.gameStartTime == gameStartTime)&&const DeepCollectionEquality().equals(other.killedIds, killedIds)&&const DeepCollectionEquality().equals(other.deathIds, deathIds));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is PartyRoomGameLogTrackerProviderState&&(identical(other.location, location) || other.location == location)&&(identical(other.kills, kills) || other.kills == kills)&&(identical(other.deaths, deaths) || other.deaths == deaths)&&(identical(other.gameStartTime, gameStartTime) || other.gameStartTime == gameStartTime)&&const DeepCollectionEquality().equals(other.deathEvents, deathEvents));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,location,kills,deaths,gameStartTime,const DeepCollectionEquality().hash(killedIds),const DeepCollectionEquality().hash(deathIds));
|
||||
int get hashCode => Object.hash(runtimeType,location,kills,deaths,gameStartTime,const DeepCollectionEquality().hash(deathEvents));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PartyRoomGameLogTrackerProviderState(location: $location, kills: $kills, deaths: $deaths, gameStartTime: $gameStartTime, killedIds: $killedIds, deathIds: $deathIds)';
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'PartyRoomGameLogTrackerProviderState(location: $location, kills: $kills, deaths: $deaths, gameStartTime: $gameStartTime, deathEvents: $deathEvents)';
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +51,7 @@ abstract mixin class $PartyRoomGameLogTrackerProviderStateCopyWith<$Res> {
|
||||
factory $PartyRoomGameLogTrackerProviderStateCopyWith(PartyRoomGameLogTrackerProviderState value, $Res Function(PartyRoomGameLogTrackerProviderState) _then) = _$PartyRoomGameLogTrackerProviderStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String location, int kills, int deaths, DateTime? gameStartTime, List<String> killedIds, List<String> deathIds
|
||||
String location, int kills, int deaths, DateTime? gameStartTime, List<(String, String)>? deathEvents
|
||||
});
|
||||
|
||||
|
||||
@ -63,15 +68,14 @@ class _$PartyRoomGameLogTrackerProviderStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of PartyRoomGameLogTrackerProviderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? location = null,Object? kills = null,Object? deaths = null,Object? gameStartTime = freezed,Object? killedIds = null,Object? deathIds = null,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? location = null,Object? kills = null,Object? deaths = null,Object? gameStartTime = freezed,Object? deathEvents = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||
as String,kills: null == kills ? _self.kills : kills // ignore: cast_nullable_to_non_nullable
|
||||
as int,deaths: null == deaths ? _self.deaths : deaths // ignore: cast_nullable_to_non_nullable
|
||||
as int,gameStartTime: freezed == gameStartTime ? _self.gameStartTime : gameStartTime // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,killedIds: null == killedIds ? _self.killedIds : killedIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,deathIds: null == deathIds ? _self.deathIds : deathIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
as DateTime?,deathEvents: freezed == deathEvents ? _self.deathEvents : deathEvents // ignore: cast_nullable_to_non_nullable
|
||||
as List<(String, String)>?,
|
||||
));
|
||||
}
|
||||
|
||||
@ -153,10 +157,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String location, int kills, int deaths, DateTime? gameStartTime, List<String> killedIds, List<String> deathIds)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String location, int kills, int deaths, DateTime? gameStartTime, List<(String, String)>? deathEvents)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PartyRoomGameLogTrackerProviderState() when $default != null:
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.killedIds,_that.deathIds);case _:
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.deathEvents);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -174,10 +178,10 @@ return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_tha
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String location, int kills, int deaths, DateTime? gameStartTime, List<String> killedIds, List<String> deathIds) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String location, int kills, int deaths, DateTime? gameStartTime, List<(String, String)>? deathEvents) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PartyRoomGameLogTrackerProviderState():
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.killedIds,_that.deathIds);}
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.deathEvents);}
|
||||
}
|
||||
/// A variant of `when` that fallback to returning `null`
|
||||
///
|
||||
@ -191,10 +195,10 @@ return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_tha
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String location, int kills, int deaths, DateTime? gameStartTime, List<String> killedIds, List<String> deathIds)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String location, int kills, int deaths, DateTime? gameStartTime, List<(String, String)>? deathEvents)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _PartyRoomGameLogTrackerProviderState() when $default != null:
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.killedIds,_that.deathIds);case _:
|
||||
return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_that.deathEvents);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -205,28 +209,21 @@ return $default(_that.location,_that.kills,_that.deaths,_that.gameStartTime,_tha
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _PartyRoomGameLogTrackerProviderState implements PartyRoomGameLogTrackerProviderState {
|
||||
const _PartyRoomGameLogTrackerProviderState({this.location = '', this.kills = 0, this.deaths = 0, this.gameStartTime, final List<String> killedIds = const [], final List<String> deathIds = const []}): _killedIds = killedIds,_deathIds = deathIds;
|
||||
class _PartyRoomGameLogTrackerProviderState with DiagnosticableTreeMixin implements PartyRoomGameLogTrackerProviderState {
|
||||
const _PartyRoomGameLogTrackerProviderState({this.location = '', this.kills = 0, this.deaths = 0, this.gameStartTime, final List<(String, String)>? deathEvents}): _deathEvents = deathEvents;
|
||||
|
||||
|
||||
@override@JsonKey() final String location;
|
||||
@override@JsonKey() final int kills;
|
||||
@override@JsonKey() final int deaths;
|
||||
@override final DateTime? gameStartTime;
|
||||
final List<String> _killedIds;
|
||||
@override@JsonKey() List<String> get killedIds {
|
||||
if (_killedIds is EqualUnmodifiableListView) return _killedIds;
|
||||
final List<(String, String)>? _deathEvents;
|
||||
@override List<(String, String)>? get deathEvents {
|
||||
final value = _deathEvents;
|
||||
if (value == null) return null;
|
||||
if (_deathEvents is EqualUnmodifiableListView) return _deathEvents;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_killedIds);
|
||||
}
|
||||
|
||||
// 本次迭代新增的击杀ID
|
||||
final List<String> _deathIds;
|
||||
// 本次迭代新增的击杀ID
|
||||
@override@JsonKey() List<String> get deathIds {
|
||||
if (_deathIds is EqualUnmodifiableListView) return _deathIds;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_deathIds);
|
||||
return EqualUnmodifiableListView(value);
|
||||
}
|
||||
|
||||
|
||||
@ -237,19 +234,25 @@ class _PartyRoomGameLogTrackerProviderState implements PartyRoomGameLogTrackerPr
|
||||
_$PartyRoomGameLogTrackerProviderStateCopyWith<_PartyRoomGameLogTrackerProviderState> get copyWith => __$PartyRoomGameLogTrackerProviderStateCopyWithImpl<_PartyRoomGameLogTrackerProviderState>(this, _$identity);
|
||||
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
properties
|
||||
..add(DiagnosticsProperty('type', 'PartyRoomGameLogTrackerProviderState'))
|
||||
..add(DiagnosticsProperty('location', location))..add(DiagnosticsProperty('kills', kills))..add(DiagnosticsProperty('deaths', deaths))..add(DiagnosticsProperty('gameStartTime', gameStartTime))..add(DiagnosticsProperty('deathEvents', deathEvents));
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PartyRoomGameLogTrackerProviderState&&(identical(other.location, location) || other.location == location)&&(identical(other.kills, kills) || other.kills == kills)&&(identical(other.deaths, deaths) || other.deaths == deaths)&&(identical(other.gameStartTime, gameStartTime) || other.gameStartTime == gameStartTime)&&const DeepCollectionEquality().equals(other._killedIds, _killedIds)&&const DeepCollectionEquality().equals(other._deathIds, _deathIds));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PartyRoomGameLogTrackerProviderState&&(identical(other.location, location) || other.location == location)&&(identical(other.kills, kills) || other.kills == kills)&&(identical(other.deaths, deaths) || other.deaths == deaths)&&(identical(other.gameStartTime, gameStartTime) || other.gameStartTime == gameStartTime)&&const DeepCollectionEquality().equals(other._deathEvents, _deathEvents));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,location,kills,deaths,gameStartTime,const DeepCollectionEquality().hash(_killedIds),const DeepCollectionEquality().hash(_deathIds));
|
||||
int get hashCode => Object.hash(runtimeType,location,kills,deaths,gameStartTime,const DeepCollectionEquality().hash(_deathEvents));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'PartyRoomGameLogTrackerProviderState(location: $location, kills: $kills, deaths: $deaths, gameStartTime: $gameStartTime, killedIds: $killedIds, deathIds: $deathIds)';
|
||||
String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) {
|
||||
return 'PartyRoomGameLogTrackerProviderState(location: $location, kills: $kills, deaths: $deaths, gameStartTime: $gameStartTime, deathEvents: $deathEvents)';
|
||||
}
|
||||
|
||||
|
||||
@ -260,7 +263,7 @@ abstract mixin class _$PartyRoomGameLogTrackerProviderStateCopyWith<$Res> implem
|
||||
factory _$PartyRoomGameLogTrackerProviderStateCopyWith(_PartyRoomGameLogTrackerProviderState value, $Res Function(_PartyRoomGameLogTrackerProviderState) _then) = __$PartyRoomGameLogTrackerProviderStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String location, int kills, int deaths, DateTime? gameStartTime, List<String> killedIds, List<String> deathIds
|
||||
String location, int kills, int deaths, DateTime? gameStartTime, List<(String, String)>? deathEvents
|
||||
});
|
||||
|
||||
|
||||
@ -277,15 +280,14 @@ class __$PartyRoomGameLogTrackerProviderStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of PartyRoomGameLogTrackerProviderState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? location = null,Object? kills = null,Object? deaths = null,Object? gameStartTime = freezed,Object? killedIds = null,Object? deathIds = null,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? location = null,Object? kills = null,Object? deaths = null,Object? gameStartTime = freezed,Object? deathEvents = freezed,}) {
|
||||
return _then(_PartyRoomGameLogTrackerProviderState(
|
||||
location: null == location ? _self.location : location // ignore: cast_nullable_to_non_nullable
|
||||
as String,kills: null == kills ? _self.kills : kills // ignore: cast_nullable_to_non_nullable
|
||||
as int,deaths: null == deaths ? _self.deaths : deaths // ignore: cast_nullable_to_non_nullable
|
||||
as int,gameStartTime: freezed == gameStartTime ? _self.gameStartTime : gameStartTime // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,killedIds: null == killedIds ? _self._killedIds : killedIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,deathIds: null == deathIds ? _self._deathIds : deathIds // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,
|
||||
as DateTime?,deathEvents: freezed == deathEvents ? _self._deathEvents : deathEvents // ignore: cast_nullable_to_non_nullable
|
||||
as List<(String, String)>?,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -66,7 +66,7 @@ final class PartyRoomGameLogTrackerProviderProvider
|
||||
}
|
||||
|
||||
String _$partyRoomGameLogTrackerProviderHash() =>
|
||||
r'ecb015eb46d25bfe11bbb153242fd5c4f20ef367';
|
||||
r'3e1560b2fffc5461a41bece57b43e27f4112ad0c';
|
||||
|
||||
final class PartyRoomGameLogTrackerProviderFamily extends $Family
|
||||
with
|
||||
|
||||
@ -97,15 +97,12 @@ class PartyRoomMemberItem extends ConsumerWidget {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
member.status.currentLocation.isNotEmpty ? member.status.currentLocation : '...',
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .9)),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
SizedBox(width: 4),
|
||||
Text(
|
||||
"K: ${member.status.kills} D: ${member.status.deaths}",
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .6)),
|
||||
Expanded(
|
||||
child: Text(
|
||||
member.status.currentLocation.isNotEmpty ? member.status.currentLocation : '...',
|
||||
style: TextStyle(fontSize: 12, color: Colors.white.withValues(alpha: .9)),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@ -210,6 +210,11 @@ class _MessageItem extends ConsumerWidget {
|
||||
final userName = _getEventUserName(roomEvent);
|
||||
final avatarUrl = _getEventAvatarUrl(roomEvent);
|
||||
|
||||
// 检查是否是死亡信号,显示特殊卡片
|
||||
if (isSignal && roomEvent.signalId == 'special_death') {
|
||||
return _buildDeathMessageCard(context, roomEvent, userName, avatarUrl);
|
||||
}
|
||||
|
||||
final text = _getEventText(roomEvent, ref);
|
||||
if (text == null) return const SizedBox.shrink();
|
||||
|
||||
@ -259,6 +264,104 @@ class _MessageItem extends ConsumerWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeathMessageCard(
|
||||
BuildContext context,
|
||||
partroom.RoomEvent roomEvent,
|
||||
String userName,
|
||||
String? avatarUrl,
|
||||
) {
|
||||
final location = roomEvent.signalParams['location'] ?? '未知位置';
|
||||
final area = roomEvent.signalParams['area'] ?? '未知区域';
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildUserAvatar(userName, avatarUrl: avatarUrl, size: 28),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 用户名和时间
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
userName,
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600, color: Colors.white),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
_formatTime(roomEvent.timestamp),
|
||||
style: const TextStyle(fontSize: 11, color: Color(0xFF80848E)),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
// 死亡卡片
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF2B2D31),
|
||||
border: Border.all(color: const Color(0xFFED4245).withValues(alpha: 0.3)),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 标题
|
||||
Row(
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFFED4245).withValues(alpha: 0.2),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(FluentIcons.status_error_full, size: 14, color: Color(0xFFED4245)),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
const Text(
|
||||
'玩家死亡',
|
||||
style: TextStyle(fontSize: 14, color: Color(0xFFED4245), fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// 位置信息
|
||||
_buildInfoRow(icon: FluentIcons.location, label: '位置', value: location),
|
||||
const SizedBox(height: 8),
|
||||
// 区域信息
|
||||
_buildInfoRow(icon: FluentIcons.map_pin, label: '区域', value: area),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoRow({required IconData icon, required String label, required String value}) {
|
||||
return Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Icon(icon, size: 14, color: Colors.white.withValues(alpha: .4)),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'$label: ',
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .4), fontWeight: FontWeight.w500),
|
||||
),
|
||||
Expanded(
|
||||
child: Text(value, style: const TextStyle(fontSize: 13, color: Color(0xFFDBDEE1))),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
String _getEventUserName(partroom.RoomEvent event) {
|
||||
switch (event.type) {
|
||||
case partroom.RoomEventType.SIGNAL_BROADCAST:
|
||||
|
||||
@ -123,7 +123,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
|
||||
? const Color(0xFF4CAF50)
|
||||
: isActive
|
||||
? const Color(0xFF4A9EFF)
|
||||
: Colors.grey.withValues(alpha: 0.3),
|
||||
: Colors.white.withValues(alpha: 0.4),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Center(
|
||||
@ -143,7 +143,7 @@ class PartyRoomRegisterPage extends HookConsumerWidget {
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
color: isActive ? const Color(0xFF4A9EFF) : Colors.grey.withValues(alpha: 0.7),
|
||||
color: isActive ? const Color(0xFF4A9EFF) : Colors.white.withValues(alpha: 0.4),
|
||||
fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
|
||||
),
|
||||
),
|
||||
|
||||
Loading…
Reference in New Issue
Block a user