增加大量保护

This commit is contained in:
wuwenbo 2026-05-23 18:51:24 +08:00
parent c0517acf7e
commit 096f6e4010
8 changed files with 506 additions and 173 deletions

View File

@ -347,6 +347,18 @@ namespace RuntimeData
return map.GetGridDataByUnitId(Id, out grid);
}
public bool IsValidOnMap(MapData map, GridData expectedGrid = null)
{
if (map?.UnitMap == null) return false;
if (!map.UnitMap.GetUnitDataByUnitId(Id, out var currentUnit)) return false;
if (!ReferenceEquals(currentUnit, this)) return false;
if (!map.GetPlayerDataByUnitId(Id, out _)) return false;
if (expectedGrid == null) return true;
return map.GetGridDataByUnitId(Id, out var currentGrid)
&& currentGrid != null
&& currentGrid.Id == expectedGrid.Id;
}
public UnitRenderer Renderer(MapData map)
{
if (!map.IsCurrentShowMap()) return null;
@ -1472,13 +1484,17 @@ namespace RuntimeData
// 移动后
public void OnMove(MapData map, GridData target, MoveType moveType, List<Vector2Int> path = null)
{
if (map == null || target == null || !IsValidOnMap(map, target)) return;
var isFrozen = IsFrozen();
var copy = new List<SkillBase>(Skills);
foreach (var skill in copy)
{
if (!IsValidOnMap(map, target)) return;
if (isFrozen && IsSkillFrozenFilter(skill)) continue;
skill.OnMove(this, target, map,moveType, path);
}
if (!IsValidOnMap(map, target)) return;
//赋予格子特殊效果,scarletEmpire的单位走到格子上时会得到被赋予一个技能realTimeVampire
// 注意:多人允许相同 Empire 后,多名 PlayerCivId==0 玩家会同时享有此特权,符合预期保留逻辑。
if(target.HasSpType(GridSpType.RemiliaGrid) && target.RealUnit(map,out var unit) && unit.Player(map,out var player) && player.PlayerCivId == 0)
@ -1528,10 +1544,14 @@ namespace RuntimeData
public void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target,out int addDmg)
{
addDmg = 0;
if (mapData == null || origin == null || target == null) return;
if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData)) return;
var isFrozen = IsFrozen();
var copy = new List<SkillBase>(Skills);
foreach (var skill in copy)
{
if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData)) return;
if (isFrozen && IsSkillFrozenFilter(skill)) continue;
skill.BeforeActiveAttackOther(mapData, origin, target, out var tmpAddDmg);
addDmg += tmpAddDmg;

View File

@ -66,7 +66,7 @@ namespace TH1_Logic.Oss
// 检查缓存的凭证是否有效提前60秒刷新
if (_cachedCredentials == null || DateTime.UtcNow >= _credentialsExpireTime.AddSeconds(-60))
{
var authTicket = GetAuthTicket();
if (!TryGetAuthTicket(out var authTicket)) return false;
_cachedCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "ossdata");
_credentialsExpireTime = DateTime.UtcNow.AddSeconds(_cachedCredentials.expiresIn);
LogSystem.LogInfo($"STS token obtained, expires at {_credentialsExpireTime}");
@ -104,7 +104,7 @@ namespace TH1_Logic.Oss
// 检查缓存的凭证是否有效提前60秒刷新
if (_cachedCollectCredentials == null || DateTime.UtcNow >= _collectCredentialsExpireTime.AddSeconds(-60))
{
var authTicket = GetAuthTicket();
if (!TryGetAuthTicket(out var authTicket)) return false;
_cachedCollectCredentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "collectdata");
_collectCredentialsExpireTime = DateTime.UtcNow.AddSeconds(_cachedCollectCredentials.expiresIn);
LogSystem.LogInfo($"Collect STS token obtained, expires at {_collectCredentialsExpireTime}");
@ -144,7 +144,7 @@ namespace TH1_Logic.Oss
return (false, null);
}
var authTicket = GetAuthTicket();
if (!TryGetAuthTicket(out var authTicket)) return (false, null);
var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "bugreport", version);
var result = await _uploadService.UploadFileAsync(credentials, packageData, "application/zip",
MaxBugReportUploadBytes);
@ -181,7 +181,7 @@ namespace TH1_Logic.Oss
return (false, null);
}
var authTicket = GetAuthTicket();
if (!TryGetAuthTicket(out var authTicket)) return (false, null);
var credentials = await _stsService.RequestStsTokenAsync(steamId, authTicket, "multilingualreport",
version);
var result = await _uploadService.UploadFileAsync(credentials, packageData, "application/zip",
@ -220,7 +220,7 @@ namespace TH1_Logic.Oss
_isSteamAuthWarmupRunning = true;
try
{
var authTicket = GetAuthTicket();
if (!TryGetAuthTicket(out var authTicket)) return false;
var response = await _stsService.WarmupSteamAuthAsync(steamId, authTicket);
var refreshSeconds = response != null && response.expiresIn > 90
? Math.Min(SteamAuthWarmupRefreshSeconds, response.expiresIn - 60)
@ -260,15 +260,28 @@ namespace TH1_Logic.Oss
}
}
private static string GetAuthTicket()
private static bool TryGetAuthTicket(out string authTicket)
{
var ticket = new byte[1024];
var identity = new SteamNetworkingIdentity();
SteamUser.GetAuthSessionTicket(ticket, 1024, out var ticketSize, ref identity);
if (ticketSize == 0)
throw new Exception("Steam auth ticket is empty");
authTicket = null;
try
{
var ticket = new byte[1024];
var identity = new SteamNetworkingIdentity();
SteamUser.GetAuthSessionTicket(ticket, 1024, out var ticketSize, ref identity);
if (ticketSize == 0)
{
LogSystem.LogWarning("Steam auth ticket is empty");
return false;
}
return BitConverter.ToString(ticket, 0, (int)ticketSize).Replace("-", "").ToLower();
authTicket = BitConverter.ToString(ticket, 0, (int)ticketSize).Replace("-", "").ToLower();
return true;
}
catch (Exception ex)
{
LogSystem.LogWarning($"Steam auth ticket unavailable: {ex.Message}");
return false;
}
}
}
}

View File

@ -30,11 +30,13 @@ namespace Logic.Skill
public override void BeforeActiveAttackOther(MapData mapData, UnitData origin, UnitData target, out int addDmg)
{
addDmg = 0;
if (origin == null || target == null) return;
if (mapData == null || origin == null || target == null) return;
if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData)) return;
if (!mapData.GetGridDataByUnitId(target.Id, out var targetGrid)) return;
var count = 0;
foreach (var unit in mapData.UnitMap.UnitList)
{
if (unit == null || !unit.IsValidOnMap(mapData)) continue;
if (unit == origin || unit == target) continue;
// 仅本体的我方/在盟盟友可参与协同;刚背盟的不算,敌方幻象更不算
if (!mapData.IsLeagueUnitByUnit(unit.Id, origin.Id)) continue;
@ -49,6 +51,7 @@ namespace Logic.Skill
MapRenderer.Instance.ProjectileManager.CreateProjectile(unitPosition, targetPosition, ProjectileType.ReisenAttack);
count++;
}
if (!origin.IsValidOnMap(mapData) || !target.IsValidOnMap(mapData, targetGrid)) return;
target.AddSkill_Legacy(SkillType.KAGUYAFRENCHSYNERGYDEBUFF, mapData,false,0,true,count,true,SpecialAddSkillType.AddLevel,origin.Id);
//target.GetSkill(SkillType.KAGUYAFRENCHSYNERGYDEBUFF, out var debuffSkill);
//debuffSkill.AddLevel(mapData, origin, target, count);

View File

