Browse Source

refactor: 🎨 use the LockedClassList

dev
shangfengh 1 year ago
parent
commit
a0e1728494
10 changed files with 269 additions and 604 deletions
  1. +49
    -236
      logic/GameClass/GameObj/Map/Map.cs
  2. +59
    -80
      logic/GameEngine/CollisionChecker.cs
  3. +5
    -14
      logic/Gaming/AttackManager.cs
  4. +36
    -60
      logic/Gaming/CharacterManager.cs
  5. +7
    -31
      logic/Gaming/Game.cs
  6. +32
    -48
      logic/Gaming/PropManager.cs
  7. +51
    -106
      logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs
  8. +3
    -3
      logic/Preparation/Interface/IMap.cs
  9. +22
    -4
      logic/Preparation/Utility/SafeValue/ListLocked.cs
  10. +5
    -22
      logic/Server/GameServer.cs

+ 49
- 236
logic/GameClass/GameObj/Map/Map.cs View File

@@ -3,6 +3,7 @@ using System.Threading;
using Preparation.Interface;
using Preparation.Utility;
using System;
using System.Collections.Concurrent;

namespace GameClass.GameObj
{
@@ -21,32 +22,18 @@ namespace GameClass.GameObj
uint value = Interlocked.Increment(ref numOfRepairedGenerators);
if (value == GameData.numOfGeneratorRequiredForEmergencyExit)
{
GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock();
try
{
Random r = new(Environment.TickCount);
EmergencyExit emergencyExit = (EmergencyExit)(GameObjDict[GameObjType.EmergencyExit][r.Next(0, GameObjDict[GameObjType.EmergencyExit].Count)]);
emergencyExit.CanOpen.SetReturnOri(true);
Preparation.Utility.Debugger.Output(emergencyExit, emergencyExit.Position.ToString());
}
finally
{
GameObjLockDict[GameObjType.EmergencyExit].ExitReadLock();
}
Random r = new(Environment.TickCount);
EmergencyExit emergencyExit = (EmergencyExit)(GameObjDict[GameObjType.EmergencyExit][r.Next(0, GameObjDict[GameObjType.EmergencyExit].Count)]);
emergencyExit.CanOpen.SetReturnOri(true);
Preparation.Utility.Debugger.Output(emergencyExit, emergencyExit.Position.ToString());
}
else
if (value == GameData.numOfGeneratorRequiredForRepair)
{
GameObjLockDict[GameObjType.Doorway].EnterReadLock();
try
GameObjDict[GameObjType.Doorway].ForEach(delegate (IGameObj doorway)
{
foreach (Doorway doorway in GameObjDict[GameObjType.Doorway])
doorway.PowerSupply.SetReturnOri(true);
}
finally
{
GameObjLockDict[GameObjType.Doorway].ExitReadLock();
}
((Doorway)doorway).PowerSupply.SetReturnOri(true);
});
}
}

@@ -113,20 +100,10 @@ namespace GameClass.GameObj

private void OpenEmergencyExit()
{
GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock();
try
{
foreach (EmergencyExit emergencyExit in GameObjDict[GameObjType.EmergencyExit])
if (emergencyExit.CanOpen)
{
emergencyExit.IsOpen = true;
break;
}
}
finally
{
GameObjLockDict[GameObjType.EmergencyExit].ExitReadLock();
}
EmergencyExit? emergencyExit =
(EmergencyExit?)GameObjDict[GameObjType.EmergencyExit].Find(gameObj => ((EmergencyExit)gameObj).CanOpen);
if (emergencyExit != null)
emergencyExit.IsOpen = true;
}
private void AddScoreFromAddict()
{
@@ -134,10 +111,8 @@ namespace GameClass.GameObj
}


private Dictionary<GameObjType, IList<IGameObj>> gameObjDict;
public Dictionary<GameObjType, IList<IGameObj>> GameObjDict => gameObjDict;
private Dictionary<GameObjType, ReaderWriterLockSlim> gameObjLockDict;
public Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict => gameObjLockDict;
private Dictionary<GameObjType, LockedClassList<IGameObj>> gameObjDict;
public Dictionary<GameObjType, LockedClassList<IGameObj>> GameObjDict => gameObjDict;

public readonly uint[,] protoGameMap;
public uint[,] ProtoGameMap => protoGameMap;
@@ -175,182 +150,55 @@ namespace GameClass.GameObj

