Add anti spam quick bar
This commit is contained in:
@@ -17,6 +17,70 @@ namespace BrewMonster
|
||||
{
|
||||
public partial class CECHostPlayer
|
||||
{
|
||||
/// <summary>
|
||||
/// Anti-spam gate for <see cref="ApplySkillShortcut"/> (client-side input flood control).
|
||||
/// <para>
|
||||
/// Purpose:
|
||||
/// - Prevents rapid repeated shortcut-trigger calls (mouse/touch spam, key repeat, UI double-fire)
|
||||
/// from spamming trace/cast requests and causing unstable client behavior.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Design:
|
||||
/// - A small global minimum interval (ANY) blocks ultra-high-frequency bursts across all skills.
|
||||
/// - A per-skill minimum interval (PER_SKILL) blocks repeatedly pressing the same skill rapidly.
|
||||
/// - Exception: "press again to release a charging skill immediately" is allowed (not blocked),
|
||||
/// so charge mechanics remain responsive.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Notes:
|
||||
/// - Uses <see cref="Time.unscaledTime"/> so throttling remains consistent under timeScale changes.
|
||||
/// - This is client-side hygiene only; server cooldown/validation is still authoritative.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// ApplySkillShortcut 的反刷 (客户端输入洪泛控制)
|
||||
///
|
||||
/// 目的:
|
||||
/// - 防止鼠标/触摸狂点、按键连发、UI 重复触发导致的频繁施法请求,避免客户端不稳定。
|
||||
///
|
||||
/// 设计:
|
||||
/// - ANY:对所有技能共用的极短间隔,过滤超高频 burst。
|
||||
/// - PER_SKILL:对同一技能的短间隔,过滤同技能连点。
|
||||
/// - 例外:充能技能“再次按下立即释放”不会被拦截,保证充能手感。
|
||||
///
|
||||
/// 备注:
|
||||
/// - 使用 Time.unscaledTime,避免 timeScale 变化导致反刷失效。
|
||||
/// - 仅客户端防抖/限流,服务器校验与冷却依然为准。
|
||||
/// </remarks>
|
||||
private const float APPLY_SKILL_SHORTCUT_MIN_INTERVAL_ANY = 0.05f;
|
||||
private const float APPLY_SKILL_SHORTCUT_MIN_INTERVAL_PER_SKILL = 0.12f;
|
||||
|
||||
private float _applySkillShortcut_lastAnyTime = -999f;
|
||||
private readonly Dictionary<int, float> _applySkillShortcut_lastSkillTime = new Dictionary<int, float>(64);
|
||||
|
||||
private bool ShouldBlockApplySkillShortcutSpam(int idSkill, bool allowChargeRelease)
|
||||
{
|
||||
// Use unscaled time so spam protection still works during slow-mo/timeScale changes.
|
||||
// 使用 unscaledTime,避免 timeScale 变化导致反刷失效
|
||||
float now = Time.unscaledTime;
|
||||
|
||||
if (now - _applySkillShortcut_lastAnyTime < APPLY_SKILL_SHORTCUT_MIN_INTERVAL_ANY)
|
||||
return true;
|
||||
|
||||
if (!allowChargeRelease)
|
||||
{
|
||||
if (_applySkillShortcut_lastSkillTime.TryGetValue(idSkill, out float lastSkillTime))
|
||||
{
|
||||
if (now - lastSkillTime < APPLY_SKILL_SHORTCUT_MIN_INTERVAL_PER_SKILL)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
_applySkillShortcut_lastAnyTime = now;
|
||||
_applySkillShortcut_lastSkillTime[idSkill] = now;
|
||||
return false;
|
||||
}
|
||||
|
||||
public struct SkillShortCutConfig
|
||||
{
|
||||
public int setNum;
|
||||
@@ -506,8 +570,15 @@ namespace BrewMonster
|
||||
|
||||
//// If we press a chargeable skill again when it's being charged,
|
||||
//// we cast it out at once
|
||||
if (IsSpellingMagic() && m_pCurSkill != null && m_pCurSkill.IsCharging() &&
|
||||
m_pCurSkill.GetSkillID() == pSkill.GetSkillID())
|
||||
bool allowChargeRelease = IsSpellingMagic() && m_pCurSkill != null && m_pCurSkill.IsCharging() &&
|
||||
m_pCurSkill.GetSkillID() == pSkill.GetSkillID();
|
||||
|
||||
// Anti-spam: block rapid-fire shortcut calls (except charge-release press)
|
||||
// 反刷:阻止短时间内狂点快捷键(充能释放第二次按下除外)
|
||||
if (ShouldBlockApplySkillShortcutSpam(idSkill, allowChargeRelease))
|
||||
return false;
|
||||
|
||||
if (allowChargeRelease)
|
||||
{
|
||||
m_pCurSkill.EndCharging();
|
||||
UnityGameSession.c2s_SendCmdContinueAction();
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# ApplySkillShortcut Anti-spam
|
||||
|
||||
## What it is
|
||||
`CECHostPlayer.ApplySkillShortcut(...)` has a small client-side anti-spam gate to prevent input floods (touch/mouse spam, key repeat, UI double-fire) from generating excessive trace/cast work.
|
||||
|
||||
This is **not** a replacement for server cooldowns or validation. It’s client-side hygiene to keep the client stable and reduce redundant requests.
|
||||
|
||||
## How it works
|
||||
The gate is implemented inside `Assets/Scripts/CECHostPlayer.Skill.cs`:
|
||||
|
||||
- **Global minimum interval (ANY)**: blocks ultra-high-frequency bursts across *all* skills.
|
||||
- **Per-skill minimum interval (PER_SKILL)**: blocks rapidly pressing the *same* skill repeatedly.
|
||||
- **Charge exception**: if the player is charging a skill and presses the **same** skill again to release immediately, that press is **allowed** (so charging remains responsive).
|
||||
|
||||
The timing source is `Time.unscaledTime` so the throttle stays consistent even if `Time.timeScale` changes.
|
||||
|
||||
## Tunables
|
||||
In `CECHostPlayer.Skill.cs`:
|
||||
|
||||
- `APPLY_SKILL_SHORTCUT_MIN_INTERVAL_ANY` (default `0.05s`)
|
||||
- `APPLY_SKILL_SHORTCUT_MIN_INTERVAL_PER_SKILL` (default `0.12s`)
|
||||
|
||||
If you need to tweak feel:
|
||||
|
||||
- Lower **ANY** carefully; it protects against accidental double-fire from UI/input systems.
|
||||
- Lower **PER_SKILL** if skills feel “eaten” during fast tapping, but keep it high enough to block spam.
|
||||
|
||||
## Why the charge exception exists
|
||||
Chargeable skills commonly use a second press to “cast now”. Blocking that second press makes charging feel broken, so the anti-spam explicitly does not block it.
|
||||
|
||||
Reference in New Issue
Block a user