@ -50,6 +50,9 @@ namespace Logic.Skill
{
if (healType != HealType.AttackAllyHeal) return;
if (mapData == null || origin == null || target == null) return;
var originId = origin.Id;
var targetId = target.Id;
if (!TryGetLiveUnit(mapData, originId, out origin) || !TryGetLiveUnit(mapData, targetId, out target)) return;
//处理三连发
if (origin.Skills != null && origin.GetSkill(SkillType.SANAENINE, out var _))
@ -57,6 +60,7 @@ namespace Logic.Skill
bool again = false;
for (int i = 0; i < 3; i++)
{
if (!TryGetLiveUnit(mapData, originId, out origin) || !TryGetLiveUnit(mapData, targetId, out target)) return;
var divine = GetBuff(mapData, origin,true, out var skill);
if(divine is SanaeDivineType.BigUnlucky or SanaeDivineType.BigLucky)
again = true;
@ -65,10 +69,8 @@ namespace Logic.Skill
var timer = Timer.Instance;
System.Action playOmikuji = () =>
{
if (mapData == null || origin == null || target == null) return;
var originGrid = origin.Grid(mapData);
var targetGrid = target.Grid(mapData);
if (originGrid == null || targetGrid == null) return;
if (!TryGetLiveUnitGrid(mapData, originId, out _, out var originGrid)) return;
if (!TryGetLiveUnitGrid(mapData, targetId, out _, out var targetGrid)) return;
//处理投掷神签动画
OmikujiAnim(mapData, originGrid, targetGrid, divine);
};
@ -85,6 +87,7 @@ namespace Logic.Skill
}
else
{
if (!TryGetLiveUnit(mapData, targetId, out target)) return;
target.AddSkill_Legacy(skill,mapData,false,1,false,-1,false,SpecialAddSkillType.AddTurnLimit,origin.Id);
if (divine == SanaeDivineType.BigLucky)
{
@ -96,11 +99,15 @@ namespace Logic.Skill
}
if(again)
{
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
}
}
//处理单次效果
else
{
if (!TryGetLiveUnit(mapData, originId, out origin) || !TryGetLiveUnit(mapData, targetId, out target)) return;
var divine = GetBuff(mapData, origin, true, out var skill);
//处理投掷神签动画
var originGrid = origin.Grid(mapData);
@ -112,13 +119,16 @@ namespace Logic.Skill
{
BigUnlucky(mapData, origin,targetGrid,0);
//origin.
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
}
else
{
if (!TryGetLiveUnit(mapData, targetId, out target)) return;
target.AddSkill_Legacy(skill,mapData,false,1,false, -1,false,SpecialAddSkillType.AddTurnLimit,origin.Id);
if (divine == SanaeDivineType.BigLucky)
{
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
BigLucky(mapData, origin,targetGrid,0f);
}
@ -133,23 +143,30 @@ namespace Logic.Skill
{
if (mapData == null || info?.DamageOrigin == null || info.DamageTargetGrid == null) return;
if (info.DamageType != DamageType.ActiveAttack) return;
var originId = info.DamageOrigin.Id;
var targetGridId = info.DamageTargetGrid.Id;
var targetId = info.DamageTarget?.Id ?? 0;
if (!TryGetLiveUnitGrid(mapData, originId, out var origin, out _)) return;
if (!TryGetLiveGrid(mapData, targetGridId, out var targetGrid)) return;
//如果是三连发
if (info.DamageOrigin.Skills != null && info.DamageOrigin.GetSkill(SkillType.SANAENINE,out var _))
if (origin.Skills != null && origin.GetSkill(SkillType.SANAENINE,out var _))
{
bool again = false;
for (int i = 0; i < 3; i++)
{
var divine = GetBuff(mapData, info.DamageOrigin,false, out var skill);
if (!TryGetLiveUnitGrid(mapData, originId, out origin, out _)) return;
if (!TryGetLiveGrid(mapData, targetGridId, out targetGrid)) return;
var divine = GetBuff(mapData, origin,false, out var skill);
if(divine is SanaeDivineType.BigUnlucky or SanaeDivineType.BigLucky)
again = true;
System.Action playOmikuji = () =>
{
if (mapData == null || info.DamageOrigin == null || info.DamageTargetGrid == null) return;
var originGrid = info.DamageOrigin.Grid(mapData);
if (originGrid == null) return;
if (!TryGetLiveUnitGrid(mapData, originId, out _, out var originGrid)) return;
if (!TryGetLiveGrid(mapData, targetGridId, out var liveTargetGrid)) return;
//处理投掷神签动画
OmikujiAnim(mapData, originGrid, info.DamageTargetGrid, divine);
OmikujiAnim(mapData, originGrid, liveTargetGrid, divine);
};
if (Timer.Instance == null)
playOmikuji();
@ -157,46 +174,54 @@ namespace Logic.Skill
Timer.Instance.TimerRegister(this, playOmikuji,1.1f * i,"SANAEDIVINE - SANAENINE");
//处理大凶
if (divine == SanaeDivineType.BigUnlucky)
BigUnlucky(mapData, info.DamageOrigin,info.DamageTargetGrid,1.1f * i);
BigUnlucky(mapData, origin,targetGrid,1.1f * i);
else
{
//skill = SkillType.DIVINE_E4_KILL;
info.DamageTarget?.AddSkill_Legacy(skill,mapData,false,1,false, -1,false, SpecialAddSkillType.AddTurnLimit,info.DamageOrigin.Id);
if (targetId != 0 && TryGetLiveUnit(mapData, targetId, out var liveTarget))
liveTarget.AddSkill_Legacy(skill,mapData,false,1,false, -1,false, SpecialAddSkillType.AddTurnLimit,origin.Id);
if (divine == SanaeDivineType.BigLucky)
{
BigLucky(mapData, info.DamageOrigin,info.DamageTargetGrid,1.1f * i);
BigLucky(mapData, origin,targetGrid,1.1f * i);
}
}
}
if(again)
info.DamageOrigin.SetFullActionPoint_AllSkillRefresh();
{
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
}
}
//如果是单发
else
{
var divine = GetBuff(mapData, info.DamageOrigin,false, out var skill);
if (!TryGetLiveUnitGrid(mapData, originId, out origin, out var originGrid)) return;
if (!TryGetLiveGrid(mapData, targetGridId, out targetGrid)) return;
var divine = GetBuff(mapData, origin,false, out var skill);
//处理投掷神签动画
var originGrid = info.DamageOrigin.Grid(mapData);
if (originGrid == null) return;
OmikujiAnim(mapData, originGrid, info.DamageTargetGrid, divine);
OmikujiAnim(mapData, originGrid, targetGrid, divine);
//处理大凶
if (divine == SanaeDivineType.BigUnlucky)
{
BigUnlucky(mapData, info.DamageOrigin,info.DamageTargetGrid,0f);
info.DamageOrigin.SetFullActionPoint_AllSkillRefresh();
BigUnlucky(mapData, origin,targetGrid,0f);
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
}
else
{
//skill = SkillType.DIVINE_E4_KILL;
info.DamageTarget?.AddSkill_Legacy(skill,mapData,false,1,false,-1,false,SpecialAddSkillType.AddTurnLimit,info.DamageOrigin.Id);
if (targetId != 0 && TryGetLiveUnit(mapData, targetId, out var liveTarget))
liveTarget.AddSkill_Legacy(skill,mapData,false,1,false,-1,false,SpecialAddSkillType.AddTurnLimit,origin.Id);
if (divine == SanaeDivineType.BigLucky)
{
info.DamageOrigin.SetFullActionPoint_AllSkillRefresh();
BigLucky(mapData, info.DamageOrigin,info.DamageTargetGrid,0f);
if (!TryGetLiveUnit(mapData, originId, out origin)) return;
origin.SetFullActionPoint_AllSkillRefresh();
BigLucky(mapData, origin,targetGrid,0f);
}
}
@ -218,11 +243,11 @@ namespace Logic.Skill
if(Table.Instance.ProjectileTypeDataAssets != null &&
Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(omikuji, out projInfo))
MapRenderer.Instance?.ProjectileManager?.CreateProjectile(startPos,endPos,omikuji);
var targetGrid = target;
var targetGridId = target.Id;
var map = mapData;
System.Action playOmikujiVfx = () =>
{
if (targetGrid == null || map == null) return;
if (!TryGetLiveGrid(map, targetGridId, out var liveTargetGrid)) return;
var vfx1 = divine switch
{
SanaeDivineType.BigLucky => GridVFXType.BigLucky,
@ -238,7 +263,7 @@ namespace Logic.Skill
_ => GridVFXType.BigUnluckyText
};
var renderer = targetGrid.Renderer(map);
var renderer = liveTargetGrid.Renderer(map);
renderer?.PlayVFXInSight(new GridVFXParams(vfx1));
renderer?.PlayVFXInSight(new GridVFXParams(vfx2));
@ -252,28 +277,39 @@ namespace Logic.Skill
private void BigUnlucky(MapData mapData, UnitData originUnit,GridData targetGrid,float waitTime)
{
var grid = targetGrid;
var originId = originUnit?.Id ?? 0;
var gridId = targetGrid?.Id ?? 0;
if (!TryGetLiveUnit(mapData, originId, out originUnit)) return;
if (!TryGetLiveGrid(mapData, gridId, out var grid)) return;
ProjectileTypeInfo projInfo = null;
if (targetGrid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
if ((grid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
&& Table.Instance?.ProjectileTypeDataAssets != null)
Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(ProjectileType.SanaeOmikuji, out projInfo);
var aroundBuf = RentAroundBuf();
mapData.GridMap.GetAroundGridData(1, 1, grid, aroundBuf);
foreach (var round in aroundBuf)
{
if (round == null) continue;
if (!TryGetLiveUnit(mapData, originId, out originUnit)) break;
if (!round.RealUnit(mapData, out var unit)) continue;
if (!unit.IsValidOnMap(mapData, round)) continue;
if (!unit.IsAlive()) continue;
bool sameUnion = mapData.SameUnionByUnitId(unit.Id, originUnit.Id);
var map = mapData;
var tmpGrid = round;
var tmpGridId = round.Id;
var tmpUnitId = unit.Id;
var dmg = sameUnion ? _bigUnluckyFriendDmg : _bigUnluckyEnemyDmg;
var canBeKilled = unit.CanBeKilled(mapData);
var dmgInfo = Main.UnitLogic.DamageSettlement(mapData, originUnit, unit, dmg , sameUnion ? DamageType.KillSelf : DamageType.Splash);
if (dmgInfo?.DamageTargetGrid == null) continue;
//处理视觉
if (dmgInfo.DamageTargetGrid.InMainSight())
{
Timer.Instance.TimerRegister(this, () =>
RegisterOrRunTimer(() =>
{
if (!TryGetLiveGrid(map, tmpGridId, out var tmpGrid)) return;
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage,dmg));
if (dmgInfo.IsKill)
@ -282,7 +318,8 @@ namespace Logic.Skill
if (canBeKilled)
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Die));
}
unit.Renderer(map)?.InstantUpdateUnit(showoff: true);
if (TryGetLiveUnit(map, tmpUnitId, out var liveUnit))
liveUnit.Renderer(map)?.InstantUpdateUnit(showoff: true);
},(projInfo?.AnimTime ?? 0f )+ waitTime,"SANAEDIVINE OMIKUJI BigUnlucky Anim");
}
@ -293,29 +330,40 @@ namespace Logic.Skill
private void BigLucky(MapData mapData, UnitData originUnit,GridData targetGrid,float waitTime)
{
var grid = targetGrid;
var originId = originUnit?.Id ?? 0;
var gridId = targetGrid?.Id ?? 0;
if (!TryGetLiveUnit(mapData, originId, out originUnit)) return;
if (!TryGetLiveGrid(mapData, gridId, out var grid)) return;
ProjectileTypeInfo projInfo = null;
if (targetGrid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
if ((grid.InMainSight() || (originUnit.Grid(mapData)?.InMainSight()??false))
&& Table.Instance?.ProjectileTypeDataAssets != null)
Table.Instance.ProjectileTypeDataAssets.GetProjectileTypeInfo(ProjectileType.SanaeOmikuji, out projInfo);
var aroundBuf = RentAroundBuf();
mapData.GridMap.GetAroundGridData(1, 1, grid, aroundBuf);
foreach (var round in aroundBuf)
{
if (round == null) continue;
if (!TryGetLiveUnit(mapData, originId, out originUnit)) break;
if (!round.RealUnit(mapData, out var unit)) continue;
if (!unit.IsValidOnMap(mapData, round)) continue;
bool sameUnion = mapData.SameUnionByUnitId(unit.Id, originUnit.Id);
var map = mapData;
var tmpGrid = round;
var tmpGridId = round.Id;
var tmpUnitId = unit.Id;
//处理敌人
if (!sameUnion)
{
var canBeKilled = unit.CanBeKilled(mapData);
var dmgInfo = Main.UnitLogic.DamageSettlement(mapData, originUnit, unit, _bigLuckyDmg , DamageType.Splash);
if (dmgInfo?.DamageTargetGrid == null) continue;
//处理视觉
if (dmgInfo.DamageTargetGrid.InMainSight())
{
Timer.Instance.TimerRegister(this, () =>
RegisterOrRunTimer(() =>
{
if (!TryGetLiveGrid(map, tmpGridId, out var tmpGrid)) return;
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage,4));
if (dmgInfo.IsKill)
@ -325,7 +373,8 @@ namespace Logic.Skill
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Die));
}
unit.Renderer(map)?.InstantUpdateUnit(showoff: true);
if (TryGetLiveUnit(map, tmpUnitId, out var liveUnit))
liveUnit.Renderer(map)?.InstantUpdateUnit(showoff: true);
},(projInfo?.AnimTime ?? 0f) + waitTime,"SANAEDIVINE OMIKUJI BigUnlucky Anim");
}
@ -338,10 +387,12 @@ namespace Logic.Skill
//处理视觉
if (unit.Grid(map)?.InMainSight()??false)
{
Timer.Instance.TimerRegister(this, () =>
RegisterOrRunTimer(() =>
{
if (!TryGetLiveGrid(map, tmpGridId, out var tmpGrid)) return;
tmpGrid.Renderer(map)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Heal));
unit.Renderer(map)?.InstantUpdateUnit(showoff: true);
if (TryGetLiveUnit(map, tmpUnitId, out var liveUnit))
liveUnit.Renderer(map)?.InstantUpdateUnit(showoff: true);
},projInfo?.AnimTime ?? 0f,"SANAEDIVINE OMIKUJI BigUnlucky Anim");
}
@ -353,10 +404,47 @@ namespace Logic.Skill
}
ReturnAroundBuf();
}
private void RegisterOrRunTimer(System.Action action, float delay, string message)
{
if (action == null) return;
var timer = Timer.Instance;
if (timer == null)
{
action();
return;
}
timer.TimerRegister(this, action, delay, message);
}
private static bool TryGetLiveUnit(MapData mapData, uint unitId, out UnitData unit)
{
unit = null;
if (unitId == 0 || mapData?.UnitMap == null) return false;
return mapData.UnitMap.GetUnitDataByUnitId(unitId, out unit)
&& unit != null
&& unit.IsValidOnMap(mapData);
}
private static bool TryGetLiveGrid(MapData mapData, uint gridId, out GridData grid)
{
grid = null;
if (gridId == 0 || mapData?.GridMap == null) return false;
return mapData.GridMap.GetGridDataByGid(gridId, out grid) && grid != null;
}
private static bool TryGetLiveUnitGrid(MapData mapData, uint unitId, out UnitData unit, out GridData grid)
{
grid = null;
if (!TryGetLiveUnit(mapData, unitId, out unit)) return false;
return mapData.GetGridDataByUnitId(unit.Id, out grid) && grid != null;
}
public SanaeDivineType GetBuff(MapData map, UnitData self,bool IsHeal, out SkillType skill)
{
skill = SkillType.NONE;
if (map?.Net == null || self == null) return SanaeDivineType.Lucky;
var roll = map.Net.GetRandom(map).Next(1, 101); // 生成1到100的随机数
var buff = SanaeDivineType.Lucky;

View File

@ -37,8 +37,11 @@ namespace Logic.Skill
public override void OnMove(UnitData self, GridData grid, MapData mapData, MoveType moveType, List<Vector2Int> path = null)
{
if (mapData == null || self == null || grid == null) return;
if (!self.IsValidOnMap(mapData, grid)) return;
if (!mapData.GetPlayerDataByUnitId(self.Id, out var selfPlayer)) return;
using var pooledSelfUnitList = THCollectionPool.GetHashSetHandle<UnitData>(out var selfUnitList);
mapData.GetPlayerDataByUnitId(self.Id, out var selfPlayer);
mapData.GetUnitDataListByPlayerId(selfPlayer.Id, selfUnitList);
// 收集需要视觉更新的数据。Renderer 必须在 DamageSettlement 前缓存。
@ -49,12 +52,18 @@ namespace Logic.Skill
mapData.GridMap.GetAroundGridData(1, 1, grid, aroundBuf);
foreach (var roundGrid in aroundBuf)
{
var ROgrid = MapRenderer.Instance.ROGridMap[roundGrid.Id];
if (roundGrid == null) continue;
if (!self.IsValidOnMap(mapData, grid)) break;
GridRenderer ROgrid = null;
if (mapData == Main.MapData)
MapRenderer.Instance?.ROGridMap?.TryGetValue(roundGrid.Id, out ROgrid);
//如果格子上没有单位,播放地震动画
if (!roundGrid.RealUnit(mapData,out var unit))
{
//TODO 下面的是不规范做法,后面要迭代
if (roundGrid.InMainSight())
if (ROgrid != null && roundGrid.InMainSight())
{
ROgrid.SetBounceAnim(NeedRandomWait:true);
}
@ -68,7 +77,8 @@ namespace Logic.Skill
var damage = Table.Instance.CalcDamage(mapData, self, unit, damagePara:0.5f);
var targetRenderer = unit.Renderer(mapData);
Main.UnitLogic.DamageSettlement(mapData, self, unit, damage, DamageType.Splash);
var settlement = Main.UnitLogic.DamageSettlement(mapData, self, unit, damage, DamageType.Splash);
if (settlement == null) continue;
bool wasKilled = !unit.IsAlive();
if (roundGrid.InMainSight())
@ -116,7 +126,7 @@ namespace Logic.Skill
g.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
g.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage, d));
g.Renderer(mapData)?.InstantUpdateGrid();
ro.SetBounceAnim(NeedRandomWait:true);
ro?.SetBounceAnim(NeedRandomWait:true);
}
}
});
@ -141,7 +151,7 @@ namespace Logic.Skill
roundGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Hurt));
roundGrid.Renderer(mapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Damage, damage));
roundGrid.Renderer(mapData)?.InstantUpdateGrid();
ROgrid.SetBounceAnim(NeedRandomWait:true);
ROgrid?.SetBounceAnim(NeedRandomWait:true);
}
}
}