public Character? FindPlayerInID(long playerID)
{
Character? player = null;
gameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character person in gameObjDict[GameObjType.Character])
{
if (playerID == person.ID)
{
player = person;
break;
}
}
}
finally
{
gameObjLockDict[GameObjType.Character].ExitReadLock();
}
return player;
return (Character?)GameObjDict[GameObjType.Character].Find(gameObj => (playerID == ((Character)gameObj).ID));
}
public Character? FindPlayerInPlayerID(long playerID)
{
Character? player = null;
gameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character person in gameObjDict[GameObjType.Character])
{
if (playerID == person.PlayerID)
{
player = person;
break;
}
}
}
finally
{
gameObjLockDict[GameObjType.Character].ExitReadLock();
}
return player;
return (Character?)GameObjDict[GameObjType.Character].Find(gameObj => (playerID == ((Character)gameObj).PlayerID));
}
public Ghost? ghost = null;
public Character? FindPlayerToAction(long playerID)
{
Character? player = null;
gameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character person in gameObjDict[GameObjType.Character])
{
if (playerID == person.ID)
{
if (person.CharacterType == CharacterType.TechOtaku)
{
foreach (Character character in gameObjDict[GameObjType.Character])
{
if (((UseRobot)person.FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID == character.PlayerID)
{
player = character;
break;
}
}
}
else player = person;
break;
}
}
}
finally
{
gameObjLockDict[GameObjType.Character].ExitReadLock();
}

Character? player = (Character?)GameObjDict[GameObjType.Character].Find(gameObj => (playerID == ((Character)gameObj).ID));
if (player == null) return null;
if (player.CharacterType == CharacterType.TechOtaku)
player = (Character?)GameObjDict[GameObjType.Character].Find(
gameObj => (
((UseRobot)player.FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID == ((Character)gameObj).PlayerID
)
);
return player;
}

public GameObj? OneForInteract(XY Pos, GameObjType gameObjType)
{
GameObj? GameObjForInteract = null;
GameObjLockDict[gameObjType].EnterReadLock();
try
{
foreach (GameObj gameObj in GameObjDict[gameObjType])
{
if (GameData.ApproachToInteract(gameObj.Position, Pos))
{
GameObjForInteract = gameObj;
break;
}
}
}
finally
{
GameObjLockDict[gameObjType].ExitReadLock();
}
return GameObjForInteract;
return (GameObj?)GameObjDict[gameObjType].Find(gameObj => GameData.ApproachToInteract(gameObj.Position, Pos));
}
public Student? StudentForInteract(Student AStudent)
{
GameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character character in GameObjDict[GameObjType.Character])
{
if (!character.IsGhost() && character != AStudent && GameData.ApproachToInteract(character.Position, AStudent.Position))
{
return (Student)character;
}
}
}
finally
return (Student?)GameObjDict[GameObjType.Character].Find(gameObj =>
{
GameObjLockDict[GameObjType.Character].ExitReadLock();
}
return null;
Character character = (Character)gameObj;
return !character.IsGhost() && character != AStudent && GameData.ApproachToInteract(character.Position, AStudent.Position);
});
}
public GameObj? OneInTheSameCell(XY Pos, GameObjType gameObjType)
{
GameObj? GameObjForInteract = null;
GameObjLockDict[gameObjType].EnterReadLock();
try
{
foreach (GameObj gameObj in GameObjDict[gameObjType])
{
if (GameData.IsInTheSameCell(gameObj.Position, Pos))
{
GameObjForInteract = gameObj;
break;
}
}
}
finally
{
GameObjLockDict[gameObjType].ExitReadLock();
}
return GameObjForInteract;
return (GameObj?)GameObjDict[gameObjType].Find(gameObj =>
GameData.IsInTheSameCell(gameObj.Position, Pos)
);
}
public GameObj? PartInTheSameCell(XY Pos, GameObjType gameObjType)
{
GameObj? GameObjForInteract = null;
GameObjLockDict[gameObjType].EnterReadLock();
try
{
foreach (GameObj gameObj in GameObjDict[gameObjType])
{
if (GameData.PartInTheSameCell(gameObj.Position, Pos))
{
GameObjForInteract = gameObj;
break;
}
}
}
finally
{
GameObjLockDict[gameObjType].ExitReadLock();
}
return GameObjForInteract;
return (GameObj?)GameObjDict[gameObjType].Find(gameObj =>
GameData.PartInTheSameCell(gameObj.Position, Pos)
);
}
public GameObj? OneForInteractInACross(XY Pos, GameObjType gameObjType)
{
GameObj? GameObjForInteract = null;
GameObjLockDict[gameObjType].EnterReadLock();
try
{
foreach (GameObj gameObj in GameObjDict[gameObjType])
{
if (GameData.ApproachToInteractInACross(gameObj.Position, Pos))
{
GameObjForInteract = gameObj;
break;
}
}
}
finally
{
GameObjLockDict[gameObjType].ExitReadLock();
}
return GameObjForInteract;
return (GameObj?)GameObjDict[gameObjType].Find(gameObj =>
GameData.ApproachToInteractInACross(gameObj.Position, Pos));
}

