diff --git a/logic/GameClass/GameObj/Map/Chest.cs b/logic/GameClass/GameObj/Map/Chest.cs index ddacde2..de67e89 100644 --- a/logic/GameClass/GameObj/Map/Chest.cs +++ b/logic/GameClass/GameObj/Map/Chest.cs @@ -20,9 +20,21 @@ namespace GameClass.GameObj public Gadget[] PropInChest => propInChest; private long openStartTime = 0; - public long OpenStartTime => openStartTime; + public long OpenStartTime + { + get + { + lock (gameObjLock) return openStartTime; + } + } private Character? whoOpen = null; - public Character? WhoOpen => whoOpen; + public Character? WhoOpen + { + get + { + lock (gameObjLock) return whoOpen; + } + } public bool Open(Character character) { lock (gameObjLock) diff --git a/logic/GameClass/GameObj/Map/Door.cs b/logic/GameClass/GameObj/Map/Door.cs index 0f03d72..8970e6c 100644 --- a/logic/GameClass/GameObj/Map/Door.cs +++ b/logic/GameClass/GameObj/Map/Door.cs @@ -47,15 +47,7 @@ namespace GameClass.GameObj } } - private int lockDegree = 0; - public int LockDegree - { - get => Interlocked.CompareExchange(ref lockDegree, -1, -1); - } - public int AddLockDegree(int add) - { - return Interlocked.Add(ref lockDegree, add); - } + public AtomicInt LockDegree { get; } = new AtomicInt(0); private long openStartTime = 0; public long OpenStartTime @@ -106,7 +98,7 @@ namespace GameClass.GameObj { if (!isOpen) return false; if (whoLockOrOpen != null) return false; - Interlocked.Exchange(ref lockDegree, 0); + LockDegree.Set(0); whoLockOrOpen = character; return true; } diff --git a/logic/GameClass/GameObj/Map/Doorway.cs b/logic/GameClass/GameObj/Map/Doorway.cs index d5e70ca..82ac6be 100644 --- a/logic/GameClass/GameObj/Map/Doorway.cs +++ b/logic/GameClass/GameObj/Map/Doorway.cs @@ -23,20 +23,7 @@ namespace GameClass.GameObj return false; } - private bool powerSupply = false; - public bool PowerSupply - { - get - { - lock (gameObjLock) - return powerSupply; - } - set - { - lock (gameObjLock) - powerSupply = value; - } - } + public AtomicBool PowerSupply { get; } = new(false); private long openStartTime = 0; public long OpenStartTime @@ -49,9 +36,10 @@ namespace GameClass.GameObj } public bool TryToOpen() { + if (!PowerSupply) return false; lock (gameObjLock) { - if (!powerSupply || openStartTime > 0) return false; + if (openStartTime > 0) return false; openStartTime = Environment.TickCount64; return true; } diff --git a/logic/GameClass/GameObj/Map/EmergencyExit.cs b/logic/GameClass/GameObj/Map/EmergencyExit.cs index 5e51daf..f121391 100644 --- a/logic/GameClass/GameObj/Map/EmergencyExit.cs +++ b/logic/GameClass/GameObj/Map/EmergencyExit.cs @@ -17,24 +17,14 @@ namespace GameClass.GameObj public override bool IgnoreCollideExecutor(IGameObj targetObj) { - if (!canOpen) return true; + if (!CanOpen) return true; if (!IsOpen) return false; if (targetObj.Type != GameObjType.Character) return true; // 非玩家不碰撞 return false; } - - private bool canOpen = false; - public bool CanOpen - { - get => canOpen; - set - { - lock (gameObjLock) - canOpen = value; - } - } + public AtomicBool CanOpen { get; } = new(false); private bool isOpen = false; public bool IsOpen diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs index 775681a..af3d9b1 100644 --- a/logic/GameClass/GameObj/Map/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -26,7 +26,7 @@ namespace GameClass.GameObj { Random r = new Random(Environment.TickCount); EmergencyExit emergencyExit = (EmergencyExit)(GameObjDict[GameObjType.EmergencyExit][r.Next(0, GameObjDict[GameObjType.EmergencyExit].Count)]); - emergencyExit.CanOpen = true; + emergencyExit.CanOpen.Set(true); Preparation.Utility.Debugger.Output(emergencyExit, emergencyExit.Position.ToString()); } finally @@ -41,7 +41,7 @@ namespace GameClass.GameObj try { foreach (Doorway doorway in GameObjDict[GameObjType.Doorway]) - doorway.PowerSupply = true; + doorway.PowerSupply.Set(true); } finally { diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index b527ef3..e2961b6 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -513,7 +513,7 @@ namespace Gaming { if ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) != null) return false; - if (doorToLock.AddLockDegree(GameData.checkInterval * player.SpeedOfOpeningOrLocking) >= GameData.basicSpeedOfOpeningOrLocking) + if (doorToLock.LockDegree.Add(GameData.checkInterval * player.SpeedOfOpeningOrLocking) >= GameData.basicSpeedOfOpeningOrLocking) return false; return true; }, diff --git a/logic/Gaming/CharacterManager.cs b/logic/Gaming/CharacterManager.cs index 4049841..86c8fef 100644 --- a/logic/Gaming/CharacterManager.cs +++ b/logic/Gaming/CharacterManager.cs @@ -311,7 +311,7 @@ namespace Gaming if (student.CharacterType == CharacterType.StraightAStudent) { - ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation = 0; + ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation.Set(0); } student.SetDegreeOfTreatment0(); diff --git a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs index b049b2d..c4f4aa5 100644 --- a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs @@ -227,7 +227,7 @@ namespace Gaming if (generator.Repair(((WriteAnswers)activeSkill).DegreeOfMeditation, player)) gameMap.AddNumOfRepairedGenerators(); Debugger.Output(player, "uses WriteAnswers in" + generator.ToString() + "with " + (((WriteAnswers)activeSkill).DegreeOfMeditation).ToString()); - ((WriteAnswers)activeSkill).DegreeOfMeditation = 0; + ((WriteAnswers)activeSkill).DegreeOfMeditation.Set(0); } }, () => diff --git a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs index 3cf26ab..b22254e 100644 --- a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs @@ -25,8 +25,8 @@ namespace Gaming // 被动技能开局时就释放,持续到游戏结束 () => gameMap.Timer.IsGaming && !player.IsRemoved, () => { - if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation += learningDegree * GameData.frameDuration; - else activeSkill.DegreeOfMeditation = 0; + if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation.Add(learningDegree * GameData.frameDuration); + else activeSkill.DegreeOfMeditation.Set(0); //Debugger.Output(player, "with " + (((WriteAnswers)activeSkill).DegreeOfMeditation).ToString()); }, timeInterval: GameData.frameDuration, diff --git a/logic/Preparation/Interface/ISkill.cs b/logic/Preparation/Interface/ISkill.cs index 36c50a3..bc3f2d6 100644 --- a/logic/Preparation/Interface/ISkill.cs +++ b/logic/Preparation/Interface/ISkill.cs @@ -174,12 +174,7 @@ namespace Preparation.Interface public override int SkillCD => GameData.commonSkillCD; public override int DurationTime => 0; - private int degreeOfMeditation = 0; - public int DegreeOfMeditation - { - get => Interlocked.CompareExchange(ref degreeOfMeditation, 0, 0); - set => Interlocked.Exchange(ref degreeOfMeditation, value); - } + public AtomicInt DegreeOfMeditation { get; } = new(0); } public class SummonGolem : ActiveSkill diff --git a/logic/Preparation/Utility/LockedValue.cs b/logic/Preparation/Utility/LockedValue.cs deleted file mode 100644 index 7d6cc2b..0000000 --- a/logic/Preparation/Utility/LockedValue.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading; - -namespace Preparation.Utility -{ - //理论上结构体最好不可变,这里采用了可变结构。 - public struct AtomicInt - { - private int v; - public AtomicInt(int x) - { - v = x; - } - public override string ToString() => Interlocked.CompareExchange(ref v, -1, -1).ToString(); - public int Get() => Interlocked.CompareExchange(ref v, -1, -1); - public static implicit operator int(AtomicInt aint) => Interlocked.CompareExchange(ref aint.v, -1, -1); - public int Set(int value) => Interlocked.Exchange(ref v, value); - - public int Add(int x) => Interlocked.Add(ref v, x); - public int Sub(int x) => Interlocked.Add(ref v, -x); - public int Inc() => Interlocked.Increment(ref v); - public int Dec() => Interlocked.Decrement(ref v); - - public void CompareExchange(int b, int c) => Interlocked.CompareExchange(ref v, b, c); - /// 返回操作前的值 - public int CompareExReturnOri(int b, int c) => Interlocked.CompareExchange(ref v, b, c); - } - public struct AtomicBool - { - private int v;//v==0为false,v!=0(v==1或v==-1)为true - public AtomicBool(bool x) - { - v = x ? 1 : 0; - } - public override string ToString() => (Interlocked.CompareExchange(ref v, -1, -1) == 0) ? "false" : "true"; - public bool Get() => (Interlocked.CompareExchange(ref v, -1, -1) != 0); - public static implicit operator bool(AtomicBool abool) => (Interlocked.CompareExchange(ref abool.v, -1, -1) != 0); - - public bool Set(bool value) => (Interlocked.Exchange(ref v, value ? 1 : 0) != 0); - - /// 赋值前的值是否与将赋予的值不相同 - public bool TrySet(bool value) - { - int ori = Interlocked.CompareExchange(ref v, value ? 1 : 0, value ? 1 : 0); - return value ? (ori == 0) : (ori != 0); - } - - public bool Invert() => Interlocked.Add(ref v, -1) != 0; - public bool And(bool x) => Interlocked.And(ref v, x ? 1 : 0) != 0; - public bool Or(bool x) => Interlocked.Or(ref v, x ? 1 : 0) != 0; - } -} diff --git a/logic/Preparation/Utility/SafeValue.cs b/logic/Preparation/Utility/SafeValue.cs new file mode 100644 index 0000000..90b1f26 --- /dev/null +++ b/logic/Preparation/Utility/SafeValue.cs @@ -0,0 +1,99 @@ +using System; +using System.Threading; + +namespace Preparation.Utility +{ + //理论上结构体最好不可变,这里采用了可变结构。 + //其对应属性不应当有set访问器,避免不安全的=赋值 + public struct AtomicInt + { + private int v; + public AtomicInt(int x) + { + v = x; + } + public override string ToString() => Interlocked.CompareExchange(ref v, -1, -1).ToString(); + public int Get() => Interlocked.CompareExchange(ref v, -1, -1); + public static implicit operator int(AtomicInt aint) => Interlocked.CompareExchange(ref aint.v, -1, -1); + public int Set(int value) => Interlocked.Exchange(ref v, value); + + public int Add(int x) => Interlocked.Add(ref v, x); + public int Sub(int x) => Interlocked.Add(ref v, -x); + public int Inc() => Interlocked.Increment(ref v); + public int Dec() => Interlocked.Decrement(ref v); + + public void CompareExchange(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + /// 返回操作前的值 + public int CompareExReturnOri(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + public struct AtomicBool + { + private int v;//v==0为false,v!=0(v==1或v==-1)为true + public AtomicBool(bool x) + { + v = x ? 1 : 0; + } + public override string ToString() => (Interlocked.CompareExchange(ref v, -2, -2) == 0) ? "false" : "true"; + public bool Get() => (Interlocked.CompareExchange(ref v, -1, -1) != 0); + public static implicit operator bool(AtomicBool abool) => (Interlocked.CompareExchange(ref abool.v, -1, -1) != 0); + + public bool Set(bool value) => (Interlocked.Exchange(ref v, value ? 1 : 0) != 0); + + /// 赋值前的值是否与将赋予的值不相同 + public bool TrySet(bool value) + { + return (Interlocked.CompareExchange(ref v, value ? 1 : 0, value ? 0 : 1) ^ (value ? 1 : 0)) != 0; + } + + public bool Invert() => Interlocked.Add(ref v, -1) != 0; + public bool And(bool x) => Interlocked.And(ref v, x ? 1 : 0) != 0; + public bool Or(bool x) => Interlocked.Or(ref v, x ? 1 : 0) != 0; + } + public struct IntProgressContinuously + { + private long endT = long.MaxValue; + private long needT; + + public IntProgressContinuously(long needTime) + { + this.needT = needTime; + } + public long GetEndTime() => Interlocked.CompareExchange(ref endT, -2, -2); + public long GetNeedTime() => Interlocked.CompareExchange(ref needT, -2, -2); + public override string ToString() => "EndTime:" + Interlocked.CompareExchange(ref endT, -2, -2).ToString() + " ms, NeedTime:" + Interlocked.CompareExchange(ref needT, -2, -2).ToString() + " ms"; + public long GetProgress() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + } + public double GetProgressDouble() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return 1; + return 1.0 - ((double)cutime / Interlocked.CompareExchange(ref needT, -2, -2)); + } + + public bool Start(long needTime) + { + //规定只有Start可以修改needT,且需要先访问endTime,从而避免锁(某种程度上endTime可以认为是needTime的锁) + if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; + if (needTime <= 2) Debugger.Output("Warning:the field of IntProgressContinuously is " + needTime.ToString() + ",which is too small."); + Interlocked.Exchange(ref this.needT, needTime); + return true; + } + public bool Start() + { + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (Interlocked.CompareExchange(ref endT, Environment.TickCount64 + needTime, long.MaxValue) != long.MaxValue) return false; + return true; + } + public void Set0() => Interlocked.Exchange(ref endT, long.MaxValue); + public void TryStop() + { + if (Environment.TickCount64 < Interlocked.CompareExchange(ref endT, -2, -2)) + Interlocked.Exchange(ref endT, long.MaxValue); + } + //增加新的写操作可能导致不安全 + } +}