View File

@ -192,52 +192,68 @@ namespace TH1_Logic.Steam
// 连接前检查
private bool PreConnectionCheck(CSteamID targetSteamID)
{
// 检查Steam是否已登录
if (!SteamUser.BLoggedOn())
try
{
LogSystem.LogError("Steam not logged in");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
// 检查Steam是否已登录
if (!SteamUser.BLoggedOn())
{
LogSystem.LogWarning("Steam not logged in");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
return false;
}
// 检查目标用户是否在线
var friendState = SteamFriends.GetFriendPersonaState(targetSteamID);
if (friendState == EPersonaState.k_EPersonaStateOffline)
{
LogSystem.LogWarning($"Target user {targetSteamID} appears offline");
}
// 检查当前lobby信息
LogSystem.LogInfo("Checking lobby status...");
return true;
}
catch (Exception ex)
{
LogSystem.LogWarning($"Steam P2P pre-check skipped: {ex.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
return false;
}
// 检查目标用户是否在线
var friendState = SteamFriends.GetFriendPersonaState(targetSteamID);
if (friendState == EPersonaState.k_EPersonaStateOffline)
{
LogSystem.LogWarning($"Target user {targetSteamID} appears offline");
}
// 检查当前lobby信息
LogSystem.LogInfo("Checking lobby status...");
return true;
}
private void DiagnoseNetworkStatus(CSteamID targetSteamID)
{
LogSystem.LogInfo("=== P2P 连接诊断 ===");
// Steam 状态
LogSystem.LogInfo($"Steam 登录状态: {SteamUser.BLoggedOn()}");
LogSystem.LogInfo($"本地 Steam ID: {SteamUser.GetSteamID()}");
// 目标用户状态
var friendState = SteamFriends.GetFriendPersonaState(targetSteamID);
LogSystem.LogInfo($"目标用户状态: {friendState}");
// 检查是否是好友
var relationShip = SteamFriends.GetFriendRelationship(targetSteamID);
LogSystem.LogInfo($"好友关系: {relationShip}");
// Steam 网络状态
SteamNetworkingUtils.GetRelayNetworkStatus(out var relayStatus);
LogSystem.LogInfo($"Steam 中继网络: {relayStatus.m_eAvail}");
LogSystem.LogInfo($"中继网络调试信息: {relayStatus.m_debugMsg}");
if (relayStatus.m_eAvail != ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current)
try
{
LogSystem.LogWarning($"Steam 中继网络问题: {relayStatus.m_debugMsg}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
LogSystem.LogInfo("=== P2P 连接诊断 ===");
// Steam 状态
LogSystem.LogInfo($"Steam 登录状态: {SteamUser.BLoggedOn()}");
LogSystem.LogInfo($"本地 Steam ID: {SteamUser.GetSteamID()}");
// 目标用户状态
var friendState = SteamFriends.GetFriendPersonaState(targetSteamID);
LogSystem.LogInfo($"目标用户状态: {friendState}");
// 检查是否是好友
var relationShip = SteamFriends.GetFriendRelationship(targetSteamID);
LogSystem.LogInfo($"好友关系: {relationShip}");
// Steam 网络状态
SteamNetworkingUtils.GetRelayNetworkStatus(out var relayStatus);
LogSystem.LogInfo($"Steam 中继网络: {relayStatus.m_eAvail}");
LogSystem.LogInfo($"中继网络调试信息: {relayStatus.m_debugMsg}");
if (relayStatus.m_eAvail != ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current)
{
LogSystem.LogWarning($"Steam 中继网络问题: {relayStatus.m_debugMsg}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.P2PConnectionFailed);
}
}
catch (Exception ex)
{
LogSystem.LogWarning($"Steam P2P diagnose skipped: {ex.Message}");
}
}

View File

@ -78,6 +78,8 @@ namespace TH1_Logic.Steam
private bool _steamServerConnected = false;
private int _steamSessionFailCount;
private bool _isHandlingSessionLoss;
private bool _steamApiUnavailable;
private bool _steamApiUnavailableLogged;
private int _suppressP2PSendFailureLobbyErrors;
private const float SteamSessionCheckInterval = 1f;
private const int SteamSessionFailThreshold = 2;
@ -146,6 +148,8 @@ namespace TH1_Logic.Steam
_refreshSteamStatus = 0;
_steamSessionFailCount = 0;
_isHandlingSessionLoss = false;
_steamApiUnavailable = false;
_steamApiUnavailableLogged = false;
_onlineFriendsId = new Dictionary<ulong, CSteamID>();
_onlineFriendsInfo = new Dictionary<ulong, MemberInfo>();
_memberInfos = new Dictionary<ulong, MemberInfo>();
@ -154,10 +158,101 @@ namespace TH1_Logic.Steam
_status = new SteamNetworkStatus();
}
private bool TrySteamApi<T>(string context, Func<T> action, out T value)
{
value = default;
if (_steamApiUnavailable) return false;
try
{
value = action();
return true;
}
catch (DllNotFoundException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (EntryPointNotFoundException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (InvalidOperationException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (Exception e)
{
LogSystem.LogWarning($"Steam API call failed at {context}: {e.Message}");
return false;
}
}
private bool TrySteamApi(string context, System.Action action)
{
if (_steamApiUnavailable) return false;
try
{
action();
return true;
}
catch (DllNotFoundException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (EntryPointNotFoundException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (InvalidOperationException e)
{
MarkSteamApiUnavailable(context, e);
return false;
}
catch (Exception e)
{
LogSystem.LogWarning($"Steam API call failed at {context}: {e.Message}");
return false;
}
}
private void MarkSteamApiUnavailable(string context, Exception e)
{
_steamApiUnavailable = true;
_isSteamInitialized = false;
_isLoggedIn = false;
_isLobbyInitialized = false;
_steamServerConnected = false;
_status = new SteamNetworkStatus();
if (!_steamApiUnavailableLogged)
{
_steamApiUnavailableLogged = true;
LogSystem.LogWarning($"Steam API unavailable at {context}: {e.GetType().Name}: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
}
}
private bool EnsureSteamReadyForLobbyAction(string context)
{
if (_steamApiUnavailable || !_isSteamInitialized)
{
LogSystem.LogWarning($"Steam lobby action skipped because Steam API is unavailable: {context}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
return false;
}
return true;
}
// 初始化
public void Init()
{
if (_steamApiUnavailable) return;
_steamSDKUpdateRecord += Time.deltaTime;
if (_steamSDKUpdateRecord < 2) return;
_steamSDKUpdateRecord = 0;
@ -171,10 +266,11 @@ namespace TH1_Logic.Steam
// 刷新 Steam
private void RefreshSteamInit()
{
if (_steamApiUnavailable) return;
if (_isSteamInitialized) return;
// 检查Steam是否运行
if (!SteamAPI.IsSteamRunning())
if (!TrySteamApi("SteamAPI.IsSteamRunning", SteamAPI.IsSteamRunning, out var steamRunning) || !steamRunning)
{
// LogSystem.LogError("Steam客户端未运行请先启动Steam。");
return;
@ -182,25 +278,51 @@ namespace TH1_Logic.Steam
// 初始化Steam API
LogSystem.LogInfo("开始初始化Steam...");
var initResult = SteamAPI.InitEx(out string steamErrMsg);
ESteamAPIInitResult initResult;
string steamErrMsg;
try
{
initResult = SteamAPI.InitEx(out steamErrMsg);
}
catch (DllNotFoundException e)
{
MarkSteamApiUnavailable("SteamAPI.InitEx", e);
return;
}
catch (EntryPointNotFoundException e)
{
MarkSteamApiUnavailable("SteamAPI.InitEx", e);
return;
}
catch (InvalidOperationException e)
{
MarkSteamApiUnavailable("SteamAPI.InitEx", e);
return;
}
catch (Exception e)
{
LogSystem.LogWarning($"Steam API initialization failed: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
return;
}
_isSteamInitialized = initResult == ESteamAPIInitResult.k_ESteamAPIInitResult_OK;
if (!_isSteamInitialized)
{
LogSystem.LogError($"Steam API初始化失败result={initResult}, msg={steamErrMsg}");
LogSystem.LogWarning($"Steam API初始化失败result={initResult}, msg={steamErrMsg}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
switch (initResult)
{
case ESteamAPIInitResult.k_ESteamAPIInitResult_NoSteamClient:
LogSystem.LogError("→ Steam客户端未运行或未登录请先打开Steam并登录账号。");
LogSystem.LogWarning("→ Steam客户端未运行或未登录请先打开Steam并登录账号。");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
break;
case ESteamAPIInitResult.k_ESteamAPIInitResult_VersionMismatch:
LogSystem.LogError("→ Steam客户端版本过旧请在Steam中检查更新。");
LogSystem.LogWarning("→ Steam客户端版本过旧请在Steam中检查更新。");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamClientOutdated);
break;
case ESteamAPIInitResult.k_ESteamAPIInitResult_FailedGeneric:
LogSystem.LogError("→ 通用失败。常见原因当前Steam账号未拥有该AppID需在Steamworks后台授予权限或工作目录下steam_appid.txt位置错误或上一次进程未退干净。");
LogSystem.LogWarning("→ 通用失败。常见原因当前Steam账号未拥有该AppID需在Steamworks后台授予权限或工作目录下steam_appid.txt位置错误或上一次进程未退干净。");
break;
}
return;
@ -217,8 +339,10 @@ namespace TH1_Logic.Steam
if (!_isSteamInitialized) return;
// 基础Steam连接状态
_status.IsSteamConnected = SteamAPI.IsSteamRunning();
_status.IsLoggedOn = SteamUser.BLoggedOn();
if (!TrySteamApi("RefreshSteamStatus.IsSteamRunning", SteamAPI.IsSteamRunning, out var steamRunning)) return;
if (!TrySteamApi("RefreshSteamStatus.BLoggedOn", SteamUser.BLoggedOn, out var loggedOn)) return;
_status.IsSteamConnected = steamRunning;
_status.IsLoggedOn = loggedOn;
_status.IsInLobby = IsInLobby();
_status.IsP2PReady = SimpleP2P.Instance?.IsInitialized ?? false;
_status.SteamServerConnected = IsSteamSessionLikelyAlive();
@ -229,25 +353,18 @@ namespace TH1_Logic.Steam
private void RefreshLoginStatus()
{
if (!_isSteamInitialized || _isLoggedIn) return;
try
if (!TrySteamApi("RefreshLoginStatus.BLoggedOn", SteamUser.BLoggedOn, out var loggedOn)) return;
_isLoggedIn = loggedOn;
if (_isLoggedIn)
{
_isLoggedIn = SteamUser.BLoggedOn();
if (_isLoggedIn)
{
_selfID = SteamUser.GetSteamID();
_selfName = SteamFriends.GetPersonaName();
LogSystem.LogInfo($"Steam用户已登录: {_selfName} ({_selfID})");
}
else
{
LogSystem.LogWarning("Steam用户未登录");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
}
if (!TrySteamApi("RefreshLoginStatus.GetSteamID", SteamUser.GetSteamID, out _selfID)) return;
if (!TrySteamApi("RefreshLoginStatus.GetPersonaName", SteamFriends.GetPersonaName, out _selfName)) return;
LogSystem.LogInfo($"Steam用户已登录: {_selfName} ({_selfID})");
}
catch (System.Exception e)
else
{
LogSystem.LogError($"检查用户登录状态异常: {e.Message}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamUnavailable);
LogSystem.LogWarning("Steam用户未登录");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.SteamLoginRequired);
}
}
@ -291,16 +408,17 @@ namespace TH1_Logic.Steam
// 添加房间内的非好友成员
if (CurrentLobby.IsValid())
{
int memberCount = SteamMatchmaking.GetNumLobbyMembers(CurrentLobby);
if (!TrySteamApi("RefreshOnlineFriends.GetNumLobbyMembers", () => SteamMatchmaking.GetNumLobbyMembers(CurrentLobby), out var memberCount)) return;
var selfMemberId = GetSelfMemberId();
for (int i = 0; i < memberCount; i++)
{
var memberId = SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i);
if (!TrySteamApi("RefreshOnlineFriends.GetLobbyMemberByIndex", () => SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i), out var memberId)) return;
// 跳过自己
if (memberId == SteamUser.GetSteamID()) continue;
if (memberId.m_SteamID == selfMemberId) continue;
// 检查是否已在好友列表中
if (friends.All(f => f.id != memberId))
{
string memberName = SteamFriends.GetFriendPersonaName(memberId);
if (!TrySteamApi("RefreshOnlineFriends.GetFriendPersonaName", () => SteamFriends.GetFriendPersonaName(memberId), out var memberName)) return;
friends.Add((memberId, memberName));
}
}
@ -353,7 +471,7 @@ namespace TH1_Logic.Steam
if (!_isSteamInitialized || !_isLoggedIn || !_isLobbyInitialized) return;
// 更新Steam回调
SteamAPI.RunCallbacks();
if (!TrySteamApi("SteamAPI.RunCallbacks", SteamAPI.RunCallbacks)) return;
SimpleP2P.Instance.Update();
SimpleP2P.Instance.PollMessages();
@ -418,10 +536,35 @@ namespace TH1_Logic.Steam
public bool IsSteamSessionLikelyAlive()
{
if (!_isSteamInitialized) return false;
if (!SteamAPI.IsSteamRunning()) return false;
if (!SteamUser.BLoggedOn()) return false;
if (!TrySteamApi("IsSteamSessionLikelyAlive.IsSteamRunning", SteamAPI.IsSteamRunning, out var steamRunning) || !steamRunning) return false;
if (!TrySteamApi("IsSteamSessionLikelyAlive.BLoggedOn", SteamUser.BLoggedOn, out var loggedOn) || !loggedOn) return false;
var avail = SteamNetworkingUtils.GetRelayNetworkStatus(out var details);
ESteamNetworkingAvailability avail;
SteamRelayNetworkStatus_t details;
try
{
avail = SteamNetworkingUtils.GetRelayNetworkStatus(out details);
}
catch (DllNotFoundException e)
{
MarkSteamApiUnavailable("SteamNetworkingUtils.GetRelayNetworkStatus", e);
return false;
}
catch (EntryPointNotFoundException e)
{
MarkSteamApiUnavailable("SteamNetworkingUtils.GetRelayNetworkStatus", e);
return false;
}
catch (InvalidOperationException e)
{
MarkSteamApiUnavailable("SteamNetworkingUtils.GetRelayNetworkStatus", e);
return false;
}
catch (Exception e)
{
LogSystem.LogWarning($"Steam relay status check failed: {e.Message}");
return false;
}
bool relayOk = avail == ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current
|| details.m_eAvail == ESteamNetworkingAvailability.k_ESteamNetworkingAvailability_Current;
return relayOk;
@ -456,16 +599,18 @@ namespace TH1_Logic.Steam
if (!_isSteamInitialized) return;
// 获取当前App ID
var currentAppId = SteamUtils.GetAppID();
if (!TrySteamApi("DisplayLaunchInfo.GetAppID", SteamUtils.GetAppID, out var currentAppId)) return;
LogSystem.LogInfo($"当前Steam App ID: {currentAppId}");
// 检查启动方式
bool launchedViaSteam = SteamApps.BIsSubscribedApp(currentAppId);
if (!TrySteamApi("DisplayLaunchInfo.BIsSubscribedApp", () => SteamApps.BIsSubscribedApp(currentAppId), out var launchedViaSteam)) return;
LogSystem.LogInfo($"通过Steam启动: {(launchedViaSteam ? "" : "")}");
// 显示Steam环境信息
LogSystem.LogInfo($"Steam语言: {SteamApps.GetCurrentGameLanguage()}");
LogSystem.LogInfo($"Steam服务器连接: {(SteamUser.BLoggedOn() ? "" : "")}");
if (TrySteamApi("DisplayLaunchInfo.GetCurrentGameLanguage", SteamApps.GetCurrentGameLanguage, out var language))
LogSystem.LogInfo($"Steam语言: {language}");
if (TrySteamApi("DisplayLaunchInfo.BLoggedOn", SteamUser.BLoggedOn, out var loggedOn))
LogSystem.LogInfo($"Steam服务器连接: {(loggedOn ? "" : "")}");
// 检查DLC和订阅状态
if (currentAppId.m_AppId == 480) // Spacewar测试应用
@ -481,6 +626,7 @@ namespace TH1_Logic.Steam
// 建房
public void CreateLobby(int maxMembers = 4, bool isPublic = true, string password = "")
{
if (!EnsureSteamReadyForLobbyAction(nameof(CreateLobby))) return;
if (CurrentState != LobbyState.None)
{
LogSystem.LogInfo($"Cannot create lobby in state: {CurrentState}");
@ -491,7 +637,8 @@ namespace TH1_Logic.Steam
_pendingLobbyIsPublic = isPublic;
CurrentState = LobbyState.Creating;
LogSystem.LogInfo($"Creating {(isPublic ? "public" : "friends-only")} lobby with max members: {maxMembers}, hasPassword: {!string.IsNullOrEmpty(_pendingLobbyPassword)}");
SteamMatchmaking.CreateLobby(isPublic?ELobbyType.k_ELobbyTypePublic:ELobbyType.k_ELobbyTypeFriendsOnly, maxMembers);
if (!TrySteamApi("SteamMatchmaking.CreateLobby", () => SteamMatchmaking.CreateLobby(isPublic?ELobbyType.k_ELobbyTypePublic:ELobbyType.k_ELobbyTypeFriendsOnly, maxMembers)))
ResetLobbyState();
}
// 加入房间
@ -502,6 +649,7 @@ namespace TH1_Logic.Steam
private void JoinLobbyInternal(CSteamID lobbyId, string password, bool requestLobbyDataIfMissing, bool checkPassword)
{
if (!EnsureSteamReadyForLobbyAction(nameof(JoinLobby))) return;
if (CurrentState != LobbyState.None)
{
//LogSystem.LogInfo($"Cannot join lobby in state: {CurrentState}");
@ -514,10 +662,11 @@ namespace TH1_Logic.Steam
_pendingJoinPassword = password ?? "";
_pendingJoinCheckPassword = checkPassword;
if (!SteamMatchmaking.RequestLobbyData(lobbyId))
if (!TrySteamApi("SteamMatchmaking.RequestLobbyData", () => SteamMatchmaking.RequestLobbyData(lobbyId), out var requestLobbyData)
|| !requestLobbyData)
{
ClearPendingJoinLobby();
LogSystem.LogError($"Failed to request lobby data before joining: {lobbyId}");
LogSystem.LogWarning($"Failed to request lobby data before joining: {lobbyId}");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyJoinFailed);
OnLobbyErrorEvent?.Invoke("Failed to request lobby data before joining");
}
@ -528,7 +677,7 @@ namespace TH1_Logic.Steam
if (checkPassword && !ValidateLobbyPasswordForJoin(lobbyId, password)) return;
// TODO 这里会涉及到房间子类对于UI的调用对于房间的多态是不合理的暂不处理糊屎
var version = SteamMatchmaking.GetLobbyData(lobbyId, "Version");
if (!TrySteamApi("SteamMatchmaking.GetLobbyData.Version", () => SteamMatchmaking.GetLobbyData(lobbyId, "Version"), out var version)) return;
if (!string.IsNullOrEmpty(version) && ConfigManager.Instance.VersionCfg.CurVersionInfo.Version != version)
{
LogSystem.LogInfo($"版本不一致 !!!");
@ -538,7 +687,8 @@ namespace TH1_Logic.Steam
LogSystem.LogInfo($"Joining lobby: {lobbyId}");
CurrentState = LobbyState.Joining;
SteamMatchmaking.JoinLobby(lobbyId);
if (!TrySteamApi("SteamMatchmaking.JoinLobby", () => SteamMatchmaking.JoinLobby(lobbyId)))
ResetLobbyState();
}
// 离开房间
@ -551,7 +701,7 @@ namespace TH1_Logic.Steam
// 断开所有P2P连接
SimpleP2P.Instance.DisconnectAll();
SteamMatchmaking.LeaveLobby(CurrentLobby);
TrySteamApi("SteamMatchmaking.LeaveLobby", () => SteamMatchmaking.LeaveLobby(CurrentLobby));
GameNetSender.Instance.ClearLobbyDataSyncState();
ResetLobbyState();
OnLobbyLeftEvent?.Invoke(null);
@ -568,14 +718,15 @@ namespace TH1_Logic.Steam
LogSystem.LogInfo("Disbanding lobby");
// 设置房间数据标记解散
SteamMatchmaking.SetLobbyData(CurrentLobby, "disbanded", "true");
TrySteamApi("SteamMatchmaking.SetLobbyData.disbanded", () => SteamMatchmaking.SetLobbyData(CurrentLobby, "disbanded", "true"));
// 踢出所有其他成员
foreach (var member in EnumerateMembers())
{
if (member != SteamUser.GetSteamID())
if (!TrySteamApi("DisbandLobby.GetSteamID", SteamUser.GetSteamID, out var selfId)) break;
if (member != selfId)
{
// Steam没有直接踢人API通过设置数据让客户端自动离开
SteamMatchmaking.SetLobbyMemberData(CurrentLobby, "kicked", "true");
TrySteamApi("SteamMatchmaking.SetLobbyMemberData.kicked", () => SteamMatchmaking.SetLobbyMemberData(CurrentLobby, "kicked", "true"));
}
}
@ -593,7 +744,8 @@ namespace TH1_Logic.Steam
return;
}
if (memberId == SteamUser.GetSteamID().m_SteamID)
if (!TrySteamApi("KickMember.GetSteamID", SteamUser.GetSteamID, out var selfIdForKick)) return;
if (memberId == selfIdForKick.m_SteamID)
{
LogSystem.LogError("Cannot kick yourself");
NetworkPlayerTipManager.Instance.Request(NetworkPlayerTipType.LobbyOperationFailed);
@ -605,7 +757,7 @@ namespace TH1_Logic.Steam
// 房主写 LobbyData只有房主有权限设 LobbyData所有客户端都能 GetLobbyData 读到
// 之前用的是 SetLobbyMemberData那个 API 只能设自己的成员数据,写出去对方根本读不到 —— 协议不匹配
// 被踢方在 RefreshKickInfo → CheckIfKicked 里读 kick_{selfId},发现是 "true" 就自动 LeaveLobby
SteamMatchmaking.SetLobbyData(CurrentLobby, $"kick_{memberId}", "true");
TrySteamApi("SteamMatchmaking.SetLobbyData.kick", () => SteamMatchmaking.SetLobbyData(CurrentLobby, $"kick_{memberId}", "true"));
}
// 通过房间ID直接加入无需好友关系
@ -654,7 +806,7 @@ namespace TH1_Logic.Steam
if (!LobbyIsPublic(lobbyId)) return true;
if (!LobbyHasPassword(lobbyId)) return true;
var expectedPassword = SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey);
if (!TrySteamApi("ValidateLobbyPasswordForJoin.GetLobbyData", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey), out var expectedPassword)) return false;
if (!string.IsNullOrEmpty(expectedPassword) && (password ?? "") == expectedPassword) return true;
var errorMsg = string.IsNullOrEmpty(expectedPassword)
@ -669,11 +821,16 @@ namespace TH1_Logic.Steam
private bool ShouldRequestLobbyDataBeforeJoin(CSteamID lobbyId)
{
if (!lobbyId.IsValid()) return false;
if (!string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, "Game"))) return false;
if (!string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, "Version"))) return false;
if (!string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, LobbyHasPasswordKey))) return false;
if (!string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey))) return false;
if (!string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, LobbyIsPublicKey))) return false;
if (!TrySteamApi("ShouldRequestLobbyDataBeforeJoin.Game", () => SteamMatchmaking.GetLobbyData(lobbyId, "Game"), out var game)) return false;
if (!TrySteamApi("ShouldRequestLobbyDataBeforeJoin.Version", () => SteamMatchmaking.GetLobbyData(lobbyId, "Version"), out var version)) return false;
if (!TrySteamApi("ShouldRequestLobbyDataBeforeJoin.HasPassword", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyHasPasswordKey), out var hasPassword)) return false;
if (!TrySteamApi("ShouldRequestLobbyDataBeforeJoin.Password", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey), out var lobbyPassword)) return false;
if (!TrySteamApi("ShouldRequestLobbyDataBeforeJoin.IsPublic", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyIsPublicKey), out var isPublic)) return false;
if (!string.IsNullOrEmpty(game)) return false;
if (!string.IsNullOrEmpty(version)) return false;
if (!string.IsNullOrEmpty(hasPassword)) return false;
if (!string.IsNullOrEmpty(lobbyPassword)) return false;
if (!string.IsNullOrEmpty(isPublic)) return false;
return true;
}
@ -686,15 +843,16 @@ namespace TH1_Logic.Steam
private bool LobbyHasPassword(CSteamID lobbyId)
{
var hasPasswordData = SteamMatchmaking.GetLobbyData(lobbyId, LobbyHasPasswordKey);
if (!TrySteamApi("LobbyHasPassword.HasPassword", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyHasPasswordKey), out var hasPasswordData)) return false;
if (IsTrueLobbyData(hasPasswordData)) return true;
if (IsFalseLobbyData(hasPasswordData)) return false;
return !string.IsNullOrEmpty(SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey));
return TrySteamApi("LobbyHasPassword.Password", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyPasswordKey), out var passwordData)
&& !string.IsNullOrEmpty(passwordData);
}
private bool LobbyIsPublic(CSteamID lobbyId)
{
var isPublicData = SteamMatchmaking.GetLobbyData(lobbyId, LobbyIsPublicKey);
if (!TrySteamApi("LobbyIsPublic.IsPublic", () => SteamMatchmaking.GetLobbyData(lobbyId, LobbyIsPublicKey), out var isPublicData)) return true;
if (IsTrueLobbyData(isPublicData)) return true;
if (IsFalseLobbyData(isPublicData)) return false;
return true;
@ -739,7 +897,8 @@ namespace TH1_Logic.Steam
public string GenerateUserCode()
{
if (!_isSteamInitialized || !_isLoggedIn) return null;
return Base36Encode(SteamUser.GetSteamID().m_SteamID);
var selfMemberId = GetSelfMemberId();
return selfMemberId == 0 ? null : Base36Encode(selfMemberId);
}
public void InviteByRawSteamId(string code)
@ -996,17 +1155,18 @@ namespace TH1_Logic.Steam
public List<(CSteamID id, string name)> GetOnlineFriends()
{
var list = new List<(CSteamID, string)>();
int count = SteamFriends.GetFriendCount(EFriendFlags.k_EFriendFlagImmediate);
if (!TrySteamApi("GetOnlineFriends.GetFriendCount", () => SteamFriends.GetFriendCount(EFriendFlags.k_EFriendFlagImmediate), out var count)) return list;
for (int i = 0; i < count; i++)
{
var fid = SteamFriends.GetFriendByIndex(i, EFriendFlags.k_EFriendFlagImmediate);
var state = SteamFriends.GetFriendPersonaState(fid);
if (!TrySteamApi("GetOnlineFriends.GetFriendByIndex", () => SteamFriends.GetFriendByIndex(i, EFriendFlags.k_EFriendFlagImmediate), out var fid)) return list;
if (!TrySteamApi("GetOnlineFriends.GetFriendPersonaState", () => SteamFriends.GetFriendPersonaState(fid), out var state)) return list;
if (state == EPersonaState.k_EPersonaStateOnline ||
state == EPersonaState.k_EPersonaStateAway ||
state == EPersonaState.k_EPersonaStateBusy ||
state == EPersonaState.k_EPersonaStateSnooze)
{
list.Add((fid, SteamFriends.GetFriendPersonaName(fid)));
if (!TrySteamApi("GetOnlineFriends.GetFriendPersonaName", () => SteamFriends.GetFriendPersonaName(fid), out var friendName)) return list;
list.Add((fid, friendName));
}
}
return list;
@ -1061,7 +1221,11 @@ namespace TH1_Logic.Steam
}
CurrentLobby = new CSteamID(data.m_ulSteamIDLobby);
CachedOwner = SteamUser.GetSteamID();
if (!TrySteamApi("OnLobbyCreatedCallback.GetSteamID", SteamUser.GetSteamID, out CachedOwner))
{
ResetLobbyState();
return;
}
LogSystem.LogInfo($"Lobby created successfully: {CurrentLobby}");
@ -1247,7 +1411,7 @@ namespace TH1_Logic.Steam
// 房主切换时内部处理
private void OnHostChangedInternal(CSteamID oldOwner, CSteamID newOwner)
{
if (oldOwner == SteamUser.GetSteamID())
if (oldOwner.m_SteamID == GetSelfMemberId())
{
LogSystem.LogWarning($"Local host ownership changed to {newOwner}, waiting local disconnect handling");
return;
@ -1287,8 +1451,9 @@ namespace TH1_Logic.Steam
// 检查是否被踢
private void CheckIfKicked()
{
var kickData = SteamMatchmaking.GetLobbyMemberData(CurrentLobby, SteamUser.GetSteamID(), "kicked");
var specificKick = SteamMatchmaking.GetLobbyData(CurrentLobby, $"kick_{SteamUser.GetSteamID().m_SteamID}");
if (!TrySteamApi("CheckIfKicked.GetSteamID", SteamUser.GetSteamID, out var selfId)) return;
if (!TrySteamApi("CheckIfKicked.GetLobbyMemberData", () => SteamMatchmaking.GetLobbyMemberData(CurrentLobby, selfId, "kicked"), out var kickData)) return;
if (!TrySteamApi("CheckIfKicked.GetLobbyData", () => SteamMatchmaking.GetLobbyData(CurrentLobby, $"kick_{selfId.m_SteamID}"), out var specificKick)) return;
if (kickData == "true" || specificKick == "true")
{
@ -1464,9 +1629,12 @@ namespace TH1_Logic.Steam
private IEnumerable<CSteamID> EnumerateMembers()
{
if (!CurrentLobby.IsValid()) yield break;
int count = SteamMatchmaking.GetNumLobbyMembers(CurrentLobby);
if (!TrySteamApi("EnumerateMembers.GetNumLobbyMembers", () => SteamMatchmaking.GetNumLobbyMembers(CurrentLobby), out var count)) yield break;
for (int i = 0; i < count; i++)
yield return SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i);
{
if (!TrySteamApi("EnumerateMembers.GetLobbyMemberByIndex", () => SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i), out var member)) yield break;
yield return member;
}
}
// 获取房间所有成员
@ -1480,14 +1648,15 @@ namespace TH1_Logic.Steam
{
if (!CurrentLobby.IsValid()) return;
_memberInfos.Clear();
int count = SteamMatchmaking.GetNumLobbyMembers(CurrentLobby);
if (!TrySteamApi("UpdateMemberInfo.GetNumLobbyMembers", () => SteamMatchmaking.GetNumLobbyMembers(CurrentLobby), out var count)) return;
for (int i = 0; i < count; i++)
{
var cSteamId = SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i);
if (!TrySteamApi("UpdateMemberInfo.GetLobbyMemberByIndex", () => SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i), out var cSteamId)) return;
if (!_membersCache.ContainsKey(cSteamId.m_SteamID)) _membersCache[cSteamId.m_SteamID] = cSteamId;
_memberInfos[cSteamId.m_SteamID] = new MemberInfo();
_memberInfos[cSteamId.m_SteamID].Id = cSteamId.m_SteamID;
_memberInfos[cSteamId.m_SteamID].Name = SteamFriends.GetFriendPersonaName(cSteamId);
if (!TrySteamApi("UpdateMemberInfo.GetFriendPersonaName", () => SteamFriends.GetFriendPersonaName(cSteamId), out var memberName)) return;
_memberInfos[cSteamId.m_SteamID].Name = memberName;
_memberInfos[cSteamId.m_SteamID].Texture = GetMemberAvatar(cSteamId.m_SteamID);
}
}
@ -1548,7 +1717,7 @@ namespace TH1_Logic.Steam
public int GetMemberCount()
{
if (!CurrentLobby.IsValid()) return 0;
return SteamMatchmaking.GetNumLobbyMembers(CurrentLobby);
return TrySteamApi("GetMemberCount.GetNumLobbyMembers", () => SteamMatchmaking.GetNumLobbyMembers(CurrentLobby), out var count) ? count : 0;
}
// 获取最大成员数
@ -1609,10 +1778,10 @@ namespace TH1_Logic.Steam
public bool IsMemberInLobby(ulong memberId)
{
if (!CurrentLobby.IsValid()) return false;
int count = SteamMatchmaking.GetNumLobbyMembers(CurrentLobby);
if (!TrySteamApi("IsMemberInLobby.GetNumLobbyMembers", () => SteamMatchmaking.GetNumLobbyMembers(CurrentLobby), out var count)) return false;
for (int i = 0; i < count; i++)
{
var cSteamId = SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i);
if (!TrySteamApi("IsMemberInLobby.GetLobbyMemberByIndex", () => SteamMatchmaking.GetLobbyMemberByIndex(CurrentLobby, i), out var cSteamId)) return false;
if (cSteamId.m_SteamID == memberId) return true;
}
return false;
@ -1628,15 +1797,15 @@ namespace TH1_Logic.Steam
// 获取自己的 memberId
public ulong GetSelfMemberId()
{
return SteamUser.GetSteamID().m_SteamID;
if (_selfID.IsValid()) return _selfID.m_SteamID;
return TrySteamApi("GetSelfMemberId.GetSteamID", SteamUser.GetSteamID, out var selfId) ? selfId.m_SteamID : 0;
}
// 获取房主的 memberId
public ulong GetLobbyOwnerId()
{
if (!CurrentLobby.IsValid()) return 0;
var owner = SteamMatchmaking.GetLobbyOwner(CurrentLobby);
return owner.m_SteamID;
return TrySteamApi("GetLobbyOwnerId.GetLobbyOwner", () => SteamMatchmaking.GetLobbyOwner(CurrentLobby), out var owner) ? owner.m_SteamID : 0;
}
// 获取当前房间状态
@ -1648,13 +1817,16 @@ namespace TH1_Logic.Steam
// 自己是否是房主
public bool IsLobbyOwner()
{
return IsInLobby() && SteamMatchmaking.GetLobbyOwner(CurrentLobby) == SteamUser.GetSteamID();
if (!IsInLobby()) return false;
if (!TrySteamApi("IsLobbyOwner.GetLobbyOwner", () => SteamMatchmaking.GetLobbyOwner(CurrentLobby), out var owner)) return false;
if (!TrySteamApi("IsLobbyOwner.GetSteamID", SteamUser.GetSteamID, out var selfId)) return false;
return owner == selfId;
}
// 自己是否在房间中
public bool IsInLobby()
{
return IsInitialized() && CurrentState == LobbyState.InLobby && CurrentLobby.IsValid();
return !_steamApiUnavailable && IsInitialized() && CurrentState == LobbyState.InLobby && CurrentLobby.IsValid();
}
// 获取自己的当前Steam在线状态 (如: 在线, 忙碌, 离开, 隐身, 离线)

View File

@ -323,6 +323,8 @@ namespace Logic
attackDmg = 0;
counterDmg = 0;
fragmentType = FragmentType.Attack;
if (mapData == null || unit1 == null || unit2 == null) return;
if (!unit1.IsValidOnMap(mapData) || !unit2.IsValidOnMap(mapData)) return;
if (!mapData.GetPlayerDataByUnitId(unit1.Id, out var player1)) return;
if (!mapData.GetPlayerDataByUnitId(unit2.Id, out var player2)) return;
if (!mapData.GetGridDataByUnitId(unit1.Id, out var grid1)) return;
@ -350,6 +352,14 @@ namespace Logic
// 攻击前生命周期
unit1.BeforeActiveAttackOther(mapData, unit1, unit2, out var tmpAddDmg);
if (!unit1.IsValidOnMap(mapData) || !unit2.IsValidOnMap(mapData)) return;
if (!mapData.GetPlayerDataByUnitId(unit1.Id, out player1)) return;
if (!mapData.GetPlayerDataByUnitId(unit2.Id, out player2)) return;
if (!mapData.GetGridDataByUnitId(unit1.Id, out grid1)) return;
if (!mapData.GetGridDataByUnitId(unit2.Id, out grid2)) return;
attackInfo.OriginPlayer = player1;
attackInfo.DamageOriginGrid = grid1;
attackInfo.DamageTargetGrid = grid2;
var attackDistance = Table.Instance.CalcDistance(grid1, grid2);
// 计算攻击伤害
@ -364,7 +374,8 @@ namespace Logic
//攻击会消耗所有类别的行动点数
unit1.ClearActionPoint();
Main.UnitLogic.DamageSettlement(mapData, unit1, unit2, dmg1, DamageType.ActiveAttack);
var activeSettlement = Main.UnitLogic.DamageSettlement(mapData, unit1, unit2, dmg1, DamageType.ActiveAttack);
if (activeSettlement == null) return;
if (!unit2.IsAlive())
{
attackInfo.IsKill = true;