public bool CanSee(Character player, GameObj gameObj)
@@ -403,70 +251,35 @@ namespace GameClass.GameObj

public bool Remove(GameObj gameObj)
{
GameObj? ToDel = null;
GameObjLockDict[gameObj.Type].EnterWriteLock();
try
if (GameObjDict[gameObj.Type].RemoveOne(obj => gameObj.ID == obj.ID))
{
foreach (GameObj obj in GameObjDict[gameObj.Type])
{
if (gameObj.ID == obj.ID)
{
ToDel = obj;
break;
}
}
if (ToDel != null)
{
GameObjDict[gameObj.Type].Remove(ToDel);
ToDel.TryToRemove();
}
}
finally
{
GameObjLockDict[gameObj.Type].ExitWriteLock();
gameObj.TryToRemove();
return true;
}
return ToDel != null;
return false;
}
public bool RemoveJustFromMap(GameObj gameObj)
{
GameObjLockDict[gameObj.Type].EnterWriteLock();
try
if (GameObjDict[gameObj.Type].Remove(gameObj))
{
if (GameObjDict[gameObj.Type].Remove(gameObj))
{
gameObj.TryToRemove();
return true;
}
return false;
}
finally
{
GameObjLockDict[gameObj.Type].ExitWriteLock();
gameObj.TryToRemove();
return true;
}
return false;
}
public void Add(GameObj gameObj)
{
GameObjLockDict[gameObj.Type].EnterWriteLock();
try
{
GameObjDict[gameObj.Type].Add(gameObj);
}
finally
{
GameObjLockDict[gameObj.Type].ExitWriteLock();
}
GameObjDict[gameObj.Type].Add(gameObj);
}

public Map(uint[,] mapResource)
{
gameObjDict = new Dictionary<GameObjType, IList<IGameObj>>();
gameObjLockDict = new Dictionary<GameObjType, ReaderWriterLockSlim>();
gameObjDict = new Dictionary<GameObjType, LockedClassList<IGameObj>>();
foreach (GameObjType idx in Enum.GetValues(typeof(GameObjType)))
{
if (idx != GameObjType.Null)
{
gameObjDict.Add(idx, new List<IGameObj>());
gameObjLockDict.Add(idx, new ReaderWriterLockSlim());
gameObjDict.TryAdd(idx, new LockedClassList<IGameObj>());
}
}



+ 59
- 80
logic/GameEngine/CollisionChecker.cs View File

@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Preparation.Interface;
using Preparation.Utility;
@@ -10,33 +11,25 @@ namespace GameEngine
public IGameObj? CheckCollision(IMoveable obj, XY Pos)
{
// 在列表中检查碰撞
Func<IEnumerable<IGameObj>, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList =
(IEnumerable<IGameObj> lst, ReaderWriterLockSlim listLock) =>
Func<LockedClassList<IGameObj>, IGameObj?> CheckCollisionInList =
(LockedClassList<IGameObj> lst) =>
{
IGameObj? collisionObj = null;
listLock.EnterReadLock();
try
foreach (IGameObj listObj in lst)
{
foreach (var listObj in lst)
if (obj.WillCollideWith(listObj, Pos))
{
if (obj.WillCollideWith(listObj, Pos))
{
collisionObj = listObj;
break;
}
collisionObj = listObj;
break;
}
}
finally
{
listLock.ExitReadLock();
}
return collisionObj;
};

IGameObj? collisionObj;
foreach (var list in lists)
{
if ((collisionObj = CheckCollisionInList(list.Item1, list.Item2)) != null)
if ((collisionObj = CheckCollisionInList(list)) != null)
{
return collisionObj;
}
@@ -127,100 +120,86 @@ namespace GameEngine
double maxDistance = uint.MaxValue;
foreach (var listWithLock in lists)
{
var lst = listWithLock.Item1;
var listLock = listWithLock.Item2;
listLock.EnterReadLock();
try
var lst = listWithLock;
foreach (IGameObj listObj in lst)
{
foreach (IGameObj listObj in lst)
// 如果再走一步发生碰撞
if (obj.WillCollideWith(listObj, nextPos))
{
// 如果再走一步发生碰撞
if (obj.WillCollideWith(listObj, nextPos))
{
switch (listObj.Shape) // 默认obj为圆形
{
switch (listObj.Shape) // 默认obj为圆形
{
case ShapeType.Circle:
{
// 计算两者之间的距离
double mod = XY.DistanceFloor3(listObj.Position, obj.Position);
int orgDeltaX = listObj.Position.x - obj.Position.x;
int orgDeltaY = listObj.Position.y - obj.Position.y;
case ShapeType.Circle:
{
// 计算两者之间的距离
double mod = XY.DistanceFloor3(listObj.Position, obj.Position);
int orgDeltaX = listObj.Position.x - obj.Position.x;
int orgDeltaY = listObj.Position.y - obj.Position.y;

if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠
if (mod < listObj.Radius + obj.Radius) // 如果两者已经重叠
{
tmpMax = 0;
}
else
{
double tmp = mod - obj.Radius - listObj.Radius;
// 计算能走的最长距离,好像这么算有一点误差?
tmp = ((int)(tmp * 1000 / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.Angle())));
if (tmp < 0 || tmp > uint.MaxValue || double.IsNaN(tmp))
{
tmpMax = 0;
tmpMax = uint.MaxValue;
}
else
{
double tmp = mod - obj.Radius - listObj.Radius;
// 计算能走的最长距离,好像这么算有一点误差?
tmp = ((int)(tmp * 1000 / Math.Cos(Math.Atan2(orgDeltaY, orgDeltaX) - moveVec.Angle())));
if (tmp < 0 || tmp > uint.MaxValue || double.IsNaN(tmp))
{
tmpMax = uint.MaxValue;
}
else
tmpMax = tmp / 1000.0;
}
break;
tmpMax = tmp / 1000.0;
}
case ShapeType.Square:
break;
}
case ShapeType.Square:
{
// if (obj.WillCollideWith(listObj, obj.Position))
// tmpMax = 0;
// else tmpMax = MaxMoveToSquare(obj, listObj);
// break;
if (obj.WillCollideWith(listObj, obj.Position))
tmpMax = 0;
else
{
// if (obj.WillCollideWith(listObj, obj.Position))
// tmpMax = 0;
// else tmpMax = MaxMoveToSquare(obj, listObj);
// break;
if (obj.WillCollideWith(listObj, obj.Position))
tmpMax = 0;
else
// 二分查找最大可能移动距离
int left = 0, right = (int)moveVec.Length();
while (left < right - 1)
{
// 二分查找最大可能移动距离
int left = 0, right = (int)moveVec.Length();
while (left < right - 1)
int mid = (right - left) / 2 + left;
if (obj.WillCollideWith(listObj, obj.Position + new XY(moveVec, mid)))
{
int mid = (right - left) / 2 + left;
if (obj.WillCollideWith(listObj, obj.Position + new XY(moveVec, mid)))
{
right = mid;
}
else
left = mid;
right = mid;
}
tmpMax = (uint)left;
else
left = mid;
}
break;
tmpMax = (uint)left;
}
default:
tmpMax = uint.MaxValue;
break;
}
if (tmpMax < maxDistance)
maxDistance = tmpMax;
}
default:
tmpMax = uint.MaxValue;
break;
}
if (tmpMax < maxDistance)
maxDistance = tmpMax;
}
}
}
finally
{
listLock.ExitReadLock();
}
}
return maxDistance;
}

readonly IMap gameMap;
private readonly Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>[] lists;
private readonly LockedClassList<IGameObj>[] lists;

public CollisionChecker(IMap gameMap)
{
this.gameMap = gameMap;
lists = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>[gameMap.GameObjDict.Count];
int i = 0;
foreach (var keyValuePair in gameMap.GameObjDict)
{
lists[i++] = new Tuple<IEnumerable<IGameObj>, ReaderWriterLockSlim>(keyValuePair.Value as IList<IGameObj>, gameMap.GameObjLockDict[keyValuePair.Key]);
}
lists = gameMap.GameObjDict.Values.ToArray();
}
}
}

+ 5
- 14
logic/Gaming/AttackManager.cs View File

@@ -168,20 +168,11 @@ namespace Gaming
{
if (bullet.CanBeBombed(kvp.Key))
{
gameMap.GameObjLockDict[kvp.Key].EnterReadLock();
try
{
foreach (var item in gameMap.GameObjDict[kvp.Key])
if (bullet.CanAttack((GameObj)item))
{
beAttackedList.Add(item);
}

}
finally
{
gameMap.GameObjLockDict[kvp.Key].ExitReadLock();
}
foreach (var item in gameMap.GameObjDict[kvp.Key])
if (bullet.CanAttack((GameObj)item))
{
beAttackedList.Add((IGameObj)item);
}
}
}



+ 36
- 60
logic/Gaming/CharacterManager.cs View File

@@ -71,96 +71,72 @@ namespace Gaming
bool noise = false;
if (!newPlayer.IsGhost())
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
if (person.IsGhost())
{
if (person.IsGhost())
if (person.CharacterType == CharacterType.ANoisyPerson)
{
if (person.CharacterType == CharacterType.ANoisyPerson)
{
noise = true;
newPlayer.AddBgm(BgmType.GhostIsComing, 1411180);
newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, 154991);
}
noise = true;
newPlayer.AddBgm(BgmType.GhostIsComing, 1411180);
newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, 154991);
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
}
new FrameRateTaskExecutor<int>(
loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved,
loopToDo: () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
if (newPlayer.IsGhost())
{
if (newPlayer.IsGhost())
double bgmVolume = 0;
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
double bgmVolume = 0;
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
if (!person.IsGhost() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
{
if (!person.IsGhost() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
{
if ((double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position) > bgmVolume)
bgmVolume = newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position);
}
if ((double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position) > bgmVolume)
bgmVolume = newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position);
}
newPlayer.AddBgm(BgmType.StudentIsApproaching, bgmVolume);
}
else
newPlayer.AddBgm(BgmType.StudentIsApproaching, bgmVolume);
}
else
{
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
if (person.IsGhost())
{
if (person.IsGhost())
if (!noise)
{
if (!noise)
{
if (XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
else newPlayer.AddBgm(BgmType.GhostIsComing, 0);
}
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
{
TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
ScoreAdded = GameData.StudentScorePinDown(TimePinningDown);
}
else TimePinningDown = ScoreAdded = 0;
break;
if (XY.DistanceFloor3(newPlayer.Position, person.Position) <= (newPlayer.AlertnessRadius / person.Concealment))
newPlayer.AddBgm(BgmType.GhostIsComing, (double)newPlayer.AlertnessRadius / XY.DistanceFloor3(newPlayer.Position, person.Position));
else newPlayer.AddBgm(BgmType.GhostIsComing, 0);
}
if (newPlayer.CharacterType != CharacterType.Teacher && newPlayer.CharacterType != CharacterType.Robot && newPlayer.CanPinDown() && XY.DistanceFloor3(newPlayer.Position, person.Position) <= GameData.PinningDownRange)
{
TimePinningDown += GameData.checkInterval;
newPlayer.AddScore(GameData.StudentScorePinDown(TimePinningDown) - ScoreAdded);
ScoreAdded = GameData.StudentScorePinDown(TimePinningDown);
}
else TimePinningDown = ScoreAdded = 0;
break;
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}

if (!noise)
{
gameMap.GameObjLockDict[GameObjType.Generator].EnterReadLock();
try
double bgmVolume = 0;
foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator])
{
double bgmVolume = 0;
foreach (Generator generator in gameMap.GameObjDict[GameObjType.Generator])
if (XY.DistanceFloor3(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius)
{
if (XY.DistanceFloor3(newPlayer.Position, generator.Position) <= newPlayer.AlertnessRadius)
{
if (generator.NumOfFixing > 0 && (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position) > bgmVolume)
bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position);
}
if (generator.NumOfFixing > 0 && (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position) > bgmVolume)
bgmVolume = (double)newPlayer.AlertnessRadius * generator.DegreeOfRepair / GameData.degreeOfFixedGenerator / XY.DistanceFloor3(newPlayer.Position, generator.Position);
}
newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, bgmVolume);
}
finally
{
gameMap.GameObjLockDict[GameObjType.Generator].ExitReadLock();
}
newPlayer.AddBgm(BgmType.GeneratorIsBeingFixed, bgmVolume);
}
},
timeInterval: GameData.checkInterval,


+ 7
- 31
logic/Gaming/Game.cs View File

@@ -281,17 +281,9 @@ namespace Gaming
{
if (!gameMap.Timer.IsGaming)
return;
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{
skillManager.UseAllPassiveSkill(player);
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
skillManager.UseAllPassiveSkill(player);
}
}

@@ -319,22 +311,14 @@ namespace Gaming
{
if (!GameData.NeedCopy(keyValuePair.Key))
{
gameMap.GameObjLockDict[keyValuePair.Key].EnterWriteLock();
try
if (keyValuePair.Key == GameObjType.Character)
{
if (keyValuePair.Key == GameObjType.Character)
foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character player in gameMap.GameObjDict[GameObjType.Character])
{
player.CanMove.SetReturnOri(false);
}
player.CanMove.SetReturnOri(false);
}
gameMap.GameObjDict[keyValuePair.Key].Clear();
}
finally
{
gameMap.GameObjLockDict[keyValuePair.Key].ExitWriteLock();
}
gameMap.GameObjDict[keyValuePair.Key].Clear();
}
}
}
@@ -350,15 +334,7 @@ namespace Gaming
{
if (GameData.NeedCopy(keyValuePair.Key))
{
gameMap.GameObjLockDict[keyValuePair.Key].EnterReadLock();
try
{
gameObjList.AddRange(gameMap.GameObjDict[keyValuePair.Key]);
}
finally
{
gameMap.GameObjLockDict[keyValuePair.Key].ExitReadLock();
}
gameObjList.AddRange(gameMap.GameObjDict[keyValuePair.Key].ToNewList());
}
}
return gameObjList;


+ 32
- 48
logic/Gaming/PropManager.cs View File

@@ -83,24 +83,16 @@ namespace Gaming
}
else
{
gameMap.GameObjLockDict[GameObjType.Gadget].EnterReadLock();
try
foreach (Gadget prop in gameMap.GameObjDict[GameObjType.Gadget])
{
foreach (Gadget prop in gameMap.GameObjDict[GameObjType.Gadget])
if (prop.GetPropType() == propType)
{
if (prop.GetPropType() == propType)
if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false)
{
if (GameData.IsInTheSameCell(prop.Position, player.Position) && prop.CanMove == false)
{
pickProp = player.PropInventory[indexing] = prop;
}
pickProp = player.PropInventory[indexing] = prop;
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Gadget].ExitReadLock();
}
}

if (pickProp.GetPropType() != PropType.Null)
@@ -142,47 +134,39 @@ namespace Gaming
int len = availableCellForGenerateProp.Count;
Random r = new Random(Environment.TickCount);

gameMap.GameObjLockDict[GameObjType.Chest].EnterReadLock();
try
int cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
int cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key3(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}
cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key5(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}
cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key6(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key3(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}
cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key5(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}
cou = 0;
while (cou < GameData.numOfKeyEachArea)
{
++cou;
Chest chest = GetChest(r);
chest.PropInChest[1] = new Key6(chest.Position);
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
}

foreach (Chest chest in gameMap.GameObjDict[GameObjType.Chest])
foreach (Chest chest in gameMap.GameObjDict[GameObjType.Chest])
{
if (chest.PropInChest[0].GetPropType() == PropType.Null)
{
if (chest.PropInChest[0].GetPropType() == PropType.Null)
{
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
chest.PropInChest[1] = ProduceOnePropNotKey(r, chest.Position);
}
chest.PropInChest[0] = ProduceOnePropNotKey(r, chest.Position);
chest.PropInChest[1] = ProduceOnePropNotKey(r, chest.Position);
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Chest].ExitReadLock();
}
/*
new Thread
(


+ 51
- 106
logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs View File

@@ -43,35 +43,27 @@ namespace Gaming
loopCondition: () => player.Commandable() && gameMap.Timer.IsGaming,
loopToDo: () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character person in gameMap.GameObjDict[GameObjType.Character])
if (!person.IsGhost() && person.CharacterType != CharacterType.Robot && !person.NoHp())
{
if (!person.IsGhost() && person.CharacterType != CharacterType.Robot && !person.NoHp())
double dis = XY.DistanceFloor3(person.Position, player.Position);
if (dis >= player.AlertnessRadius)
{
double dis = XY.DistanceFloor3(person.Position, player.Position);
if (dis >= player.AlertnessRadius)
{
person.AddMoveSpeed(GameData.checkIntervalWhenShowTime, dis / player.AlertnessRadius);
actionManager.MovePlayerWhenStunned(person, GameData.checkIntervalWhenShowTime, (player.Position - person.Position).Angle());
}
else if (dis >= player.ViewRange)
person.AddMoveSpeed(GameData.checkIntervalWhenShowTime, dis / player.AlertnessRadius);
actionManager.MovePlayerWhenStunned(person, GameData.checkIntervalWhenShowTime, (player.Position - person.Position).Angle());
}
else if (dis >= player.ViewRange)
{
Student student = (Student)person;
student.GamingAddiction.AddPositiveV(GameData.checkIntervalWhenShowTime);
if (student.GamingAddiction.IsMaxV())
{
Student student = (Student)person;
student.GamingAddiction.AddPositiveV(GameData.checkIntervalWhenShowTime);
if (student.GamingAddiction.IsMaxV())
{
characterManager.Die(student);
}
characterManager.Die(student);
}
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
},
timeInterval: GameData.checkIntervalWhenShowTime,
maxTotalDuration: skill.DurationTime,
@@ -178,22 +170,15 @@ namespace Gaming
loopToDo: () =>
{
dis = ((homingMissile == null || homingMissile.IsRemoved) ? double.MaxValue : XY.DistanceFloor3(homingMissile.Position, whoAttacked.Position));
gameMap.GameObjLockDict[GameObjType.Bullet].EnterReadLock();
try
foreach (Bullet bullet in gameMap.GameObjDict[GameObjType.Bullet])
{
foreach (Bullet bullet in gameMap.GameObjDict[GameObjType.Bullet])
if (!bullet.CanMove && XY.DistanceFloor3(bullet.Position, whoAttacked.Position) < dis && bullet.TypeOfBullet == BulletType.JumpyDumpty)
{
if (!bullet.CanMove && XY.DistanceFloor3(bullet.Position, whoAttacked.Position) < dis && bullet.TypeOfBullet == BulletType.JumpyDumpty)
{
homingMissile = bullet;
dis = XY.DistanceFloor3(bullet.Position, whoAttacked.Position);
}
homingMissile = bullet;
dis = XY.DistanceFloor3(bullet.Position, whoAttacked.Position);
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Bullet].ExitReadLock();
}
if (homingMissile != null)
{
homingMissile.CanMove.SetReturnOri(true);
@@ -309,22 +294,14 @@ namespace Gaming
if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Howl), player, () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange)
{
if (!character.IsGhost() && !character.NoHp() && XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange)
{
if (CharacterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl));
}
if (CharacterManager.BeStunned(character, GameData.timeOfStudentStunnedWhenHowl) > 0)
player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStudentStunnedWhenHowl));
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
characterManager.BackSwing(player, GameData.timeOfGhostSwingingAfterHowl);
Debugger.Output(player, "howled!");
},
@@ -337,29 +314,21 @@ namespace Gaming
if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Punish), player, () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
if (character.IsGhost() &&
(character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging
|| character.PlayerState == PlayerStateType.UsingSkill
|| character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor
|| character.PlayerState == PlayerStateType.ClimbingThroughWindows)
&& XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange / 3)
{
if (character.IsGhost() &&
(character.PlayerState == PlayerStateType.TryingToAttack || character.PlayerState == PlayerStateType.Swinging
|| character.PlayerState == PlayerStateType.UsingSkill
|| character.PlayerState == PlayerStateType.LockingTheDoor || character.PlayerState == PlayerStateType.OpeningTheDoor
|| character.PlayerState == PlayerStateType.ClimbingThroughWindows)
&& XY.DistanceFloor3(character.Position, player.Position) <= player.ViewRange / 3)
{
int stunTime = (GameData.timeOfGhostStunnedWhenPunish + (int)((GameData.factorOfTimeStunnedWhenPunish * (player.HP.GetMaxV() - player.HP) / GameData.basicApOfGhost))) / characterManager.FactorTeacher;
if (CharacterManager.BeStunned(character, stunTime) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(stunTime) / characterManager.FactorTeacher);
break;
}
int stunTime = (GameData.timeOfGhostStunnedWhenPunish + (int)((GameData.factorOfTimeStunnedWhenPunish * (player.HP.GetMaxV() - player.HP) / GameData.basicApOfGhost))) / characterManager.FactorTeacher;
if (CharacterManager.BeStunned(character, stunTime) > 0)
player.AddScore(GameData.StudentScoreTrickerBeStunned(stunTime) / characterManager.FactorTeacher);
break;
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
Debugger.Output(player, "uses punishing!");
},
() =>
@@ -411,28 +380,20 @@ namespace Gaming
if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Rouse), player, () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
lock (character.ActionLock)
{
lock (character.ActionLock)
if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character))
{
if ((character.PlayerState == PlayerStateType.Addicted) && gameMap.CanSee(player, character))
{
character.SetPlayerStateNaturally();
character.HP.SetPositiveV(GameData.RemainHpWhenAddLife);
((Student)character).TimeOfRescue.SetReturnOri(0);
player.AddScore(GameData.StudentScoreRescue);
break;
}
character.SetPlayerStateNaturally();
character.HP.SetPositiveV(GameData.RemainHpWhenAddLife);
((Student)character).TimeOfRescue.SetReturnOri(0);
player.AddScore(GameData.StudentScoreRescue);
break;
}
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
Debugger.Output(player, "rouse someone!");
},
() =>
@@ -444,24 +405,16 @@ namespace Gaming
if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Encourage), player, () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
if ((character.HP < character.HP.GetMaxV()) && gameMap.CanSee(player, character))
{
if ((character.HP < character.HP.GetMaxV()) && gameMap.CanSee(player, character))
{
player.AddScore(GameData.StudentScoreTreat(GameData.addHpWhenEncourage));
character.HP.AddPositiveV(GameData.addHpWhenEncourage);
((Student)character).SetDegreeOfTreatment0();
break;
}
player.AddScore(GameData.StudentScoreTreat(GameData.addHpWhenEncourage));
character.HP.AddPositiveV(GameData.addHpWhenEncourage);
((Student)character).SetDegreeOfTreatment0();
break;
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
Debugger.Output(player, "encourage someone!");
},
() =>
@@ -473,22 +426,14 @@ namespace Gaming
if ((!player.Commandable())) return false;
return ActiveSkillEffect(player.FindActiveSkill(ActiveSkillType.Inspire), player, () =>
{
gameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in gameMap.GameObjDict[GameObjType.Character])
if (gameMap.CanSee(player, character) && !character.IsGhost())
{
if (gameMap.CanSee(player, character) && !character.IsGhost())
{
player.AddScore(GameData.ScoreInspire);
character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire);
}
player.AddScore(GameData.ScoreInspire);
character.AddMoveSpeed(GameData.timeOfAddingSpeedWhenInspire, GameData.addedTimeOfSpeedWhenInspire);
}
}
finally
{
gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
}
Debugger.Output(player, "inspires!");
},
() =>


+ 3
- 3
logic/Preparation/Interface/IMap.cs View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using Preparation.Utility;

@@ -9,8 +10,7 @@ namespace Preparation.Interface
ITimer Timer { get; }

// the two dicts must have same keys
Dictionary<GameObjType, IList<IGameObj>> GameObjDict { get; }
Dictionary<GameObjType, ReaderWriterLockSlim> GameObjLockDict { get; }
Dictionary<GameObjType, LockedClassList<IGameObj>> GameObjDict { get; }

public uint[,] ProtoGameMap { get; }
public PlaceType GetPlaceType(IGameObj obj);


+ 22
- 4
logic/Preparation/Utility/SafeValue/ListLocked.cs View File

@@ -1,24 +1,26 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace Preparation.Utility
{
public class LockedClassList<T> where T : class
public class LockedClassList<T> : IEnumerable
where T : class
{
private readonly ReaderWriterLockSlim listLock = new();
private List<T> list;

#region 构造
public LockedList()
public LockedClassList()
{
list = new List<T>();
}
public LockedList(int capacity)
public LockedClassList(int capacity)
{
list = new List<T>(capacity);
}
public LockedList(IEnumerable<T> collection)
public LockedClassList(IEnumerable<T> collection)
{
list = new List<T>(collection);
}
@@ -141,6 +143,17 @@ namespace Preparation.Utility
return ReadLock(() => { return list.IndexOf(item); });
}

public Array ToArray()
{
return ReadLock(() => { return list.ToArray(); });
}

public List<T> ToNewList()
{
List<T> lt = new();
return ReadLock(() => { lt.AddRange(list); return lt; });
}

public bool Contains(T item)
{
return ReadLock(() => { return list.Contains(item); });
@@ -159,6 +172,11 @@ namespace Preparation.Utility
public int FindIndex(Predicate<T> match) => ReadLock(() => { return list.FindIndex(match); });

public void ForEach(Action<T> action) => ReadLock(() => { list.ForEach(action); });

public IEnumerator GetEnumerator()
{
return ReadLock(() => { return list.GetEnumerator(); });
}
#endregion
}
}

+ 5
- 22
logic/Server/GameServer.cs View File

@@ -173,17 +173,9 @@ namespace Server
}
private bool playerDeceased(int playerID)
{
game.GameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
foreach (Character character in game.GameMap.GameObjDict[GameObjType.Character])
{
foreach (Character character in game.GameMap.GameObjDict[GameObjType.Character])
{
if (character.PlayerID == playerID && character.PlayerState == PlayerStateType.Deceased) return true;
}
}
finally
{
game.GameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
if (character.PlayerID == playerID && character.PlayerState == PlayerStateType.Deceased) return true;
}
return false;
}
@@ -191,19 +183,10 @@ namespace Server
public override int[] GetScore()
{
int[] score = new int[2]; // 0代表Student,1代表Tricker
game.GameMap.GameObjLockDict[GameObjType.Character].EnterReadLock();
try
{
foreach (Character character in game.GameMap.GameObjDict[GameObjType.Character])
{
if (!character.IsGhost()) score[0] += (int)character.Score;
else score[1] += (int)character.Score;
}

}
finally
foreach (Character character in game.GameMap.GameObjDict[GameObjType.Character])
{
game.GameMap.GameObjLockDict[GameObjType.Character].ExitReadLock();
if (!character.IsGhost()) score[0] += (int)character.Score;
else score[1] += (int)character.Score;
}
return score;
}


Loading…
Cancel
Save