diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3a08bf5..997554ce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,7 +5,7 @@ jobs: if: true runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET Core uses: actions/setup-dotnet@v3 with: diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 58409d3c..4d0acd25 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -7,7 +7,7 @@ jobs: upload_docker_images: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Log in to DockerHub run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 48a7fac0..c7eeb3a0 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -4,7 +4,7 @@ jobs: clang-format-checking: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: DoozyX/clang-format-lint-action@v0.15 with: source: '.' @@ -16,7 +16,7 @@ jobs: dotnet-format-checking: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup .NET Core uses: actions/setup-dotnet@v3 with: diff --git a/.github/workflows/upload_COS.yml b/.github/workflows/upload_COS.yml index 9aeef8d2..80ae5472 100644 --- a/.github/workflows/upload_COS.yml +++ b/.github/workflows/upload_COS.yml @@ -13,7 +13,7 @@ jobs: client_build: runs-on: windows-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup dotnet Core uses: actions/setup-dotnet@v3 @@ -21,7 +21,7 @@ jobs: dotnet-version: 6.0.402 - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9.2' architecture: 'x64' @@ -88,7 +88,7 @@ jobs: cp -r ./CAPI/shell/* ./THUAI6/linux/ cp -r ./CAPI/shell/* ./THUAI6/osx/ - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v4 with: name: my-artifact path: ./THUAI6 @@ -97,7 +97,7 @@ jobs: needs: client_build runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup dotnet Core uses: actions/setup-dotnet@v3 @@ -105,14 +105,14 @@ jobs: dotnet-version: 6.0.x - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: '3.9' - name: Pip Install paramiko run: pip install paramiko - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: my-artifact path: ./THUAI6 diff --git a/CAPI/python/requirements.txt b/CAPI/python/requirements.txt index 61138d46..1c67a0db 100644 --- a/CAPI/python/requirements.txt +++ b/CAPI/python/requirements.txt @@ -1,3 +1,3 @@ grpcio==1.54.2 -grpcio-tools==1.54.2 +grpcio-tools==1.62.1 numpy diff --git a/dependency/Dockerfile/Dockerfile_run b/dependency/Dockerfile/Dockerfile_run index 001d93ad..07f3a39b 100644 --- a/dependency/Dockerfile/Dockerfile_run +++ b/dependency/Dockerfile/Dockerfile_run @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/sdk:6.0.408-jammy-amd64 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0.204-jammy-amd64 AS build MAINTAINER eesast WORKDIR /usr/local COPY . . diff --git a/dependency/proto/Protos.csproj b/dependency/proto/Protos.csproj index 329475b8..9b4b0412 100755 --- a/dependency/proto/Protos.csproj +++ b/dependency/proto/Protos.csproj @@ -14,11 +14,11 @@ --> - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/dependency/shell/compile.sh b/dependency/shell/compile.sh index fd83cbaf..d87eddb9 100644 --- a/dependency/shell/compile.sh +++ b/dependency/shell/compile.sh @@ -1,35 +1,31 @@ #! /bin/bash # WORKDIR /usr/local/PlayerCode/CAPI/cpp -i=1 +workdir=/usr/local/PlayerCode/CAPI/cpp +bind=/usr/local/code +output=/usr/local/output + flag=1 -bind=/usr/local/mnt -while (( $i <= 5 )) -do - if [ -f "${bind}/player${i}.cpp" ]; then - cp -f $bind/player$i.cpp ./API/src/AI.cpp - cmake ./CMakeLists.txt && make -j$(nproc) >compile_log$i.txt 2>&1 - mv ./capi $bind/capi$i # executable file - if [ $? -ne 0 ]; then - flag=0 - fi - mv ./compile_log$i.txt $bind/compile_log$i.txt - elif [ -f "${bind}/player${i}.py" ]; then - pushd ../python - cp -f $bind/player$i.py ./PyAPI/AI.py - python3 -m compileall ./PyAPI >compile_log$i.txt 2>&1 - if [ ! -f ./PyAPI/__pycache__/AI.cpython-39.pyc ]; then - flag=0 - else - rm -rf ./PyAPI/__pycache__/AI.cpython-39.pyc - fi - mv ./compile_log$i.txt $bind/compile_log$i.txt - popd + +cd $bind +file_count=$(ls -l *.cpp | wc -l); +if [ $file_count -eq 1 ] +then + filename=$(ls *.cpp) + base_name=$(basename "$filename" .cpp) + cd $workdir + cp -f $bind/$filename $workdir/API/src/AI.cpp + cmake ./CMakeLists.txt && make -j$(nproc) >$base_name.log 2>&1 + mv ./capi $output/$base_name + if [ $? -ne 0 ]; then + flag=0 fi - let "i++" -done -# HTML request to update status. + mv ./$base_name.log $output/$base_name.log +else + flag=0 +fi + if [ $flag -eq 1 ]; then - curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"compiled"}' > $bind/curl_log.txt + curl $URL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"Completed"}' > $output/$base_name.curl.log else - curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"failed"}' > $bind/curl_log.txt + curl $URL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"compile_status":"Failed"}' > $output/$base_name.curl.log fi diff --git a/dependency/shell/run.sh b/dependency/shell/run.sh index d92f7663..760f1a65 100644 --- a/dependency/shell/run.sh +++ b/dependency/shell/run.sh @@ -1,71 +1,228 @@ #!/usr/local - python_dir=/usr/local/PlayerCode/CAPI/python/PyAPI python_main_dir=/usr/local/PlayerCode/CAPI/python -playback_dir=/usr/local/playback +playback_dir=/usr/local/output +map_dir=/usr/local/map +mkdir -p $playback_dir -if [ $EXPOSED -eq 1 ]; then - nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $TIME --mode $MODE --mapResource $MAP --url $URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log 2>&1 & - server_pid=$! -else - nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $TIME --mode $MODE --mapResource $MAP --notAllowSpectator --url $URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log 2>&1 & - server_pid=$! +# initialize +if [[ "${MODE}" == "ARENA" ]]; then + MODE_NUM=0 +elif [[ "${MODE}" == "COMPETITION" ]]; then + MODE_NUM=1 fi -sleep 5 -for k in {1..2} -do - pushd /usr/local/team$k - if [ $k -eq 1 ]; then - for i in {1..4} + +# set default value +: "${TEAM_SEQ_ID:=0}" +: "${TEAM_LABELS:=Student:Tricker}" +: "${TEAM_LABEL:=Student}" +: "${EXPOSED=1}" +: "${MODE_NUM=0}" +: "${GAME_TIME=10}" +: "${CONNECT_IP=172.17.0.1}" + + +get_current_team_label() { + if [ $TEAM_SEQ_ID -eq $2 ]; then + echo "find current team label: $1" + current_team_label=$1 + fi +} + +read_array() { + callback=$1 + echo "read array: set callback command: $callback" + + IFS=':' read -r -a fields <<< "$2" + + count=0 # loop count + + for field in "${fields[@]}" + do + echo "parse field: $field" + param0=$field + + # call command + run_command="$callback $param0 $count" + echo "Call Command: $run_command" + $run_command + + count=$((count+1)) + done +} + + +function retry_command { + local command="$1" + local max_attempts=2 + local attempt_num=1 + local sleep_seconds=10 + + while [ $attempt_num -le $max_attempts ]; do + echo "Attempt $attempt_num / $max_attempts to run command: $command" + + eval $command & + local PID=$! + + sleep $sleep_seconds + + if kill -0 $PID 2>/dev/null; then + echo "Connected to server successfully." + return 0 + else + echo "Failed to connect to server. Retrying..." + ((attempt_num++)) + fi + done + + echo "Failed to connect to server after $max_attempts attempts." + return 1 +} + + + +if [ "$TERMINAL" = "SERVER" ]; then + map_path=$map_dir/$MAP_ID.txt + # allow spectator always. + nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $GAME_TIME --mode $MODE_NUM --mapResource $map_path --url $SCORE_URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log 2>&1 & + server_pid=$! + echo "server pid: $server_pid" + ls $playback_dir + + echo "SCORE URL: $SCORE_URL" + echo "FINISH URL: $FINISH_URL" + + echo "waiting..." + sleep 30 # wait connection time + echo "watching..." + + if [ -f $playback_dir/start.lock ]; then + ps -p $server_pid + while [ $? -eq 0 ] do - j=$((i - 1)) - if [ -f "./player$i.py" ]; then - cp -r $python_main_dir $python_main_dir$i - cp -f ./player$i.py $python_main_dir$i/PyAPI/AI.py - nice -0 python3 $python_main_dir$i/PyAPI/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log 2>&1 & - elif [ -f "./capi$i" ]; then - nice -0 ./capi$i -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log 2>&1 & - else - echo "ERROR. $i is not found." - fi + sleep 1 + ps -p $server_pid > /dev/null 2>&1 done + + # update score by finish url + + echo "Getting result score..." + result=$(cat $playback_dir/result.json) + score0=$(echo "$result" | grep -oP '(?<="Student":)\d+') # Student score + score1=$(echo "$result" | grep -oP '(?<="Tricker":)\d+') # Tricker score + echo "Result score: Student: $score0, Tricker: $score1" + + # detect two team seqs: + echo "Parsing TEAM_LABELS: $TEAM_LABELS" + TEAM_SEQ_ID=0 + read_array get_current_team_label $TEAM_LABELS + if [[ "${current_team_label}" == "Student" ]]; then + echo "Parse Success: 1st team is Student" + finish_payload='{"result": {"status": "Finished", "scores": ['${score0}', '${score1}']}}' + elif [[ "${current_team_label}" == "Tricker" ]]; then + echo "Parse Success: 1st team is Tricker" + finish_payload='{"result": {"status": "Finished", "scores": ['${score1}', '${score0}']}}' + else + echo "Parse Failure: 1st team is Unknown" + finish_payload='{"result": {"status": "Crashed", "scores": [0, 0]}}' + fi + + if [[ -n $finish_payload ]]; then + echo "FINISH_URL: $FINISH_URL, payload: $finish_payload. Start update score..." + curl $FINISH_URL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d "${finish_payload}" > $playback_dir/send.log 2>&1 + else + echo "Payload not set." + fi + + # Congratulations! You have finished the competition!!!!! + # touch $playback_dir/finish.lock + echo "Finish!" else - for i in {5..5} - do - j=$((i - 1)) - if [ -f "./player$i.py" ]; then + echo "Failed to start game." + touch $playback_dir/finish.lock + touch temp.lock + mv -f temp.lock $playback_dir/video.thuaipb + kill -9 $server_pid + finish_payload='{"result": {"status": "Crashed", "scores": [0, 0]}}' + curl $FINISH_URL -X POST -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d "${finish_payload}" > $playback_dir/send.log 2>&1 + fi + + +elif [ "$TERMINAL" = "CLIENT" ]; then + echo "Client Mode! Team Label data - $TEAM_LABEL" + + # parse team label name + current_team_label=$TEAM_LABEL + + # k is an enum (1,2), 1 = Student, 2 = Tricker + if [ "$current_team_label" = "Student" ]; then + k=1 + elif [ "$current_team_label" = "Tricker" ]; then + k=2 + else + echo "Error: Invalid Team Label" + exit + fi + pushd /usr/local/code + if [ $k -eq 1 ]; then + for i in {1..4} + do + j=$((i - 1)) # student player id from 0 to 3 + code_name=Student$i + if [ -f "./$code_name.py" ]; then + echo "find ./$code_name.py" + cp -r $python_main_dir $python_main_dir$i + cp -f ./$code_name.py $python_main_dir$i/PyAPI/AI.py + + command="nice -0 python3 $python_main_dir$i/PyAPI/main.py -I $CONNECT_IP -P $PORT -p $j > $playback_dir/team$k-player$j.log 2>&1" + + retry_command "$command" & + + ps -aux |grep main.py + + elif [ -f "./$code_name" ]; then + echo "find ./$code_name" + + command="nice -0 ./$code_name -I $CONNECT_IP -P $PORT -p $j > $playback_dir/team$k-player$j.log 2>&1" + + ps -aux |grep $code_name + + else + echo "ERROR. $code_name is not found." + fi + done + else + i=5 + j=4 # tricker id is 4 + code_name=Tricker + if [ -f "./$code_name.py" ]; then + echo "find ./$code_name.py" cp -r $python_main_dir $python_main_dir$i - cp -f ./player$i.py $python_main_dir$i/PyAPI/AI.py - nice -0 python3 $python_main_dir$i/PyAPI/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log 2>&1 & - elif [ -f "./capi$i" ]; then - nice -0 ./capi$i -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log 2>&1 & + cp -f ./$code_name.py $python_main_dir$i/PyAPI/AI.py + + command="nice -0 python3 $python_main_dir$i/PyAPI/main.py -I $CONNECT_IP -P $PORT -p $j > $playback_dir/team$k-player$j.log 2>&1" + + retry_command "$command" + + ps -aux |grep main.py + elif [ -f "./$code_name" ]; then + echo "find ./$code_name" + + command="nice -0 ./$code_name -I $CONNECT_IP -P $PORT -p $j > $playback_dir/team$k-player$j.log 2>&1" + + retry_command "$command" + + ps -aux |grep $code_name else - echo "ERROR. $i is not found." + echo "ERROR. $code_name is not found." fi - done - fi - popd -done + fi -sleep 10 + # curl $CONNECT_IP:$PORT -if [ -f $playback_dir/start.lock ]; then - ps -p $server_pid - while [ $? -eq 0 ] - do - sleep 1 - ps -p $server_pid > /dev/null 2>&1 - done - # result=$(cat /usr/local/playback/result.json) - # score0=$(echo "$result" | grep -oP '(?<="Student":)\d+') - # score1=$(echo "$result" | grep -oP '(?<="Tricker":)\d+') - # curl $URL -X PUT -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN" -d '{"result":[{"team_id":0, "score":'${score0}'}, {"team_id":1, "score":'${score1}'}], "mode":'${MODE}'}'> $playback_dir/send.log 2>&1 - touch $playback_dir/finish.lock - echo "Finish" + sleep $((GAME_TIME * 2)) + + popd else - echo "Failed to start game." - touch $playback_dir/finish.lock - touch temp.lock - mv -f temp.lock $playback_dir/video.thuaipb - kill -9 $server_pid + echo "VALUE ERROR: TERMINAL is neither SERVER nor CLIENT." fi diff --git a/installer/Installer/Installer.csproj b/installer/Installer/Installer.csproj index 0d16d13a..48819a26 100644 --- a/installer/Installer/Installer.csproj +++ b/installer/Installer/Installer.csproj @@ -23,9 +23,9 @@ - + - + diff --git a/installer/InstallerUpdater/InstallerUpdater.csproj b/installer/InstallerUpdater/InstallerUpdater.csproj index 3be5681c..1e60168a 100644 --- a/installer/InstallerUpdater/InstallerUpdater.csproj +++ b/installer/InstallerUpdater/InstallerUpdater.csproj @@ -17,7 +17,7 @@ - + true diff --git a/logic/Client/Client.csproj b/logic/Client/Client.csproj index 02642032..f5b667b8 100644 --- a/logic/Client/Client.csproj +++ b/logic/Client/Client.csproj @@ -15,7 +15,7 @@ - + diff --git a/logic/Client/MainWindow.xaml.cs b/logic/Client/MainWindow.xaml.cs index 1ac24a75..2d4c213b 100644 --- a/logic/Client/MainWindow.xaml.cs +++ b/logic/Client/MainWindow.xaml.cs @@ -836,7 +836,7 @@ private void Refresh(object? sender, EventArgs e) //log未更新 foreach (var skill in occupation.ListOfIActiveSkill) { var iActiveSkill = SkillFactory.FindActiveSkill(skill); - coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD; + coolTime[i, obj.PlayerId] = (int)(iActiveSkill.SkillCD.GetCD()); ++i; } isDataFixed[obj.PlayerId] = true; @@ -844,14 +844,14 @@ private void Refresh(object? sender, EventArgs e) //log未更新 } foreach (var obj in listOfButcher) { - if (!isDataFixed[obj.PlayerId]) + if (obj.PlayerId < GameData.numOfPeople && !isDataFixed[obj.PlayerId]) { IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); int j = 0; foreach (var skill in occupation1.ListOfIActiveSkill) { var iActiveSkill = SkillFactory.FindActiveSkill(skill); - coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; + coolTime[j, GameData.numOfStudent] = (int)(iActiveSkill.SkillCD.GetCD()); ++j; } isDataFixed[obj.PlayerId] = true; diff --git a/logic/ClientTest/ClientTest.csproj b/logic/ClientTest/ClientTest.csproj index e76f9356..9e7974e7 100644 --- a/logic/ClientTest/ClientTest.csproj +++ b/logic/ClientTest/ClientTest.csproj @@ -8,7 +8,7 @@ - + diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs index 072a993b..4ddd23ed 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs @@ -8,7 +8,7 @@ internal sealed class CommonAttackOfGhost : Bullet public CommonAttackOfGhost(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) { - AP.Set(GameData.basicApOfGhost); + AP.SetReturnOri(GameData.basicApOfGhost); } public override double BulletBombRange => 0; public override double AttackDistance => GameData.basicAttackShortRange; @@ -45,7 +45,7 @@ internal sealed class Strike : Bullet public Strike(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) { - AP.Set(GameData.basicApOfGhost * 16 / 15); + AP.SetReturnOri(GameData.basicApOfGhost * 16 / 15); } public override double BulletBombRange => 0; public override double AttackDistance => GameData.basicAttackShortRange * 20 / 22; @@ -83,7 +83,7 @@ internal sealed class FlyingKnife : Bullet public FlyingKnife(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) { - AP.Set(GameData.basicApOfGhost * 4 / 5); + AP.SetReturnOri(GameData.basicApOfGhost * 4 / 5); } public override double BulletBombRange => 0; public override double AttackDistance => GameData.basicRemoteAttackRange * 13; @@ -123,7 +123,7 @@ internal sealed class BombBomb : Bullet { public BombBomb(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) { - AP.Set((int)(GameData.basicApOfGhost * 6.0 / 5)); + AP.SetReturnOri((int)(GameData.basicApOfGhost * 6.0 / 5)); } public override double BulletBombRange => GameData.basicBulletBombRange; public override double AttackDistance => GameData.basicAttackShortRange; @@ -163,7 +163,7 @@ internal sealed class JumpyDumpty : Bullet { public JumpyDumpty(Character player, XY pos, int radius = GameData.bulletRadius) : base(player, radius, pos) { - AP.Set((int)(GameData.basicApOfGhost * 0.6)); + AP.SetReturnOri((int)(GameData.basicApOfGhost * 0.6)); } public override double BulletBombRange => GameData.basicBulletBombRange / 2; public override double AttackDistance => GameData.basicAttackShortRange * 18 / 22; diff --git a/logic/GameClass/GameObj/Bullet/Bullet.cs b/logic/GameClass/GameObj/Bullet/Bullet.cs index c47f746c..28954297 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.cs @@ -11,7 +11,8 @@ public abstract class Bullet : ObjOfCharacter /// public abstract double BulletBombRange { get; } public abstract double AttackDistance { get; } - public AtomicInt AP { get; } + private AtomicInt ap = new(0); + public AtomicInt AP { get => ap; } public abstract int Speed { get; } public abstract bool IsRemoteAttack { get; } public abstract int CastTime { get; } @@ -43,8 +44,8 @@ public override bool IgnoreCollideExecutor(IGameObj targetObj) public Bullet(Character player, int radius, XY Position) : base(Position, radius, GameObjType.Bullet) { - this.CanMove.Set(true); - this.MoveSpeed.Set(this.Speed); + this.CanMove.SetReturnOri(true); + this.MoveSpeed.SetReturnOri(this.Speed); this.hasSpear = player.TryUseSpear(); this.Parent = player; } diff --git a/logic/GameClass/GameObj/Character/Character.BuffManager.cs b/logic/GameClass/GameObj/Character/Character.BuffManager.cs index 1677eaa7..cc6ffa83 100644 --- a/logic/GameClass/GameObj/Character/Character.BuffManager.cs +++ b/logic/GameClass/GameObj/Character/Character.BuffManager.cs @@ -15,35 +15,15 @@ public partial class Character /// private class BuffManager { - [StructLayout(LayoutKind.Explicit, Size = 8)] - private struct BuffValue // buff参数联合体类型,可能是int或double - { - [FieldOffset(0)] - public int iValue; - [FieldOffset(0)] - public double lfValue; - - public BuffValue(int intValue) - { - this.lfValue = 0.0; - this.iValue = intValue; - } - public BuffValue(double longFloatValue) - { - this.iValue = 0; - this.lfValue = longFloatValue; - } - } - /// /// buff列表 /// - private readonly LinkedList[] buffList; + private readonly LinkedList[] buffList; private readonly object[] buffListLock; - private void AddBuff(BuffValue bf, int buffTime, BuffType buffType, Action ReCalculateFunc) + private void AddBuff(double bf, int buffTime, BuffType buffType, Action ReCalculateFunc) { - LinkedListNode buffNode; + LinkedListNode buffNode; lock (buffListLock[(int)buffType]) { buffNode = buffList[(int)buffType].AddLast(bf); @@ -80,13 +60,13 @@ public int ReCalculateFloatBuff(BuffType buffType, int orgVal, int maxVal, int m { foreach (var add in buffList[(int)buffType]) { - times *= add.lfValue; + times *= add; } } return Math.Max(Math.Min((int)Math.Round(orgVal * times), maxVal), minVal); } - public void AddMoveSpeed(double add, int buffTime, Action SetNewMoveSpeed, int orgMoveSpeed) => AddBuff(new BuffValue(add), buffTime, BuffType.AddSpeed, () => SetNewMoveSpeed(ReCalculateFloatBuff(BuffType.AddSpeed, orgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed))); + public void AddMoveSpeed(double add, int buffTime, Action SetNewMoveSpeed, int orgMoveSpeed) => AddBuff(add, buffTime, BuffType.AddSpeed, () => SetNewMoveSpeed(ReCalculateFloatBuff(BuffType.AddSpeed, orgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed))); public bool HasFasterSpeed { get @@ -98,7 +78,7 @@ public bool HasFasterSpeed } } - public void AddShield(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Shield, () => + public void AddShield(int shieldTime) => AddBuff(0, shieldTime, BuffType.Shield, () => { }); public bool HasShield { @@ -123,7 +103,7 @@ public bool TryUseShield() return false; } - public void AddAp(int time) => AddBuff(new BuffValue(), time, BuffType.AddAp, () => { }); + public void AddAp(int time) => AddBuff(0, time, BuffType.AddAp, () => { }); public bool HasAp { get @@ -147,7 +127,7 @@ public bool TryAddAp() return false; } - public void AddLife(int totelTime) => AddBuff(new BuffValue(), totelTime, BuffType.AddLife, () => + public void AddLife(int totelTime) => AddBuff(0, totelTime, BuffType.AddLife, () => { }); public bool HasLIFE { @@ -172,7 +152,7 @@ public bool TryActivatingLIFE() return false; } - public void AddSpear(int spearTime) => AddBuff(new BuffValue(), spearTime, BuffType.Spear, () => + public void AddSpear(int spearTime) => AddBuff(0, spearTime, BuffType.Spear, () => { }); public bool HasSpear { @@ -197,20 +177,7 @@ public bool TryUseSpear() return false; } - public bool TryDeleteInvisible() - { - if (HasInvisible) - { - lock (buffListLock[(int)BuffType.Invisible]) - { - buffList[(int)BuffType.Invisible].RemoveFirst(); - } - return true; - } - return false; - } - - public void AddClairaudience(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Clairaudience, () => + public void AddClairaudience(int shieldTime) => AddBuff(0, shieldTime, BuffType.Clairaudience, () => { }); public bool HasClairaudience { @@ -223,7 +190,7 @@ public bool HasClairaudience } } - public void AddInvisible(int shieldTime) => AddBuff(new BuffValue(), shieldTime, BuffType.Invisible, () => + public void AddInvisible(int shieldTime) => AddBuff(0, shieldTime, BuffType.Invisible, () => { }); public bool HasInvisible { @@ -235,6 +202,18 @@ public bool HasInvisible } } } + public bool TryDeleteInvisible() + { + if (HasInvisible) + { + lock (buffListLock[(int)BuffType.Invisible]) + { + buffList[(int)BuffType.Invisible].RemoveFirst(); + } + return true; + } + return false; + } /// /// 清除所有buff @@ -253,12 +232,12 @@ public void ClearAll() public BuffManager() { var buffTypeArray = Enum.GetValues(typeof(BuffType)); - buffList = new LinkedList[buffTypeArray.Length]; + buffList = new LinkedList[buffTypeArray.Length]; buffListLock = new object[buffList.Length]; int i = 0; foreach (BuffType type in buffTypeArray) { - buffList[i] = new LinkedList(); + buffList[i] = new LinkedList(); buffListLock[i++] = new object(); } } diff --git a/logic/GameClass/GameObj/Character/Character.Skill.cs b/logic/GameClass/GameObj/Character/Character.Skill.cs index 81eea6c2..21f4a48d 100644 --- a/logic/GameClass/GameObj/Character/Character.Skill.cs +++ b/logic/GameClass/GameObj/Character/Character.Skill.cs @@ -31,12 +31,12 @@ public bool IsGhost() protected Character(XY initPos, int initRadius, CharacterType characterType) : base(initPos, initRadius, GameObjType.Character) { - this.CanMove.Set(true); + this.CanMove.SetReturnOri(true); this.score = 0; this.buffManager = new BuffManager(); this.occupation = OccupationFactory.FindIOccupation(characterType); - this.MaxHp = this.hp = Occupation.MaxHp; - this.MoveSpeed.Set(this.orgMoveSpeed = Occupation.MoveSpeed); + this.HP = new(Occupation.MaxHp); + this.MoveSpeed.SetReturnOri(this.orgMoveSpeed = Occupation.MoveSpeed); this.BulletOfPlayer = this.OriBulletOfPlayer = Occupation.InitBullet; this.concealment = Occupation.Concealment; this.alertnessRadius = Occupation.AlertnessRadius; diff --git a/logic/GameClass/GameObj/Character/Character.Student.cs b/logic/GameClass/GameObj/Character/Character.Student.cs index 1b52f08f..9be96200 100644 --- a/logic/GameClass/GameObj/Character/Character.Student.cs +++ b/logic/GameClass/GameObj/Character/Character.Student.cs @@ -7,106 +7,24 @@ namespace GameClass.GameObj { public class Student : Character { - private readonly object fixLock = new(); - protected int fixSpeed; - /// - /// 修理电机速度 - /// - public int FixSpeed - { - get - { - lock (fixLock) - return fixSpeed; - } - set - { - lock (fixLock) - { - fixSpeed = value; - } - } - } + public AtomicInt FixSpeed { get; } = new AtomicInt(0); /// /// 原初修理电机速度 /// protected readonly int orgFixSpeed; - private readonly object treatLock = new(); - protected int treatSpeed = GameData.basicTreatSpeed; - public int TreatSpeed - { - get - { - lock (treatLock) - return treatSpeed; - } - set - { - lock (treatLock) - { - treatSpeed = value; - } - } - } + public AtomicInt TreatSpeed { get; } = new AtomicInt(GameData.basicTreatSpeed); protected readonly int orgTreatSpeed; - private readonly object addictionLock = new(); - private int maxGamingAddiction; - public int MaxGamingAddiction - { - get - { - lock (addictionLock) - return maxGamingAddiction; - } - protected set - { - lock (addictionLock) - { - if (value < gamingAddiction) gamingAddiction = value; - maxGamingAddiction = value; - } - } - } - private int gamingAddiction; - public int GamingAddiction - { - get - { - lock (addictionLock) - return gamingAddiction; - } - set - { - if (value > 0) - lock (addictionLock) - gamingAddiction = value <= maxGamingAddiction ? value : maxGamingAddiction; - else - lock (addictionLock) - gamingAddiction = 0; - } - } + public IntInTheVariableRange GamingAddiction { get; } = new IntInTheVariableRange(0, 0); - private int timeOfRescue = 0; - public int TimeOfRescue - { - get => Interlocked.CompareExchange(ref timeOfRescue, -1, -1); - } - public bool AddTimeOfRescue(int value) - { - return Interlocked.Add(ref timeOfRescue, value) >= GameData.basicTimeOfRescue; - } - public void SetTimeOfRescue(int value) - { - Interlocked.Exchange(ref timeOfRescue, value); - } + public AtomicInt TimeOfRescue { get; } = new AtomicInt(0); public Student(XY initPos, int initRadius, CharacterType characterType) : base(initPos, initRadius, characterType) { - this.orgFixSpeed = this.fixSpeed = ((IStudentType)Occupation).FixSpeed; - this.TreatSpeed = this.orgTreatSpeed = ((IStudentType)Occupation).TreatSpeed; - this.MaxGamingAddiction = ((IStudentType)Occupation).MaxGamingAddiction; + this.FixSpeed.SetReturnOri(this.orgFixSpeed = ((IStudentType)Occupation).FixSpeed); + this.TreatSpeed.SetReturnOri(this.orgTreatSpeed = ((IStudentType)Occupation).TreatSpeed); + this.GamingAddiction.SetPositiveMaxV(((IStudentType)Occupation).MaxGamingAddiction); } } public class Golem : Student, IGolem diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs index 9ce07bcc..6c07855b 100644 --- a/logic/GameClass/GameObj/Character/Character.cs +++ b/logic/GameClass/GameObj/Character/Character.cs @@ -10,20 +10,7 @@ public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终 { #region 装弹、攻击相关的基本属性及方法 private readonly object attackLock = new(); - /// - /// 装弹冷却 - /// - protected int cd; - public int CD - { - get - { - lock (attackLock) - { - return cd; - } - } - } + public IntNumUpdateEachCD BulletNum { get; } = new IntNumUpdateEachCD(); private int orgCD; public int OrgCD { @@ -50,56 +37,26 @@ public BulletType BulletOfPlayer lock (attackLock) { bulletOfPlayer = value; - cd = orgCD = (BulletFactory.BulletCD(value)); - Debugger.Output(this, string.Format("'s CD has been set to: {0}.", cd)); - maxBulletNum = bulletNum = (BulletFactory.BulletNum(value)); + orgCD = (BulletFactory.BulletCD(value)); + BulletNum.SetCD(orgCD); + Debugger.Output(this, string.Format("'s CD has been set to: {0}.", orgCD)); + BulletNum.SetPositiveMaxNumAndNum(BulletFactory.BulletNum(value)); } } } - protected int maxBulletNum; - public int MaxBulletNum - { - get - { - lock (attackLock) - { - return maxBulletNum; - } - } - } - private int bulletNum; - private int updateTimeOfBulletNum = 0; - - public int UpdateBulletNum(int time)//通过该函数获取真正的bulletNum - { - lock (attackLock) - { - if (bulletNum < maxBulletNum && time - updateTimeOfBulletNum >= cd) - { - int add = Math.Min(maxBulletNum - bulletNum, (time - updateTimeOfBulletNum) / cd); - updateTimeOfBulletNum += add * cd; - return (bulletNum += add); - } - return bulletNum; - } - } - /// /// 进行一次攻击 /// /// 攻击操作发出的子弹 - public Bullet? Attack(double angle, int time) + public Bullet? Attack(double angle) { lock (attackLock) { if (bulletOfPlayer == BulletType.Null) return null; - if (UpdateBulletNum(time) > 0) + if (BulletNum.TrySub(1) == 1) { - if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time; - --bulletNum; - XY res = Position + new XY // 子弹紧贴人物生成。 ( (int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)), @@ -115,31 +72,6 @@ public int UpdateBulletNum(int time)//通过该函数获取真正的bulletNum return null; } } - - /* - /// - /// 攻击被反弹,反弹伤害不会再被反弹 - /// - /// - /// - /// 反弹伤害者 - /// 是否因反弹伤害而死 - private bool BeBounced(int subHP, bool hasSpear, Character? bouncer) - { - lock (beAttackedLock) - { - if (hp <= 0) - return false; - if (!(bouncer?.TeamID == this.TeamID)) - { - if (hasSpear || !HasShield) - _ = SubHp(subHP); - if (hp <= 0) - TryActivatingLIFE(); - } - return hp <= 0; - } - }*/ #endregion #region 感知相关的基本属性及方法 private readonly object bgmLock = new(); @@ -169,157 +101,18 @@ public void AddBgm(BgmType bgm, double value) #endregion #region 交互相关的基本属性及方法 private readonly int speedOfOpeningOrLocking; - public int SpeedOfOpeningOrLocking - { - get => speedOfOpeningOrLocking; - } + public int SpeedOfOpeningOrLocking => speedOfOpeningOrLocking; private readonly int speedOfClimbingThroughWindows; - public int SpeedOfClimbingThroughWindows - { - get => speedOfClimbingThroughWindows; - } + public int SpeedOfClimbingThroughWindows => speedOfClimbingThroughWindows; private readonly int speedOfOpenChest; - public int SpeedOfOpenChest - { - get => speedOfOpenChest; - } + public int SpeedOfOpenChest => speedOfOpenChest; #endregion #region 血量相关的基本属性及方法 - private readonly ReaderWriterLockSlim hpReaderWriterLock = new(); - public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock; - - private long maxHp; - public long MaxHp - { - get - { - HPReadWriterLock.EnterReadLock(); - try - { - return maxHp; - } - finally - { - HPReadWriterLock.ExitReadLock(); - } - } - protected set - { - HPReadWriterLock.EnterWriteLock(); - try - { - maxHp = value; - if (hp > maxHp) hp = maxHp; - } - finally - { - HPReadWriterLock.ExitWriteLock(); - } - } - } - // 最大血量 - protected long hp; - public long HP - { - get - { - HPReadWriterLock.EnterReadLock(); - try - { - return hp; - } - finally - { - HPReadWriterLock.ExitReadLock(); - } - } - } + public LongInTheVariableRange HP { get; } - public long SetHP(long value) - { - HPReadWriterLock.EnterWriteLock(); - try - { - if (value > 0) - { - return hp = value <= maxHp ? value : maxHp; - } - else - return hp = 0; - } - finally - { - HPReadWriterLock.ExitWriteLock(); - } - } - - /// - /// 尝试减血 - /// - /// 减血量 - public long SubHp(long sub) - { - HPReadWriterLock.EnterWriteLock(); - try - { - long previousHp = hp; - if (hp <= sub) - { - hp = 0; - return hp; - } - else - { - hp -= sub; - return sub; - } - } - finally - { - HPReadWriterLock.ExitWriteLock(); - } - } - - public long AddHP(long add) - { - HPReadWriterLock.EnterWriteLock(); - try - { - long previousHp = hp; - return (hp = (hp + add > maxHp) ? maxHp : hp + add) - previousHp; - } - finally - { - HPReadWriterLock.ExitWriteLock(); - } - } - - private readonly object vampireLock = new(); - public object VampireLock => vampire; - - private double vampire = 0; // 回血率:0-1之间 - public double Vampire - { - get - { - lock (vampireLock) - return vampire; - } - set - { - lock (vampireLock) - { - if (value > 1) - vampire = 1; - else if (value < 0) - vampire = 0; - else - vampire = value; - } - } - } + public DoubleInTheVariableRange Vampire { get; } = new DoubleInTheVariableRange(0, 1); public double OriVampire { get; protected set; } private readonly object treatLock = new(); @@ -328,55 +121,42 @@ public int DegreeOfTreatment { get { - HPReadWriterLock.EnterReadLock(); - try - { + lock (treatLock) return degreeOfTreatment; - } - finally - { - HPReadWriterLock.ExitReadLock(); - } } } public void SetDegreeOfTreatment0() { - HPReadWriterLock.EnterWriteLock(); - try - { + lock (treatLock) degreeOfTreatment = 0; - } - finally - { - HPReadWriterLock.ExitWriteLock(); - } } + public bool AddDegreeOfTreatment(int value, Student whoTreatYou) { - HPReadWriterLock.EnterWriteLock(); - try + lock (treatLock) { - if (value >= maxHp - hp) + degreeOfTreatment += value; + long addV = HP.TryAddToMaxV(degreeOfTreatment); + if (addV == 0) + { + degreeOfTreatment = 0; + return false; + } + if (addV > 0) { - whoTreatYou.AddScore(GameData.StudentScoreTreat(maxHp - hp)); - hp = maxHp; + whoTreatYou.AddScore(GameData.StudentScoreTreat(addV)); degreeOfTreatment = 0; return true; } - if (value >= GameData.basicTreatmentDegree) + if (degreeOfTreatment >= GameData.basicTreatmentDegree) { whoTreatYou.AddScore(GameData.StudentScoreTreat(GameData.basicTreatmentDegree)); - hp += GameData.basicTreatmentDegree; + HP.AddPositiveV(GameData.basicTreatmentDegree); degreeOfTreatment = 0; return true; } - degreeOfTreatment = value; - } - finally - { - HPReadWriterLock.ExitWriteLock(); + return false; } - return false; } #endregion #region 查询状态相关的基本属性与方法 @@ -542,12 +322,12 @@ public long SetPlayerState(RunningStateType runningState, PlayerStateType value case PlayerStateType.OpeningTheChest: if (value == PlayerStateType.Rescued) return -1; - ((Chest)lastObj!).StopOpen(); + ((Chest)lastObj!).OpenProgress.Set0(); return ChangePlayerState(runningState, value, gameObj); case PlayerStateType.OpeningTheDoorway: if (value == PlayerStateType.Rescued) return -1; Doorway doorway = (Doorway)lastObj!; - doorway.StopOpenning(); + doorway.ProgressOfDoorway.TryStop(); return ChangePlayerState(runningState, value, gameObj); case PlayerStateType.OpeningTheDoor: if (value == PlayerStateType.Rescued) return -1; @@ -570,7 +350,7 @@ public long SetPlayerState(RunningStateType runningState, PlayerStateType value else { if (value != PlayerStateType.UsingSkill) - ((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)playerID; + ((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)PlayerID; return ChangePlayerState(runningState, value, gameObj); } } @@ -647,7 +427,7 @@ public bool TryToRemoveFromGame(PlayerStateType playerStateType) { if (SetPlayerState(RunningStateType.RunningForcibly, playerStateType) == -1) return false; TryToRemove(); - CanMove.Set(false); + CanMove.SetReturnOri(false); position = GameData.PosWhoDie; } return true; @@ -673,18 +453,8 @@ public virtual void AddScore(long add) /// /// 角色所属队伍ID /// - private long teamID = long.MaxValue; - public long TeamID - { - get => Interlocked.Read(ref teamID); - set => Interlocked.Exchange(ref teamID, value); - } - private long playerID = long.MaxValue; - public long PlayerID - { - get => Interlocked.Read(ref playerID); - set => Interlocked.Exchange(ref playerID, value); - } + public AtomicLong TeamID { get; } = new AtomicLong(long.MaxValue); + public AtomicLong PlayerID { get; } = new AtomicLong(long.MaxValue); #region 道具和buff相关属性、方法 private readonly object inventoryLock = new(); @@ -802,7 +572,7 @@ public int IndexingOfAddProp() } public void AddMoveSpeed(int buffTime, double add = 1.0) => buffManager.AddMoveSpeed(add, buffTime, newVal => - { MoveSpeed.Set(newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed); }, + { MoveSpeed.SetReturnOri(newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed); }, OrgMoveSpeed); public bool HasFasterSpeed => buffManager.HasFasterSpeed; @@ -866,7 +636,7 @@ public void TryActivatingLIFE() if (buffManager.TryActivatingLIFE()) { AddScore(GameData.ScorePropRemainHp); - hp = GameData.RemainHpWhenAddLife; + HP.SetPositiveV(GameData.RemainHpWhenAddLife); } } diff --git a/logic/GameClass/GameObj/GameObj.cs b/logic/GameClass/GameObj/GameObj.cs index 1934d642..3933b7a3 100644 --- a/logic/GameClass/GameObj/GameObj.cs +++ b/logic/GameClass/GameObj/GameObj.cs @@ -30,7 +30,8 @@ public abstract class GameObj : IGameObj public abstract ShapeType Shape { get; } - public AtomicBool IsRemoved { get; } = new AtomicBool(false); + private AtomicBool isRemoved = new(false); + public AtomicBool IsRemoved { get => isRemoved; } public virtual bool TryToRemove() { return IsRemoved.TrySet(true); diff --git a/logic/GameClass/GameObj/Map/Chest.cs b/logic/GameClass/GameObj/Map/Chest.cs index de67e895..bf4e833b 100644 --- a/logic/GameClass/GameObj/Map/Chest.cs +++ b/logic/GameClass/GameObj/Map/Chest.cs @@ -1,13 +1,12 @@ using Preparation.Interface; using Preparation.Utility; -using System; namespace GameClass.GameObj { /// /// 箱子 /// - public class Chest : Immovable, IChest + public class Chest : Immovable { public Chest(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) @@ -19,38 +18,7 @@ public Chest(XY initPos) : private readonly Gadget[] propInChest = new Gadget[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; public Gadget[] PropInChest => propInChest; - private long openStartTime = 0; - public long OpenStartTime - { - get - { - lock (gameObjLock) return openStartTime; - } - } - private Character? whoOpen = null; - public Character? WhoOpen - { - get - { - lock (gameObjLock) return whoOpen; - } - } - public bool Open(Character character) - { - lock (gameObjLock) - { - if (whoOpen != null) return false; - openStartTime = Environment.TickCount64; - whoOpen = character; - } - return true; - } - public void StopOpen() - { - lock (gameObjLock) - { - whoOpen = null; - } - } + private TimeBasedProgressOptimizedForInterrupting openProgress = new TimeBasedProgressOptimizedForInterrupting(); + public TimeBasedProgressOptimizedForInterrupting OpenProgress { get => openProgress; } } } diff --git a/logic/GameClass/GameObj/Map/Door.cs b/logic/GameClass/GameObj/Map/Door.cs index 8970e6c5..ca6ba5f3 100644 --- a/logic/GameClass/GameObj/Map/Door.cs +++ b/logic/GameClass/GameObj/Map/Door.cs @@ -1,7 +1,6 @@ using Preparation.Interface; using Preparation.Utility; using System; -using System.Threading; namespace GameClass.GameObj { @@ -47,7 +46,8 @@ public bool IsOpen } } - public AtomicInt LockDegree { get; } = new AtomicInt(0); + private AtomicInt lockDegree = new AtomicInt(0); + public AtomicInt LockDegree { get => lockDegree; } private long openStartTime = 0; public long OpenStartTime @@ -98,7 +98,7 @@ public bool TryLock(Character character) { if (!isOpen) return false; if (whoLockOrOpen != null) return false; - LockDegree.Set(0); + LockDegree.SetReturnOri(0); whoLockOrOpen = character; return true; } diff --git a/logic/GameClass/GameObj/Map/Doorway.cs b/logic/GameClass/GameObj/Map/Doorway.cs index 82ac6bea..a0835627 100644 --- a/logic/GameClass/GameObj/Map/Doorway.cs +++ b/logic/GameClass/GameObj/Map/Doorway.cs @@ -17,70 +17,18 @@ public Doorway(XY initPos) : public override ShapeType Shape => ShapeType.Square; public override bool IgnoreCollideExecutor(IGameObj targetObj) { - if (!IsOpen()) return false; + if (!ProgressOfDoorway.IsFinished()) return false; if (targetObj.Type != GameObjType.Character) return true; // 非玩家不碰撞 return false; } public AtomicBool PowerSupply { get; } = new(false); - - private long openStartTime = 0; - public long OpenStartTime - { - get - { - lock (gameObjLock) - return openStartTime; - } - } + public TimeBasedProgressAtVariableSpeed ProgressOfDoorway { get; } = new(GameData.degreeOfOpenedDoorway, 1); public bool TryToOpen() { if (!PowerSupply) return false; - lock (gameObjLock) - { - if (openStartTime > 0) return false; - openStartTime = Environment.TickCount64; - return true; - } + return ProgressOfDoorway.Start(); } - - public bool StopOpenning() - { - lock (gameObjLock) - { - if (Environment.TickCount64 - openStartTime + openDegree >= GameData.degreeOfOpenedDoorway) - { - openDegree = GameData.degreeOfOpenedDoorway; - return true; - } - else - { - openDegree = (int)(Environment.TickCount64 - openStartTime) + openDegree; - openStartTime = 0; - return false; - } - } - } - - public void FinishOpenning() - { - lock (gameObjLock) - { - openDegree = GameData.degreeOfOpenedDoorway; - } - } - - private int openDegree = 0; - public int OpenDegree - { - get - { - lock (gameObjLock) - return openDegree; - } - } - - public bool IsOpen() => (OpenDegree == GameData.degreeOfOpenedDoorway); } } diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs index af3d9b1b..86ee053a 100644 --- a/logic/GameClass/GameObj/Map/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -3,6 +3,7 @@ using Preparation.Interface; using Preparation.Utility; using System; +using System.Collections.Concurrent; namespace GameClass.GameObj { @@ -21,32 +22,18 @@ public void AddNumOfRepairedGenerators() uint value = Interlocked.Increment(ref numOfRepairedGenerators); if (value == GameData.numOfGeneratorRequiredForEmergencyExit) { - GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock(); - try - { - Random r = new Random(Environment.TickCount); - EmergencyExit emergencyExit = (EmergencyExit)(GameObjDict[GameObjType.EmergencyExit][r.Next(0, GameObjDict[GameObjType.EmergencyExit].Count)]); - emergencyExit.CanOpen.Set(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.Set(true); - } - finally - { - GameObjLockDict[GameObjType.Doorway].ExitReadLock(); - } + ((Doorway)doorway).PowerSupply.SetReturnOri(true); + }); } } @@ -113,20 +100,10 @@ public void MapRescueStudent() 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 @@ private void AddScoreFromAddict() } - private Dictionary> gameObjDict; - public Dictionary> GameObjDict => gameObjDict; - private Dictionary gameObjLockDict; - public Dictionary GameObjLockDict => gameObjLockDict; + private Dictionary> gameObjDict; + public Dictionary> GameObjDict => gameObjDict; public readonly uint[,] protoGameMap; public uint[,] ProtoGameMap => protoGameMap; @@ -175,183 +150,55 @@ public IOutOfBound GetOutOfBound(XY pos) 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) - { - Debugger.Output(person, person.PlayerID.ToString()); - 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) @@ -404,70 +251,35 @@ public bool CanSee(Character player, GameObj 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>(); - gameObjLockDict = new Dictionary(); + gameObjDict = new Dictionary>(); foreach (GameObjType idx in Enum.GetValues(typeof(GameObjType))) { if (idx != GameObjType.Null) { - gameObjDict.Add(idx, new List()); - gameObjLockDict.Add(idx, new ReaderWriterLockSlim()); + gameObjDict.TryAdd(idx, new LockedClassList()); } } diff --git a/logic/GameClass/GameObj/Moveable.cs b/logic/GameClass/GameObj/Moveable.cs index 1ff26f7c..093f17a6 100644 --- a/logic/GameClass/GameObj/Moveable.cs +++ b/logic/GameClass/GameObj/Moveable.cs @@ -94,14 +94,16 @@ public void ReSetPos(XY position) } } - public AtomicBool CanMove { get; } + private AtomicBool canMove = new(false); + public AtomicBool CanMove { get => canMove; } public bool IsAvailableForMove => !IsMoving && CanMove && !IsRemoved; // 是否能接收移动指令 /// /// 移动速度 /// - public AtomicInt MoveSpeed { get; } + private AtomicInt moveSpeed = new(0); + public AtomicInt MoveSpeed { get => moveSpeed; } /// /// 原初移动速度 /// diff --git a/logic/GameClass/GameObj/ObjOfCharacter.cs b/logic/GameClass/GameObj/ObjOfCharacter.cs index 5f245664..254342e3 100644 --- a/logic/GameClass/GameObj/ObjOfCharacter.cs +++ b/logic/GameClass/GameObj/ObjOfCharacter.cs @@ -16,17 +16,27 @@ public ICharacter? Parent { get { - lock (objOfCharacterReaderWriterLock) + objOfCharacterReaderWriterLock.EnterReadLock(); + try { return parent; } + finally + { + objOfCharacterReaderWriterLock.ExitReadLock(); + } } set { - lock (objOfCharacterReaderWriterLock) + objOfCharacterReaderWriterLock.EnterWriteLock(); + try { parent = value; } + finally + { + objOfCharacterReaderWriterLock.ExitWriteLock(); + } } } // LHR注:本来考虑在构造函数里设置parent属性,见THUAI4在游戏引擎中才设置该属性,作罢。——2021/9/24 diff --git a/logic/GameClass/GameObj/Prop/Gadget.cs b/logic/GameClass/GameObj/Prop/Gadget.cs index 85565cce..74f545cc 100644 --- a/logic/GameClass/GameObj/Prop/Gadget.cs +++ b/logic/GameClass/GameObj/Prop/Gadget.cs @@ -24,8 +24,8 @@ public override bool IgnoreCollideExecutor(IGameObj targetObj) public Gadget(XY initPos, int radius = GameData.propRadius) : base(initPos, radius, GameObjType.Gadget) { - this.CanMove.Set(false); - this.MoveSpeed.Set(GameData.propMoveSpeed); + this.CanMove.SetReturnOri(false); + this.MoveSpeed.SetReturnOri(GameData.propMoveSpeed); } } public abstract class Tool : Gadget diff --git a/logic/GameClass/GameObj/Prop/Item.cs b/logic/GameClass/GameObj/Prop/Item.cs index 2b83ceaf..881e38fa 100644 --- a/logic/GameClass/GameObj/Prop/Item.cs +++ b/logic/GameClass/GameObj/Prop/Item.cs @@ -17,8 +17,8 @@ public abstract class Item : ObjOfCharacter public Item(XY initPos, int radius = GameData.propRadius) : base(initPos, radius, GameObjType.Item) { - this.CanMove.Set(false); - this.MoveSpeed.Set(0); + this.CanMove.SetReturnOri(false); + this.MoveSpeed.SetReturnOri(0); } } diff --git a/logic/GameEngine/CollisionChecker.cs b/logic/GameEngine/CollisionChecker.cs index 1a7a2e31..b0f3e65f 100644 --- a/logic/GameEngine/CollisionChecker.cs +++ b/logic/GameEngine/CollisionChecker.cs @@ -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 @@ internal class CollisionChecker public IGameObj? CheckCollision(IMoveable obj, XY Pos) { // 在列表中检查碰撞 - Func, ReaderWriterLockSlim, IGameObj?> CheckCollisionInList = - (IEnumerable lst, ReaderWriterLockSlim listLock) => + Func, IGameObj?> CheckCollisionInList = + (LockedClassList 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 @@ public double FindMax(IMoveable obj, XY nextPos, XY moveVec) 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, ReaderWriterLockSlim>[] lists; + private readonly LockedClassList[] lists; public CollisionChecker(IMap gameMap) { this.gameMap = gameMap; - lists = new Tuple, ReaderWriterLockSlim>[gameMap.GameObjDict.Count]; - int i = 0; - foreach (var keyValuePair in gameMap.GameObjDict) - { - lists[i++] = new Tuple, ReaderWriterLockSlim>(keyValuePair.Value as IList, gameMap.GameObjLockDict[keyValuePair.Key]); - } + lists = gameMap.GameObjDict.Values.ToArray(); } } } diff --git a/logic/GameEngine/MoveEngine.cs b/logic/GameEngine/MoveEngine.cs index a2236b79..d4c76f78 100644 --- a/logic/GameEngine/MoveEngine.cs +++ b/logic/GameEngine/MoveEngine.cs @@ -102,7 +102,7 @@ public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum lock (obj.ActionLock) { if (!obj.IsAvailableForMove) { EndMove(obj); return; } - obj.IsMoving.Set(true); + obj.IsMoving.SetReturnOri(true); } new Thread @@ -139,7 +139,7 @@ public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum if (isEnded) { - obj.IsMoving.Set(false); + obj.IsMoving.SetReturnOri(false); EndMove(obj); return; } @@ -184,7 +184,7 @@ public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum } if (isEnded) { - obj.IsMoving.Set(false); + obj.IsMoving.SetReturnOri(false); EndMove(obj); return; } @@ -224,7 +224,7 @@ public void MoveObj(IMoveable obj, int moveTime, double direction, long stateNum } } while (flag); } - obj.IsMoving.Set(false); // 结束移动 + obj.IsMoving.SetReturnOri(false); // 结束移动 EndMove(obj); } } diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index e2961b6d..645f7ea9 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -2,6 +2,7 @@ using System.Threading; using GameClass.GameObj; using GameEngine; +using Preparation.Interface; using Preparation.Utility; using Timothy.FrameRateTask; @@ -148,11 +149,11 @@ public bool OpenDoorway(Student player) player.ThreadNum.Release(); return; } - Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree); + Thread.Sleep(GameData.degreeOfOpenedDoorway - (int)doorwayToOpen.ProgressOfDoorway.GetProgressNow()); if (player.ResetPlayerState(stateNum)) { - doorwayToOpen.FinishOpenning(); + doorwayToOpen.ProgressOfDoorway.Finish(); player.ThreadNum.Release(); } } @@ -168,7 +169,7 @@ public bool Escape(Student player) return false; Doorway? doorwayForEscape = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway); - if (doorwayForEscape != null && doorwayForEscape.IsOpen()) + if (doorwayForEscape != null && doorwayForEscape.ProgressOfDoorway.IsProgressing()) { if (!player.TryToRemoveFromGame(PlayerStateType.Escaped)) return false; player.AddScore(GameData.StudentScoreEscape); @@ -199,7 +200,7 @@ public bool Treat(Student player, Student? playerTreated = null) } else if (!GameData.ApproachToInteract(playerTreated.Position, player.Position)) return false; - if (playerTreated.HP == playerTreated.MaxHp) return false; + if (playerTreated.HP == playerTreated.HP.GetMaxV()) return false; long stateNumTreated = playerTreated.SetPlayerState(RunningStateType.Waiting, PlayerStateType.Treated); if (stateNumTreated == -1) return false; @@ -318,10 +319,10 @@ public bool Rescue(Student player, Student? playerRescued = null) { if (playerRescued.StateNum == stateNumRescued) { - if (playerRescued.AddTimeOfRescue(GameData.checkInterval)) + if (playerRescued.TimeOfRescue.Add(GameData.checkInterval) >= GameData.basicTimeOfRescue) { playerRescued.SetPlayerStateNaturally(); - playerRescued.SetHP(playerRescued.MaxHp / 2); + playerRescued.HP.SetPositiveV(playerRescued.HP.GetMaxV() / 2); player.AddScore(GameData.StudentScoreRescue); return false; } @@ -334,7 +335,7 @@ public bool Rescue(Student player, Student? playerRescued = null) finallyReturn: () => 0 ) .Start(); - playerRescued.SetTimeOfRescue(0); + playerRescued.TimeOfRescue.SetReturnOri(0); player.ThreadNum.Release(); playerRescued.ThreadNum.Release(); @@ -369,7 +370,7 @@ public bool OpenChest(Character player) return; } else - if (!chestToOpen.Open(player)) + if (!chestToOpen.OpenProgress.Start((long)(GameData.degreeOfOpenedChest / player.SpeedOfOpenChest))) { player.ThreadNum.Release(); player.SetPlayerStateNaturally(); @@ -450,12 +451,12 @@ public bool ClimbingThroughWindow(Character player) player.ReSetPos(windowToPlayer + windowForClimb.Position); } - player.MoveSpeed.Set(player.SpeedOfClimbingThroughWindows); + player.MoveSpeed.SetReturnOri(player.SpeedOfClimbingThroughWindows); moveEngine.MoveObj(player, (int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2), (-1 * windowToPlayer).Angle(), stateNum); Thread.Sleep((int)(GameData.numOfPosGridPerCell * 3 * 1000 / player.MoveSpeed / 2)); - player.MoveSpeed.Set(player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed)); + player.MoveSpeed.SetReturnOri(player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed)); lock (player.ActionLock) { @@ -513,7 +514,7 @@ public bool LockDoor(Character player) { if ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) != null) return false; - if (doorToLock.LockDegree.Add(GameData.checkInterval * player.SpeedOfOpeningOrLocking) >= GameData.basicSpeedOfOpeningOrLocking) + if (doorToLock.LockDegree.Add(GameData.checkInterval * player.SpeedOfOpeningOrLocking) >= GameData.degreeOfLockingOrOpeningTheDoor) return false; return true; }, diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index ac7aea7a..98e6bb9c 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -33,7 +33,7 @@ public AttackManager(Map gameMap, CharacterManager characterManager) Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64); if (obj.CanMove && ((Bullet)obj).TypeOfBullet != BulletType.JumpyDumpty) BulletBomb((Bullet)obj, null); - obj.CanMove.Set(false); + obj.CanMove.SetReturnOri(false); } ); this.characterManager = characterManager; @@ -89,7 +89,7 @@ public bool TryRemoveBullet(Bullet bullet) { if (gameMap.Remove(bullet)) { - bullet.CanMove.Set(false); + bullet.CanMove.SetReturnOri(false); if (bullet.BulletBombRange > 0) { BombedBullet bombedBullet = new(bullet); @@ -168,20 +168,11 @@ private void BulletBomb(Bullet bullet, GameObj? objBeingShot) { 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); + } } } @@ -203,7 +194,7 @@ public bool Attack(Character player, double angle) { // 子弹如果没有和其他物体碰撞,将会一直向前直到超出人物的attackRange if (!player.Commandable()) return false; - Bullet? bullet = player.Attack(angle, gameMap.Timer.nowTime()); + Bullet? bullet = player.Attack(angle); if (bullet != null) { diff --git a/logic/Gaming/CharacterManager.cs b/logic/Gaming/CharacterManager.cs index 86c8fef5..157d4a38 100644 --- a/logic/Gaming/CharacterManager.cs +++ b/logic/Gaming/CharacterManager.cs @@ -57,41 +57,8 @@ public void DoubleFactorTeacher() } gameMap.Add(newPlayer); - newPlayer.TeamID = teamID; - newPlayer.PlayerID = playerID; - /* #region 人物装弹 - new Thread - ( - () => - { - while (!gameMap.Timer.IsGaming) - Thread.Sleep(Math.Max(newPlayer.CD, GameData.checkInterval)); - long lastTime = Environment.TickCount64; - new FrameRateTaskExecutor( - loopCondition: () => gameMap.Timer.IsGaming && !newPlayer.IsRemoved, - loopToDo: () => - { - long nowTime = Environment.TickCount64; - if (newPlayer.BulletNum == newPlayer.MaxBulletNum) - lastTime = nowTime; - else if (nowTime - lastTime >= newPlayer.CD) - { - _ = newPlayer.TryAddBulletNum(); - lastTime = nowTime; - } - }, - timeInterval: GameData.checkInterval, - finallyReturn: () => 0 - ) - { - AllowTimeExceed = true, - } - .Start(); - } - ) - { IsBackground = true }.Start(); - #endregion - */ + newPlayer.TeamID.SetReturnOri(teamID); + newPlayer.PlayerID.SetReturnOri(playerID); #region BGM,牵制得分更新 new Thread ( @@ -104,96 +71,72 @@ public void DoubleFactorTeacher() 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( 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, @@ -229,9 +172,9 @@ public void BeAddictedToGame(Student player, Ghost ghost) if (player.GamingAddiction > 0) { if (player.GamingAddiction < GameData.BeginGamingAddiction) - player.GamingAddiction = GameData.BeginGamingAddiction; + player.GamingAddiction.SetV(GameData.BeginGamingAddiction); else if (player.GamingAddiction < GameData.MidGamingAddiction) - player.GamingAddiction = GameData.MidGamingAddiction; + player.GamingAddiction.SetV(GameData.MidGamingAddiction); else { Die(player); @@ -247,16 +190,16 @@ public void BeAddictedToGame(Student player, Ghost ghost) Debugger.Output(player, " is addicted "); new FrameRateTaskExecutor( - () => stateNum == player.StateNum && player.GamingAddiction < player.MaxGamingAddiction && gameMap.Timer.IsGaming, + () => stateNum == player.StateNum && !player.GamingAddiction.IsMaxV() && gameMap.Timer.IsGaming, () => { - player.GamingAddiction += (player.PlayerState == PlayerStateType.Addicted) ? GameData.frameDuration : 0; + if (player.PlayerState == PlayerStateType.Addicted) player.GamingAddiction.AddPositiveV(GameData.frameDuration); }, timeInterval: GameData.frameDuration, () => { gameMap.MapRescueStudent(); - if (player.GamingAddiction == player.MaxGamingAddiction && gameMap.Timer.IsGaming) + if (player.GamingAddiction.IsMaxV() && gameMap.Timer.IsGaming) { Die(player); } @@ -311,7 +254,7 @@ public void BeAttacked(Student student, Bullet bullet) if (student.CharacterType == CharacterType.StraightAStudent) { - ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation.Set(0); + ((WriteAnswers)student.FindActiveSkill(ActiveSkillType.WriteAnswers)).DegreeOfMeditation.SetReturnOri(0); } student.SetDegreeOfTreatment0(); @@ -322,10 +265,10 @@ public void BeAttacked(Student student, Bullet bullet) { if (bullet.HasSpear) { - long subHp = student.SubHp(bullet.AP); + long subHp = student.HP.SubPositiveV(bullet.AP); Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp) + GameData.ScorePropUseSpear); - bullet.Parent.AddHP((long)bullet.Parent.Vampire * subHp); + bullet.Parent.HP.AddPositiveV((long)bullet.Parent.Vampire * subHp); } else return; } @@ -334,12 +277,12 @@ public void BeAttacked(Student student, Bullet bullet) long subHp; if (bullet.HasSpear) { - subHp = student.SubHp(bullet.AP + GameData.ApSpearAdd); + subHp = student.HP.SubPositiveV(bullet.AP + GameData.ApSpearAdd); Debugger.Output(this, "is being shot with Spear! Now his hp is" + student.HP.ToString()); } else { - subHp = student.SubHp(bullet.AP); + subHp = student.HP.SubPositiveV(bullet.AP); Debugger.Output(this, "is being shot! Now his hp is" + student.HP.ToString()); } bullet.Parent.AddScore(GameData.TrickerScoreAttackStudent(subHp)); @@ -348,7 +291,7 @@ public void BeAttacked(Student student, Bullet bullet) student.AddScore(subHp * GameData.factorOfScoreWhenTeacherAttacked / GameData.basicApOfGhost / FactorTeacher); } - bullet.Parent.AddHP((long)(bullet.Parent.Vampire * subHp)); + bullet.Parent.HP.AddPositiveV((long)(bullet.Parent.Vampire * subHp)); } if (student.HP <= 0) student.TryActivatingLIFE(); // 如果有复活甲 diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 96db6753..2b47d90e 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -84,7 +84,7 @@ public bool MovePlayer(long playerID, int moveTimeInMilliseconds, double angle) else { #if DEBUG - Console.WriteLine($"PlayerID:{playerID} player does not exists!"); + Console.WriteLine($"playerID:{playerID} player does not exists!"); #endif return false; @@ -281,17 +281,9 @@ public void AllPlayerUsePassiveSkill() { 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 @@ public void ClearAllLists() { 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.Set(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 @@ public List GetGameObj() { 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; diff --git a/logic/Gaming/PropManager.cs b/logic/Gaming/PropManager.cs index 75426af6..4a1f18f4 100644 --- a/logic/Gaming/PropManager.cs +++ b/logic/Gaming/PropManager.cs @@ -44,9 +44,9 @@ public void ConsumeProp(Character player, PropType propType) break; case PropType.AddHpOrAp: if (!player.IsGhost()) - if (player.HP < player.MaxHp) + if (player.HP < player.HP.GetMaxV()) { - player.AddHP(GameData.basicTreatmentDegree / 2); + player.HP.AddPositiveV(GameData.basicTreatmentDegree / 2); player.AddScore(GameData.ScorePropAddHp); } else player.AddAp(GameData.PropDuration); @@ -83,24 +83,16 @@ public bool PickProp(Character player, PropType propType = PropType.Null) } 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 @@ public void StartProducing() 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 ( diff --git a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs index c4f4aa51..e3a5eafe 100644 --- a/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.ActiveSkill.cs @@ -43,35 +43,27 @@ public bool ShowTime(Character player) 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 += GameData.checkIntervalWhenShowTime; - if (student.GamingAddiction == student.MaxGamingAddiction) - { - characterManager.Die(student); - } + characterManager.Die(student); } } } } - finally - { - gameMap.GameObjLockDict[GameObjType.Character].ExitReadLock(); - } }, timeInterval: GameData.checkIntervalWhenShowTime, maxTotalDuration: skill.DurationTime, @@ -178,25 +170,18 @@ public bool SparksNSplash(Character player, int AttackID) 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.Set(true); + homingMissile.CanMove.SetReturnOri(true); attackManager.moveEngine.MoveObj(homingMissile, GameData.checkIntervalWhenSparksNSplash - 1, (whoAttacked.Position - homingMissile.Position).Angle(), ++homingMissile.StateNum); } }, @@ -227,7 +212,7 @@ public bool WriteAnswers(Character player) 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.Set(0); + ((WriteAnswers)activeSkill).DegreeOfMeditation.SetReturnOri(0); } }, () => @@ -309,22 +294,14 @@ public bool Howl(Character player) 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 @@ public bool Punish(Character player) 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.MaxHp - 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 @@ public bool Rouse(Character player) 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.SetHP(GameData.RemainHpWhenAddLife); - ((Student)character).SetTimeOfRescue(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 @@ public bool Encourage(Character player) 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.MaxHp) && gameMap.CanSee(player, character)) - { - player.AddScore(GameData.StudentScoreTreat(GameData.addHpWhenEncourage)); - character.AddHP(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 @@ public bool Inspire(Character player) 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!"); }, () => @@ -499,7 +444,7 @@ public static bool ActiveSkillEffect(ActiveSkill activeSkill, Character player, { lock (activeSkill.ActiveSkillUseLock) { - if (activeSkill.StartSkill()) + if (activeSkill.SkillCD.TryUse()) { new Thread (() => diff --git a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs index b22254ef..3cc89c78 100644 --- a/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs +++ b/logic/Gaming/SkillManager/SkillManager.PassiveSkill.cs @@ -26,7 +26,7 @@ public void Meditate(Character player) () => { if (player.Commandable() && player.PlayerState != PlayerStateType.Fixing) activeSkill.DegreeOfMeditation.Add(learningDegree * GameData.frameDuration); - else activeSkill.DegreeOfMeditation.Set(0); + else activeSkill.DegreeOfMeditation.SetReturnOri(0); //Debugger.Output(player, "with " + (((WriteAnswers)activeSkill).DegreeOfMeditation).ToString()); }, timeInterval: GameData.frameDuration, diff --git a/logic/Preparation/Interface/ICharacter.cs b/logic/Preparation/Interface/ICharacter.cs index 07f0b938..a6e6a5d0 100644 --- a/logic/Preparation/Interface/ICharacter.cs +++ b/logic/Preparation/Interface/ICharacter.cs @@ -1,21 +1,19 @@ -using System; -using Preparation.Utility; +using Preparation.Utility; namespace Preparation.Interface { public interface ICharacter : IMoveable { - public long TeamID { get; } - public long HP { get; } - public long AddHP(long add); + public AtomicLong TeamID { get; } + public LongInTheVariableRange HP { get; } public long Score { get; } public void AddScore(long add); - public double Vampire { get; } + public DoubleInTheVariableRange Vampire { get; } public PlayerStateType PlayerState { get; } public BulletType BulletOfPlayer { get; set; } public CharacterType CharacterType { get; } public ActiveSkill FindActiveSkill(ActiveSkillType activeSkillType); - public int UpdateBulletNum(int time); + public IntNumUpdateEachCD BulletNum { get; } public long SetPlayerState(RunningStateType running, PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); public bool ResetPlayerState(long state, RunningStateType running = RunningStateType.Null, PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null); diff --git a/logic/Preparation/Interface/IChest.cs b/logic/Preparation/Interface/IChest.cs deleted file mode 100644 index d787cde9..00000000 --- a/logic/Preparation/Interface/IChest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Preparation.Utility; - -namespace Preparation.Interface -{ - public interface IChest : IGameObj - { - public void StopOpen(); - } -} \ No newline at end of file diff --git a/logic/Preparation/Interface/IDoorway.cs b/logic/Preparation/Interface/IDoorway.cs index b48be758..942f1032 100644 --- a/logic/Preparation/Interface/IDoorway.cs +++ b/logic/Preparation/Interface/IDoorway.cs @@ -4,9 +4,6 @@ namespace Preparation.Interface { public interface IDoorway : IGameObj { - public long OpenStartTime { get; } - public int OpenDegree { get; } - public bool StopOpenning(); - public bool TryToOpen(); + public TimeBasedProgressAtVariableSpeed ProgressOfDoorway { get; } } } \ No newline at end of file diff --git a/logic/Preparation/Interface/IMap.cs b/logic/Preparation/Interface/IMap.cs index e504121d..11346539 100644 --- a/logic/Preparation/Interface/IMap.cs +++ b/logic/Preparation/Interface/IMap.cs @@ -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 @@ public interface IMap ITimer Timer { get; } // the two dicts must have same keys - Dictionary> GameObjDict { get; } - Dictionary GameObjLockDict { get; } + Dictionary> GameObjDict { get; } public uint[,] ProtoGameMap { get; } public PlaceType GetPlaceType(IGameObj obj); diff --git a/logic/Preparation/Interface/ISkill.cs b/logic/Preparation/Interface/ISkill.cs index bc3f2d60..02aa6407 100644 --- a/logic/Preparation/Interface/ISkill.cs +++ b/logic/Preparation/Interface/ISkill.cs @@ -13,7 +13,7 @@ public interface IPassiveSkill : ISkill } public interface IActiveSkill : ISkill { - public int SkillCD { get; } + public LongProgressUpdateEachCD SkillCD { get; } public int DurationTime { get; } //技能持续时间 public object ActiveSkillUseLock { get; } public bool IsBeingUsed { get; set; } @@ -21,7 +21,7 @@ public interface IActiveSkill : ISkill public abstract class ActiveSkill : IActiveSkill { - public abstract int SkillCD { get; } + public abstract LongProgressUpdateEachCD SkillCD { get; } public abstract int DurationTime { get; } private readonly object activeSkillUseLock = new(); @@ -30,28 +30,6 @@ public abstract class ActiveSkill : IActiveSkill private readonly object skillLock = new(); public object SkillLock => skillLock; - private long startTime = Environment.TickCount64 - 600000; - public long StartTime - { - get - { - lock (skillLock) - return startTime; - } - } - public bool StartSkill() - { - lock (skillLock) - { - if (Environment.TickCount64 - startTime >= SkillCD) - { - startTime = Environment.TickCount64; - return true; - } - } - return false; - } - public int isBeingUsed = 0;//实为bool public bool IsBeingUsed { @@ -62,79 +40,79 @@ public bool IsBeingUsed public class CanBeginToCharge : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 2; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 2); public override int DurationTime => GameData.commonSkillTime * 3 / 10; } public class BecomeInvisible : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 4 / 3; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 4 / 3); public override int DurationTime => GameData.commonSkillTime; } public class Punish : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 45 / 30; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 45 / 30); public override int DurationTime => 0; } public class HaveTea : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 3; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 3); public override int DurationTime => 0; } public class Rouse : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 4; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 4); public override int DurationTime => 0; } public class Encourage : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 4; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 4); public override int DurationTime => 0; } public class Inspire : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 4; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 4); public override int DurationTime => 0; } public class Howl : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 25 / 30; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 25 / 30); public override int DurationTime => 0; } public class ShowTime : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 8 / 3; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 8 / 3); public override int DurationTime => GameData.commonSkillTime; } public class JumpyBomb : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD / 2; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD / 2); public override int DurationTime => GameData.commonSkillTime * 3 / 10; } public class SparksNSplash : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 45 / 30; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 45 / 30); public override int DurationTime => GameData.commonSkillTime; } public class UseKnife : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD); public override int DurationTime => GameData.commonSkillTime / 10; } public class UseRobot : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD / 300; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD / 300); public override int DurationTime => 0; private int nowPlayerID; @@ -171,15 +149,16 @@ public bool TryResetNowPlayerID(int tryPlayerID) public class WriteAnswers : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD); public override int DurationTime => 0; - public AtomicInt DegreeOfMeditation { get; } = new(0); + private AtomicInt degreeOfMeditation = new(0); + public AtomicInt DegreeOfMeditation { get => degreeOfMeditation; } } public class SummonGolem : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD * 4 / 3; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD * 4 / 3); public override int DurationTime => 6000; private int[] golemStateArray = new int[GameData.maxSummonedGolemNum] { 0, 0, 0 }; @@ -232,7 +211,7 @@ public void AddGolem(int num) public class NullSkill : ActiveSkill { - public override int SkillCD => GameData.commonSkillCD; + public override LongProgressUpdateEachCD SkillCD { get; } = new(GameData.commonSkillCD); public override int DurationTime => GameData.commonSkillTime; } diff --git a/logic/Preparation/Preparation.csproj b/logic/Preparation/Preparation.csproj index 322b60fb..8d6f6435 100644 --- a/logic/Preparation/Preparation.csproj +++ b/logic/Preparation/Preparation.csproj @@ -9,7 +9,7 @@ - + diff --git a/logic/Preparation/Utility/SafeValue.cs b/logic/Preparation/Utility/SafeValue.cs deleted file mode 100644 index 90b1f26b..00000000 --- a/logic/Preparation/Utility/SafeValue.cs +++ /dev/null @@ -1,99 +0,0 @@ -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); - } - //增加新的写操作可能导致不安全 - } -} diff --git a/logic/Preparation/Utility/SafeValue/Atomic.cs b/logic/Preparation/Utility/SafeValue/Atomic.cs new file mode 100644 index 00000000..5f9b31ec --- /dev/null +++ b/logic/Preparation/Utility/SafeValue/Atomic.cs @@ -0,0 +1,296 @@ +using System; +using System.Threading; + +namespace Preparation.Utility +{ + //其对应属性不应当有set访问器,避免不安全的=赋值 + public abstract class Atomic + { + } + + public class AtomicInt : Atomic + { + protected 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 virtual int SetReturnOri(int value) => Interlocked.Exchange(ref v, value); + public virtual int Add(int x) => Interlocked.Add(ref v, x); + /// + /// 注意:确保参数为非负数 + /// + public virtual int AddPositive(int x) => Interlocked.Add(ref v, x); + public virtual int Sub(int x) => Interlocked.Add(ref v, -x); + /// + /// 注意:确保参数为非负数 + /// + public virtual int SubPositive(int x) => Interlocked.Add(ref v, -x); + public virtual int Inc() => Interlocked.Increment(ref v); + public virtual int Dec() => Interlocked.Decrement(ref v); + /// 返回操作前的值 + public virtual int CompareExReturnOri(int newV, int compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + + /// + /// 参数要求倍率speed(默认1)以及AtomicInt类的Score, + /// 在发生正向的变化时,自动给Score加上正向变化的差乘以speed取整。 + /// 注意:AtomicIntOnlyAddScore本身为AtomicInt,提供的Score可能构成环而死锁。 + /// + public class AtomicIntOnlyAddScore : AtomicInt + { + public AtomicInt Score { get; set; } + public AtomicDouble speed; + public AtomicIntOnlyAddScore(int x, AtomicInt Score, double speed = 1.0) : base(x) + { + this.Score = Score; + this.speed = new(speed); + } + /// 返回操作前的值 + public override int SetReturnOri(int value) + { + int previousV = Interlocked.Exchange(ref v, value); + if (value - previousV > 0) + Score.AddPositive((int)(speed * (value - previousV))); + return previousV; + } + public override int Add(int x) + { + if (x > 0) Score.AddPositive((int)(speed * x)); + return Interlocked.Add(ref v, x); + } + /// + /// 注意:确保参数为非负数 + /// + public override int AddPositive(int x) + { + Score.AddPositive((int)(speed * x)); + return Interlocked.Add(ref v, x); + } + public override int Sub(int x) + { + if (x < 0) Score.AddPositive((int)(speed * -x)); + return Interlocked.Add(ref v, -x); + } + /// + /// 注意:确保参数为非负数 + /// + public override int SubPositive(int x) + { + return Interlocked.Add(ref v, -x); + } + public override int Inc() + { + Score.AddPositive((int)(speed)); + return Interlocked.Increment(ref v); + } + /// 返回操作前的值 + public override int CompareExReturnOri(int newV, int compareTo) + { + int previousV = Interlocked.CompareExchange(ref v, newV, compareTo); + if (newV - previousV > 0) + Score.AddPositive((int)(speed * (newV - previousV))); + return previousV; + } + } + + /// + /// 参数要求倍率speed(默认1)以及AtomicInt类的Score, + /// 在发生变化时,自动给Score加上变化的差乘以speed取整。 + /// 注意:AtomicIntChangeAffectScore本身为AtomicInt,提供的Score可能构成环而死锁。 + /// + public class AtomicIntChangeAffectScore : AtomicInt + { + public AtomicInt Score { get; set; } + public AtomicDouble speed; + public AtomicIntChangeAffectScore(int x, AtomicInt Score, double speed = 1.0) : base(x) + { + this.Score = Score; + this.speed = new(speed); + } + /// 返回操作前的值 + public override int SetReturnOri(int value) + { + int previousV = Interlocked.Exchange(ref v, value); + Score.Add((int)(speed * (value - previousV))); + return previousV; + } + public override int Add(int x) + { + Score.Add((int)(speed * x)); + return Interlocked.Add(ref v, x); + } + /// + /// 注意:确保参数为非负数 + /// + public override int AddPositive(int x) + { + Score.AddPositive((int)(speed * x)); + return Interlocked.Add(ref v, x); + } + public override int Sub(int x) + { + Score.Sub((int)(speed * x)); + return Interlocked.Add(ref v, -x); + } + /// + /// 注意:确保参数为非负数 + /// + public override int SubPositive(int x) + { + Score.SubPositive((int)(speed * x)); + return Interlocked.Add(ref v, -x); + } + public override int Inc() + { + Score.AddPositive((int)(speed)); + return Interlocked.Increment(ref v); + } + public override int Dec() + { + Score.SubPositive((int)(speed)); + return Interlocked.Decrement(ref v); + } + /// 返回操作前的值 + public override int CompareExReturnOri(int newV, int compareTo) + { + int previousV = Interlocked.CompareExchange(ref v, newV, compareTo); + Score.Add((int)(speed * (newV - previousV))); + return previousV; + } + } + + public class AtomicLong : Atomic + { + protected long v; + public AtomicLong(long x) + { + v = x; + } + public override string ToString() => Interlocked.CompareExchange(ref v, -1, -1).ToString(); + public long Get() => Interlocked.CompareExchange(ref v, -1, -1); + public static implicit operator long(AtomicLong along) => Interlocked.CompareExchange(ref along.v, -1, -1); + /// 返回操作前的值 + public virtual long SetReturnOri(long value) => Interlocked.Exchange(ref v, value); + public virtual long Add(long x) => Interlocked.Add(ref v, x); + /// + /// 注意:确保参数为非负数 + /// + public virtual long AddPositive(long x) => Interlocked.Add(ref v, x); + public virtual long Sub(long x) => Interlocked.Add(ref v, -x); + /// + /// 注意:确保参数为非负数 + /// + public virtual long SubPositive(long x) => Interlocked.Add(ref v, -x); + public virtual long Inc() => Interlocked.Increment(ref v); + public virtual long Dec() => Interlocked.Decrement(ref v); + /// 返回操作前的值 + public virtual long CompareExReturnOri(long newV, long compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + + /// + /// 参数要求倍率speed(默认1)以及AtomicLong类的Score, + /// 在发生正向的变化时,自动给Score加上正向变化的差乘以speed取整。 + /// 注意:AtomicLongOnlyAddScore本身为AtomicLong,提供的Score可能构成环而死锁。 + /// + public class AtomicLongOnlyAddScore : AtomicLong + { + public AtomicLong Score { get; set; } + public AtomicDouble speed; + public AtomicLongOnlyAddScore(long x, AtomicLong score, double speed = 1.0) : base(x) + { + this.Score = score; + this.speed = new(speed); + } + /// 返回操作前的值 + public override long SetReturnOri(long value) + { + long previousV = Interlocked.Exchange(ref v, value); + if (value - previousV > 0) + Score.AddPositive((long)(speed * (value - previousV))); + return previousV; + } + public override long Add(long x) + { + if (x > 0) Score.AddPositive((long)(speed * x)); + return Interlocked.Add(ref v, x); + } + /// + /// 注意:确保参数为非负数 + /// + public override long AddPositive(long x) + { + Score.AddPositive((long)(speed * x)); + return Interlocked.Add(ref v, x); + } + public override long Sub(long x) + { + if (x < 0) Score.AddPositive((long)(speed * -x)); + return Interlocked.Add(ref v, -x); + } + /// + /// 注意:确保参数为非负数 + /// + public override long SubPositive(long x) + { + return Interlocked.Add(ref v, -x); + } + public override long Inc() + { + Score.AddPositive((long)(speed)); + return Interlocked.Increment(ref v); + } + /// 返回操作前的值 + public override long CompareExReturnOri(long newV, long compareTo) + { + long previousV = Interlocked.CompareExchange(ref v, newV, compareTo); + if (newV - previousV > 0) + Score.AddPositive((long)(speed * (newV - previousV))); + return previousV; + } + } + + public class AtomicDouble : Atomic + { + private double v; + public AtomicDouble(double x) + { + v = x; + } + public override string ToString() => Interlocked.CompareExchange(ref v, -2.0, -2.0).ToString(); + public double Get() => Interlocked.CompareExchange(ref v, -2.0, -2.0); + public static implicit operator double(AtomicDouble adouble) => Interlocked.CompareExchange(ref adouble.v, -2.0, -2.0); + /// 返回操作前的值 + public double SetReturnOri(double value) => Interlocked.Exchange(ref v, value); + /// 返回操作前的值 + public double CompareExReturnOri(double newV, double compareTo) => Interlocked.CompareExchange(ref v, newV, compareTo); + } + + public class AtomicBool : Atomic + { + private int v;//v&1==0为false,v&1==1为true + public AtomicBool(bool x) + { + v = x ? 1 : 0; + } + public override string ToString() => ((Interlocked.CompareExchange(ref v, -2, -2) & 1) == 0) ? "false" : "true"; + public bool Get() => ((Interlocked.CompareExchange(ref v, -2, -2) & 1) == 1); + public static implicit operator bool(AtomicBool abool) => abool.Get(); + /// 返回操作前的值 + public bool SetReturnOri(bool value) => ((Interlocked.Exchange(ref v, value ? 1 : 0) & 1) == 1); + /// 赋值前的值是否与将赋予的值不相同 + public bool TrySet(bool value) + { + return ((Interlocked.Exchange(ref v, value ? 1 : 0) & 1) != (value ? 1 : 0)); + } + public bool And(bool x) => (Interlocked.And(ref v, x ? 1 : 0) & 1) == 1; + public bool Or(bool x) => (Interlocked.Or(ref v, x ? 1 : 0) & 1) == 1; + /// 返回操作后的值 + public bool Reverse() => (Interlocked.Increment(ref v) & 1) == 1; + public bool Xor(bool x) => (Interlocked.Add(ref v, x ? 1 : 0) & 1) == 1; + } +} \ No newline at end of file diff --git a/logic/Preparation/Utility/SafeValue/InTheRange.cs b/logic/Preparation/Utility/SafeValue/InTheRange.cs new file mode 100644 index 00000000..f08f2e04 --- /dev/null +++ b/logic/Preparation/Utility/SafeValue/InTheRange.cs @@ -0,0 +1,996 @@ +using System; +using System.Threading; + +namespace Preparation.Utility +{ + //其对应属性不应当有set访问器,避免不安全的=赋值 + + public class InTheVariableRange + { + protected readonly object vLock = new(); + public object VLock => vLock; + private static int numOfClass = 0; + public static int NumOfClass => numOfClass; + public readonly int idInClass; + public int IdInClass => idInClass; + public InTheVariableRange() + { + idInClass = Interlocked.Increment(ref numOfClass); + } + } + + /// + /// 一个保证在[0,maxValue]的可变int,支持可变的maxValue(请确保大于0) + /// + public class IntInTheVariableRange : InTheVariableRange + { + protected int v; + protected int maxV; + #region 构造与读取 + public IntInTheVariableRange(int value, int maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set IntInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = value < maxValue ? value : maxValue; + this.maxV = maxValue; + } + /// + /// 默认使Value=maxValue + /// + public IntInTheVariableRange(int maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set IntInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = this.maxV = maxValue; + } + public IntInTheVariableRange() : base() + { + v = this.maxV = int.MaxValue; + } + + public override string ToString() + { + lock (vLock) + { + return "value:" + v.ToString() + " , maxValue:" + maxV.ToString(); + } + } + public int GetValue() { lock (vLock) return v; } + public static implicit operator int(IntInTheVariableRange aint) => aint.GetValue(); + public int GetMaxV() { lock (vLock) return maxV; } + public (int, int) GetValueAndMaxV() { lock (vLock) return (v, maxV); } + public bool IsMaxV() { lock (vLock) return v == maxV; } + #endregion + + #region 内嵌读取(在锁的情况下读取内容同时读取其他更基本的外部数据) + public (int, long) GetValue(StartTime startTime) + { + lock (vLock) + { + return (v, startTime.Get()); + } + } + public (int, int, long) GetValueAndMaxValue(StartTime startTime) + { + lock (vLock) + { + return (v, maxV, startTime.Get()); + } + } + #endregion + + #region 普通设置MaxV与Value的值的方法 + /// + /// 若maxValue<=0则maxValue设为0并返回False + /// + public bool SetMaxV(int maxValue) + { + if (maxValue <= 0) + { + lock (vLock) + { + v = maxV = 0; + return false; + } + } + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + return true; + } + /// + /// 应当保证该maxValue>=0 + /// + public void SetPositiveMaxV(int maxValue) + { + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + } + + public int SetV(int value) + { + if (value <= 0) + { + lock (vLock) + { + return v = 0; + } + } + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + /// + /// 应当保证该value>=0 + /// + public int SetPositiveV(int value) + { + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + #endregion + + #region 特殊条件的设置MaxV与Value的值的方法 + /// + /// 如果当前值大于maxValue,则更新maxValue失败 + /// + public bool TrySetMaxV(int maxValue) + { + lock (vLock) + { + if (v > maxValue) return false; + maxV = maxValue; + return true; + } + } + public void SetVToMaxV() + { + lock (vLock) + { + v = maxV; + } + } + public bool Set0IfNotMaxor0() + { + lock (vLock) + { + if (v < maxV && v > 0) + { + v = 0; + return true; + } + } + return false; + } + public bool Set0IfMax() + { + lock (vLock) + { + if (v == maxV) + { + v = 0; + return true; + } + } + return false; + } + #endregion + + #region 普通运算 + /// 返回实际改变量 + public int AddV(int addV) + { + lock (vLock) + { + int previousV = v; + v += addV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证增加值大于0 + /// + /// 返回实际改变量 + public int AddPositiveV(int addPositiveV) + { + lock (vLock) + { + addPositiveV = Math.Min(addPositiveV, maxV - v); + v += addPositiveV; + } + return addPositiveV; + } + public void MulV(int mulV) + { + if (mulV <= 0) + { + lock (vLock) v = 0; + return; + } + lock (vLock) + { + if (v > maxV / mulV) v = maxV; //避免溢出 + else v *= mulV; + } + } + public void MulV(double mulV) + { + if (mulV <= 0) + { + lock (vLock) v = 0; + return; + } + lock (vLock) + { + if (v > maxV / mulV) v = maxV; //避免溢出 + else v = (int)(v * mulV); + } + } + /// + /// 应当保证乘数大于0 + /// + public void MulPositiveV(int mulPositiveV) + { + lock (vLock) + { + if (v > maxV / mulPositiveV) v = maxV; //避免溢出 + else v *= mulPositiveV; + } + } + /// + /// 应当保证乘数大于0 + /// + public void MulPositiveV(double mulPositiveV) + { + lock (vLock) + { + if (v > maxV / mulPositiveV) v = maxV; //避免溢出 + else v = (int)(v * mulPositiveV); + } + } + /// 返回实际改变量 + public int SubV(int subV) + { + lock (vLock) + { + int previousV = v; + v -= subV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证该减少值大于0 + /// + /// 返回实际改变量 + public int SubPositiveV(int subPositiveV) + { + lock (vLock) + { + subPositiveV = Math.Min(subPositiveV, v); + v -= subPositiveV; + } + return subPositiveV; + } + #endregion + + #region 特殊条件的运算 + /// + /// 试图加到满,如果无法加到maxValue则不加并返回-1 + /// + /// 返回实际改变量 + public int TryAddToMaxV(int addV) + { + lock (vLock) + { + if (maxV - v <= addV) + { + addV = maxV - v; + v = maxV; + return addV; + } + return -1; + } + } + #endregion + + #region 与InTheVariableRange类的运算,运算会影响该对象的值 + public int AddV(IntInTheVariableRange a) + { + if (this.idInClass == a.idInClass) return -1; + bool thisLock = false; + bool thatLock = false; + try + { + if (this.idInClass < a.idInClass) + { + Monitor.Enter(vLock, ref thisLock); + Monitor.Enter(a.VLock, ref thatLock); + } + else + { + Monitor.Enter(a.VLock, ref thatLock); + Monitor.Enter(vLock, ref thisLock); + } + int previousV = v; + v += a.GetValue(); + if (v > maxV) v = maxV; + a.SubPositiveV(v - previousV); + return v - previousV; + } + finally + { + if (thisLock) Monitor.Exit(vLock); + if (thatLock) Monitor.Exit(a.VLock); + } + } + public int SubV(IntInTheVariableRange a) + { + if (this.idInClass == a.idInClass) return -1; + bool thisLock = false; + bool thatLock = false; + try + { + if (this.idInClass < a.idInClass) + { + Monitor.Enter(vLock, ref thisLock); + Monitor.Enter(a.VLock, ref thatLock); + } + else + { + Monitor.Enter(a.VLock, ref thatLock); + Monitor.Enter(vLock, ref thisLock); + } + int previousV = v; + v -= a.GetValue(); + if (v < 0) v = 0; + a.SubPositiveV(previousV - v); + return previousV - v; + } + finally + { + if (thisLock) Monitor.Exit(vLock); + if (thatLock) Monitor.Exit(a.VLock); + } + } + #endregion + + #region 与StartTime类的特殊条件的运算,运算会影响StartTime类的值 + /// + /// 试图加到满,如果加上时间差*速度可以达到MaxV,则加上并使startTime变为long.MaxValue + /// 如果无法加到maxValue则不加 + /// + /// 返回试图加到的值与最大值 + public (int, int, long) TryAddToMaxV(StartTime startTime, double speed = 1.0) + { + lock (vLock) + { + long addV = (long)(startTime.StopIfPassing(maxV - v) * speed); + if (addV < 0) return (v, maxV, startTime.Get()); + if (maxV - v < addV) return (v = maxV, maxV, startTime.Get()); + return ((int)(v + addV), maxV, startTime.Get()); + } + } + /// + /// 增加量为时间差*速度,并将startTime变为long.MaxValue + /// + /// 返回实际改变量 + public int AddV(StartTime startTime, double speed = 1.0) + { + lock (vLock) + { + int previousV = v; + int addV = (int)((Environment.TickCount64 - startTime.Stop()) * speed); + if (addV < 0) v += addV; + else return 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + + /// + /// 试图加到满,如果加上时间差*速度可以达到MaxV,则加上并使startTime变为long.MaxValue + /// 如果无法加到maxValue则清零 + /// + /// 返回是否清零 + public bool Set0IfNotAddToMaxV(StartTime startTime, double speed = 1.0) + { + lock (vLock) + { + if (v == maxV) return false; + int addV = (int)(startTime.StopIfPassing(maxV - v) * speed); + if (addV < 0) + { + v = 0; + return true; + } + if (maxV - v < addV) + { + v = maxV; + return false; + } + v = 0; + return false; + } + } + #endregion + } + + /// + /// 一个保证在[0,maxValue]的可变long,支持可变的maxValue(请确保大于0) + /// + public class LongInTheVariableRange : InTheVariableRange + { + protected long v; + protected long maxV; + #region 构造与读取 + public LongInTheVariableRange(long value, long maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set SafaValues.LongInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = value < maxValue ? value : maxValue; + this.maxV = maxValue; + } + /// + /// 默认使Value=maxValue + /// + public LongInTheVariableRange(long maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set SafaValues.LongInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = this.maxV = maxValue; + } + public LongInTheVariableRange() : base() + { + v = this.maxV = long.MaxValue; + } + + public override string ToString() + { + lock (vLock) + { + return "value:" + v.ToString() + " , maxValue:" + maxV.ToString(); + } + } + public long GetValue() { lock (vLock) return v; } + public static implicit operator long(LongInTheVariableRange aint) => aint.GetValue(); + public long GetMaxV() { lock (vLock) return maxV; } + public (long, long) GetValueAndMaxV() { lock (vLock) return (v, maxV); } + public bool IsMaxV() + { + lock (vLock) + { + return v == maxV; + } + } + #endregion + + #region 普通设置MaxV与Value的值的方法 + /// + /// 若maxValue<=0则maxValue设为0并返回False + /// + public bool SetMaxV(long maxValue) + { + if (maxValue <= 0) + { + lock (vLock) + { + v = maxV = 0; + return false; + } + } + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + return true; + } + /// + /// 应当保证该maxValue>=0 + /// + public void SetPositiveMaxV(long maxValue) + { + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + } + + public long SetV(long value) + { + if (value <= 0) + { + lock (vLock) + { + return v = 0; + } + } + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + /// + /// 应当保证该value>=0 + /// + public long SetPositiveV(long value) + { + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + #endregion + + #region 普通运算 + /// 返回实际改变量 + public long AddV(long addV) + { + lock (vLock) + { + long previousV = v; + v += addV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证增加值大于0 + /// + /// 返回实际改变量 + public long AddPositiveV(long addPositiveV) + { + lock (vLock) + { + addPositiveV = Math.Min(addPositiveV, maxV - v); + v += addPositiveV; + } + return addPositiveV; + } + public void MulV(long mulV) + { + if (mulV <= 0) + { + lock (vLock) v = 0; + return; + } + lock (vLock) + { + if (v > maxV / mulV) v = maxV; //避免溢出 + else v *= mulV; + } + } + public void MulV(double mulV) + { + if (mulV <= 0) + { + lock (vLock) v = 0; + return; + } + lock (vLock) + { + if (v > maxV / mulV) v = maxV; //避免溢出 + else v = (long)(v * mulV); + } + } + /// + /// 应当保证乘数大于0 + /// + public void MulPositiveV(long mulPositiveV) + { + lock (vLock) + { + if (v > maxV / mulPositiveV) v = maxV; //避免溢出 + else v *= mulPositiveV; + } + } + /// + /// 应当保证乘数大于0 + /// + public void MulPositiveV(double mulPositiveV) + { + lock (vLock) + { + if (v > maxV / mulPositiveV) v = maxV; //避免溢出 + else v = (long)(v * mulPositiveV); + } + } + /// 返回实际改变量 + public long SubV(long subV) + { + lock (vLock) + { + long previousV = v; + v -= subV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证该减少值大于0 + /// + /// 返回实际改变量 + public long SubPositiveV(long subPositiveV) + { + lock (vLock) + { + subPositiveV = Math.Min(subPositiveV, v); + v -= subPositiveV; + } + return subPositiveV; + } + #endregion + + #region 特殊条件的设置MaxV与Value的值的方法 + /// + /// 如果当前值大于maxValue,则更新maxValue失败 + /// + public bool TrySetMaxV(long maxValue) + { + lock (vLock) + { + if (v > maxValue) return false; + maxV = maxValue; + return true; + } + } + public void SetVToMaxV() + { + lock (vLock) + { + v = maxV; + } + } + + public bool Set0IfNotMax() + { + lock (vLock) + { + if (v < maxV) + { + v = 0; + return true; + } + } + return false; + } + public bool Set0IfMax() + { + lock (vLock) + { + if (v == maxV) + { + v = 0; + return true; + } + } + return false; + } + #endregion + + #region 特殊条件的运算 + /// + /// 试图加到满,如果无法加到maxValue则不加并返回-1 + /// + /// 返回实际改变量 + public long TryAddToMaxV(long addV) + { + lock (vLock) + { + if (maxV - v <= addV) + { + addV = maxV - v; + v = maxV; + return addV; + } + return -1; + } + } + #endregion + + #region 与StartTime类的特殊条件的运算,运算会影响StartTime类的值 + /// + /// 试图加到满,如果加上时间差*速度可以达到MaxV,则加上并使startTime变为long.MaxValue + /// 如果无法加到maxValue则不加 + /// + /// 返回试图加到的值与最大值 + public (long, long, long) TryAddToMaxV(StartTime startTime, double speed = 1.0) + { + lock (vLock) + { + long addV = (long)(startTime.StopIfPassing(maxV - v) * speed); + if (addV < 0) return (v, maxV, startTime.Get()); + if (maxV - v < addV) return (v = maxV, maxV, startTime.Get()); + return (v + addV, maxV, startTime.Get()); + } + } + + #endregion + } + + /// + /// 一个保证在[0,maxValue]的可变double,支持可变的maxValue(请确保大于0) + /// + public class DoubleInTheVariableRange : InTheVariableRange + { + protected double v; + protected double maxV; + #region 构造与读取 + public DoubleInTheVariableRange(double value, double maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set DoubleInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = value < maxValue ? value : maxValue; + this.maxV = maxValue; + } + /// + /// 默认使Value=maxValue + /// + public DoubleInTheVariableRange(double maxValue) : base() + { + if (maxValue < 0) + { + Debugger.Output("Warning:Try to set DoubleInTheVariableRange.maxValue to " + maxValue.ToString() + "."); + maxValue = 0; + } + v = this.maxV = maxValue; + } + public DoubleInTheVariableRange() : base() + { + v = this.maxV = double.MaxValue; + } + + public override string ToString() + { + lock (vLock) + { + return "value:" + v.ToString() + " , maxValue:" + maxV.ToString(); + } + } + public double GetValue() { lock (vLock) return v; } + public static implicit operator double(DoubleInTheVariableRange adouble) => adouble.GetValue(); + public double GetMaxV() { lock (vLock) return maxV; } + public (double, double) GetValueAndMaxV() { lock (vLock) return (v, maxV); } + public bool IsMaxV() + { + lock (vLock) + { + return v == maxV; + } + } + #endregion + + #region 普通设置MaxV与Value的值的方法 + /// + /// 若maxValue<=0则maxValue设为0并返回False + /// + public bool SetMaxV(double maxValue) + { + if (maxValue <= 0) + { + lock (vLock) + { + v = maxV = 0; + return false; + } + } + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + return true; + } + /// + /// 应当保证该maxValue>=0 + /// + public void SetPositiveMaxV(double maxValue) + { + lock (vLock) + { + maxV = maxValue; + if (v > maxValue) v = maxValue; + } + } + + public double SetV(double value) + { + if (value <= 0) + { + lock (vLock) + { + return v = 0; + } + } + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + /// + /// 应当保证该value>=0 + /// + public double SetPositiveV(double value) + { + lock (vLock) + { + return v = (value > maxV) ? maxV : value; + } + } + #endregion + + #region 普通运算 + /// 返回实际改变量 + public double AddV(double addV) + { + lock (vLock) + { + double previousV = v; + v += addV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证增加值大于0 + /// + /// 返回实际改变量 + public double AddPositiveV(double addPositiveV) + { + lock (vLock) + { + addPositiveV = Math.Min(addPositiveV, maxV - v); + v += addPositiveV; + } + return addPositiveV; + } + public void MulV(double mulV) + { + if (mulV <= 0) + { + lock (vLock) v = 0; + return; + } + lock (vLock) + { + if (v > maxV / mulV) v = maxV; //避免溢出 + else v *= mulV; + } + } + /// + /// 应当保证乘数大于0 + /// + public void MulPositiveV(double mulPositiveV) + { + lock (vLock) + { + if (v > maxV / mulPositiveV) v = maxV; //避免溢出 + else v *= mulPositiveV; + } + } + /// 返回实际改变量 + public double SubV(double subV) + { + lock (vLock) + { + double previousV = v; + v -= subV; + if (v < 0) v = 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + /// + /// 应当保证该减少值大于0 + /// + /// 返回实际改变量 + public double SubPositiveV(double subPositiveV) + { + lock (vLock) + { + subPositiveV = Math.Min(subPositiveV, v); + v -= subPositiveV; + } + return subPositiveV; + } + #endregion + + #region 特殊条件的设置MaxV与Value的值的方法 + /// + /// 如果当前值大于maxValue,则更新maxValue失败 + /// + public bool TrySetMaxV(double maxValue) + { + lock (vLock) + { + if (v > maxValue) return false; + maxV = maxValue; + return true; + } + } + public void SetVToMaxV() + { + lock (vLock) + { + v = maxV; + } + } + + public bool Set0IfNotMax() + { + lock (vLock) + { + if (v < maxV) + { + v = 0; + return true; + } + } + return false; + } + public bool Set0IfMax() + { + lock (vLock) + { + if (v == maxV) + { + v = 0; + return true; + } + } + return false; + } + #endregion + + #region 特殊条件的运算 + /// + /// 试图加到满,如果无法加到maxValue则不加并返回-1 + /// + /// 返回实际改变量 + public double TryAddToMaxV(double addV) + { + lock (vLock) + { + if (maxV - v <= addV) + { + addV = maxV - v; + v = maxV; + return addV; + } + return -1; + } + } + #endregion + } +} \ No newline at end of file diff --git a/logic/Preparation/Utility/SafeValue/ListLocked.cs b/logic/Preparation/Utility/SafeValue/ListLocked.cs new file mode 100644 index 00000000..fbee8556 --- /dev/null +++ b/logic/Preparation/Utility/SafeValue/ListLocked.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace Preparation.Utility +{ + public class LockedClassList : IEnumerable + where T : class + { + private readonly ReaderWriterLockSlim listLock = new(); + private List list; + + #region 构造 + public LockedClassList() + { + list = new List(); + } + public LockedClassList(int capacity) + { + list = new List(capacity); + } + public LockedClassList(IEnumerable collection) + { + list = new List(collection); + } + #endregion + + #region 修改 + public TResult WriteLock(Func func) + { + listLock.EnterWriteLock(); + try + { + return func(); + } + finally + { + listLock.ExitWriteLock(); + } + + } + public void WriteLock(Action func) + { + listLock.EnterWriteLock(); + try + { + func(); + } + finally + { + listLock.ExitWriteLock(); + } + + } + + public void Add(T item) + { + WriteLock(() => { list.Add(item); }); + } + + public void Insert(int index, T item) + { + WriteLock(() => { list.Insert(index, item); }); + } + + public void Clear() + { + WriteLock(() => { list.Clear(); }); + } + + public bool Remove(T item) + { + return WriteLock(() => { return list.Remove(item); }); + } + + public int RemoveAll(T item) => WriteLock(() => { return list.RemoveAll((t) => { return t == item; }); }); + + public bool RemoveOne(Predicate match) => + WriteLock(() => + { + int index = list.FindIndex(match); + if (index == -1) return false; + list.RemoveAt(index); + return true; + }); + + public int RemoveAll(Predicate match) => WriteLock(() => { return list.RemoveAll(match); }); + + public bool RemoveAt(int index) + { + return WriteLock(() => + { + if (index > list.Count) return false; + list.RemoveAt(index); + return true; + }); + } + #endregion + + #region 读取与对类操作 + public TResult ReadLock(Func func) + { + listLock.EnterReadLock(); + try + { + return func(); + } + finally + { + listLock.ExitReadLock(); + } + } + public void ReadLock(Action func) + { + listLock.EnterReadLock(); + try + { + func(); + } + finally + { + listLock.ExitReadLock(); + } + + } + + public T this[int index] + { + get + { + return ReadLock(() => { return list[index]; }); + } + set + { + ReadLock(() => { list[index] = value!; }); + } + } + public int Count => ReadLock(() => { return list.Count; }); + + public int IndexOf(T item) + { + return ReadLock(() => { return list.IndexOf(item); }); + } + + public Array ToArray() + { + return ReadLock(() => { return list.ToArray(); }); + } + + public List ToNewList() + { + List lt = new(); + return ReadLock(() => { lt.AddRange(list); return lt; }); + } + + public bool Contains(T item) + { + return ReadLock(() => { return list.Contains(item); }); + } + + public T? Find(Predicate match) + { + return ReadLock(() => { return list.Find(match); }); + } + + public List FindAll(Predicate match) + { + return ReadLock(() => { return list.FindAll(match); }); + } + + public int FindIndex(Predicate match) => ReadLock(() => { return list.FindIndex(match); }); + + public void ForEach(Action action) => ReadLock(() => { list.ForEach(action); }); + + public IEnumerator GetEnumerator() + { + return ReadLock(() => { return list.GetEnumerator(); }); + } + #endregion + } +} \ No newline at end of file diff --git a/logic/Preparation/Utility/SafeValue/TimeBased.cs b/logic/Preparation/Utility/SafeValue/TimeBased.cs new file mode 100644 index 00000000..402595a1 --- /dev/null +++ b/logic/Preparation/Utility/SafeValue/TimeBased.cs @@ -0,0 +1,624 @@ +using System; +using System.Threading; + +namespace Preparation.Utility +{ + //其对应属性不应当有set访问器,避免不安全的=赋值 + + /// + /// 记录上次Start的时间,尚未Start则为long.MaxValue + /// 当前不为long.MaxValue则不能Start + /// + public class StartTime + { + private long _time; + public StartTime(long time) + { + _time = time; + } + public StartTime() { _time = long.MaxValue; } + public long Get() => Interlocked.CompareExchange(ref _time, -2, -2); + public override string ToString() => Get().ToString(); + /// 返回操作前的值 + public long Start() => Interlocked.CompareExchange(ref _time, Environment.TickCount64, long.MaxValue); + /// 返回操作前的值 + public long Stop() => Interlocked.Exchange(ref _time, long.MaxValue); + /// 返回时间差,<0意味着未开始 + public long StopIfPassing(long passedTime) + { + long ans = Environment.TickCount64 - Interlocked.CompareExchange(ref _time, -2, -2); + if (ans > passedTime) + { + Interlocked.Exchange(ref _time, long.MaxValue); + } + return ans; + } + } + + public class LongInTheVariableRangeWithStartTime : LongInTheVariableRange + { + public StartTime startTime = new(); + public LongInTheVariableRangeWithStartTime(long value, long maxValue) : base(value, maxValue) { } + /// + /// 默认使Value=maxValue + /// + public LongInTheVariableRangeWithStartTime(long maxValue) : base(maxValue) { } + public LongInTheVariableRangeWithStartTime() : base() { } + + #region 读取 + public (long, long) GetValueWithStartTime() + { + lock (vLock) + { + return (v, startTime.Get()); + } + } + public (long, long, long) GetValueAndMaxVWithStartTime() + { + lock (vLock) + { + return (v, maxV, startTime.Get()); + } + } + #endregion + + /// + /// 试图加到满,如果加上时间差*速度可以达到MaxV,则加上并使startTime变为long.MaxValue + /// 如果无法加到maxValue则不加 + /// + /// 返回试图加到的值与最大值 + public (long, long, long) AddStartTimeToMaxV(double speed = 1.0) + { + lock (vLock) + { + long addV = (long)(startTime.StopIfPassing(maxV - v) * speed); + if (addV < 0) return (v, maxV, startTime.Get()); + if (maxV - v < addV) return (v = maxV, maxV, startTime.Get()); + return (v + addV, maxV, startTime.Get()); + } + } + + /// + /// 增加量为时间差*速度,并将startTime变为long.MaxValue + /// + /// 返回实际改变量 + public long AddStartTime(double speed = 1.0) + { + lock (vLock) + { + long previousV = v; + long addV = (Environment.TickCount64 - startTime.Stop()); + if (addV < 0) v += (long)(addV * speed); + else return 0; + if (v > maxV) v = maxV; + return v - previousV; + } + } + + /// + /// 试图加到满,如果加上时间差*速度可以达到MaxV,则加上 + /// 如果无法加到maxValue则清零 + /// 无论如何startTime变为long.MaxValue + /// + /// 返回是否清零 + public bool Set0IfNotAddStartTimeToMaxV(double speed = 1.0) + { + lock (vLock) + { + if (v == maxV) return false; + long addV = (long)(startTime.Stop() * speed); + if (addV < 0) + { + v = 0; + return true; + } + if (maxV - v < addV) + { + v = maxV; + return false; + } + v = 0; + return false; + } + } + + public void SetAndStop(long value = 0) + { + lock (vLock) + { + this.v = value; + startTime.Stop(); + } + } + } + + public class TimeBasedProgressAtVariableSpeed + { + private LongInTheVariableRangeWithStartTime progress; + public AtomicDouble speed; + + #region 构造 + public TimeBasedProgressAtVariableSpeed(long needProgress, double speed = 1.0) + { + progress = new LongInTheVariableRangeWithStartTime(0, needProgress); + if (needProgress <= 0) Debugger.Output("Bug:TimeBasedProgressAtVariableSpeed.needProgress (" + needProgress.ToString() + ") is less than 0."); + this.speed = new(speed); + } + public TimeBasedProgressAtVariableSpeed() + { + progress = new LongInTheVariableRangeWithStartTime(0, 0); + this.speed = new(1.0); + } + #endregion + + #region 读取 + public override string ToString() + { + long progressStored, lastStartTime; + (progressStored, lastStartTime) = progress.GetValueWithStartTime(); + return "ProgressStored: " + progressStored.ToString() + + " ; LastStartTime: " + lastStartTime.ToString() + "ms" + + " ; Speed: " + speed.ToString(); + } + public long GetProgressNow() => progress.AddStartTimeToMaxV((double)speed).Item1; + public (long, long, long) GetProgressNowAndNeedTimeAndLastStartTime() => progress.AddStartTimeToMaxV((double)speed); + public long GetProgressStored() => progress.GetValue(); + public (long, long) GetProgressStoredAndNeedTime() => progress.GetValueAndMaxV(); + public (long, long, long) GetProgressStoredAndNeedTimeAndLastStartTime() => progress.GetValueAndMaxVWithStartTime(); + + public bool IsFinished() + { + long progressNow, needTime; + (progressNow, needTime, _) = progress.AddStartTimeToMaxV((double)speed); + return progressNow == needTime; + } + public bool IsProgressing() + { + long progressNow, needTime, startT; + (progressNow, needTime, startT) = progress.AddStartTimeToMaxV((double)speed); + return (startT != long.MaxValue && progressNow != needTime); + } + #endregion + + public bool Start(long needTime) + { + if (needTime <= 2) + { + Debugger.Output("Warning:Start TimeBasedProgressAtVariableSpeed with the needProgress (" + needTime.ToString() + ") which is less than 0."); + return false; + } + if (progress.startTime.Start() != long.MaxValue) return false; + progress.SetMaxV(needTime); + return true; + } + public bool Start() + { + return progress.startTime.Start() == long.MaxValue; + } + /// + /// 使进度条强制终止清零 + /// + public void Set0() + { + progress.SetAndStop(); + } + /// + /// 如果进度条加上时间差不能为满,使进度条强制终止清零 + /// + public void TryStop() + { + progress.Set0IfNotAddStartTimeToMaxV(speed); + } + /// + /// 使进度条暂停 + /// + public bool Pause() + { + return progress.AddStartTime((double)speed) != 0; + } + /// + /// 使进度条进度为满 + /// + public void Finish() + { + progress.SetVToMaxV(); + progress.startTime.Stop(); + } + } + + /// + /// 根据时间推算Start后完成多少进度的进度条(long)。 + /// 只允许Start(清零状态的进度条才可以Start)时修改needTime(请确保大于0); + /// 支持InterruptToSet0使未完成的进度条终止清零;支持Set0使进度条强制终止清零; + /// 通过原子操作实现。 + /// + public class TimeBasedProgressOptimizedForInterrupting + { + private long endT = long.MaxValue; + private long needT; + + public TimeBasedProgressOptimizedForInterrupting(long needTime) + { + if (needTime <= 0) Debugger.Output("Bug:TimeBasedProgressOptimizedForInterrupting.needProgress (" + needTime.ToString() + ") is less than 0."); + this.needT = needTime; + } + public TimeBasedProgressOptimizedForInterrupting() + { + this.needT = 0; + } + 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 bool IsFinished() + { + return Interlocked.CompareExchange(ref endT, -2, -2) <= Environment.TickCount64; + } + public bool IsStarted() => Interlocked.Read(ref endT) != long.MaxValue; + /// + /// GetProgress<0则表明未开始 + /// + 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 long GetNonNegativeProgress() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + return progress < 0 ? 0 : progress; + } + /// + /// GetProgress<0则表明未开始 + /// + public long GetProgress(long time) + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - time; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + return Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + } + public long GetNonNegativeProgress(long time) + { + long cutime = Interlocked.Read(ref endT) - time; + if (cutime <= 0) return Interlocked.CompareExchange(ref needT, -2, -2); + long progress = Interlocked.CompareExchange(ref needT, -2, -2) - cutime; + return progress < 0 ? 0 : progress; + } + /// + /// <0则表明未开始 + /// + public static implicit operator long(TimeBasedProgressOptimizedForInterrupting pLong) => pLong.GetProgress(); + + /// + /// GetProgressDouble<0则表明未开始 + /// + public double GetProgressDouble() + { + long cutime = Interlocked.CompareExchange(ref endT, -2, -2) - Environment.TickCount64; + if (cutime <= 0) return 1; + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (needTime == 0) return 0; + return 1.0 - ((double)cutime / needTime); + } + public double GetNonNegativeProgressDouble(long time) + { + long cutime = Interlocked.Read(ref endT) - time; + if (cutime <= 0) return 1; + long needTime = Interlocked.CompareExchange(ref needT, -2, -2); + if (needTime <= cutime) return 0; + return 1.0 - ((double)cutime / needTime); + } + + public bool Start(long needTime) + { + if (needTime <= 0) + { + Debugger.Output("Warning:Start TimeBasedProgressOptimizedForInterrupting with the needProgress (" + needTime.ToString() + ") which is less than 0."); + return false; + } + //规定只有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 TimeBasedProgressOptimizedForInterrupting is " + needTime.ToString() + ",which is too small."); + Interlocked.Exchange(ref 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 bool InterruptToSet0() + { + if (Environment.TickCount64 < Interlocked.CompareExchange(ref endT, -2, -2)) + { + Interlocked.Exchange(ref endT, long.MaxValue); + return true; + } + return false; + } + //增加其他新的写操作可能导致不安全 + } + + /// + /// 冷却时间为可变的CDms的bool,不支持查看当前进度,初始为True + /// + public class BoolUpdateEachCD + { + private long cd; + private long nextUpdateTime = 0; + public BoolUpdateEachCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:BoolUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public BoolUpdateEachCD(long cd) + { + if (cd <= 1) Debugger.Output("Bug:BoolUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public BoolUpdateEachCD(long cd, long startTime) + { + if (cd <= 1) Debugger.Output("Bug:BoolUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + this.nextUpdateTime = startTime; + } + + public long GetCD() => Interlocked.Read(ref cd); + + public bool TryUse() + { + long needTime = Interlocked.Exchange(ref nextUpdateTime, long.MaxValue); + if (needTime <= Environment.TickCount64) + { + Interlocked.Exchange(ref nextUpdateTime, Environment.TickCount64 + Interlocked.Read(ref cd)); + return true; + } + Interlocked.Exchange(ref nextUpdateTime, needTime); + return false; + } + public void SetCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:BoolUpdateEachCD.cd to " + cd.ToString() + "."); + Interlocked.Exchange(ref this.cd, cd); + } + } + + /// + /// 冷却时间为可变的CDms的进度条,初始为满 + /// + public class LongProgressUpdateEachCD + { + private int isusing = 0; + private long cd; + private long nextUpdateTime = 0; + public LongProgressUpdateEachCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:LongProgressUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public LongProgressUpdateEachCD(long cd) + { + if (cd <= 1) Debugger.Output("Bug:LongProgressUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + } + public LongProgressUpdateEachCD(long cd, long startTime) + { + if (cd <= 1) Debugger.Output("Bug:LongProgressUpdateEachCD.cd (" + cd.ToString() + ") is less than 1."); + this.cd = cd; + this.nextUpdateTime = startTime; + } + + public long GetRemainingTime() + { + long v = Interlocked.Read(ref nextUpdateTime) - Environment.TickCount64; + return v < 0 ? 0 : v; + } + public long GetCD() => Interlocked.Read(ref cd); + + public bool TryUse() + { + if (Interlocked.Exchange(ref isusing, 1) == 1) return false; + long needTime = Interlocked.Read(ref nextUpdateTime); + if (needTime <= Environment.TickCount64) + { + Interlocked.Exchange(ref nextUpdateTime, Environment.TickCount64 + Interlocked.Read(ref cd)); + Interlocked.Exchange(ref isusing, 0); + return true; + } + Interlocked.Exchange(ref isusing, 0); + return false; + } + public void SetCD(int cd) + { + if (cd <= 1) Debugger.Output("Bug:Set LongProgressUpdateEachCD.cd to " + cd.ToString() + "."); + Interlocked.Exchange(ref this.cd, cd); + } + } + + /// + /// 一个保证在[0,maxNum],每CDms自动+1的int,支持可变的CD、maxNum(请确保大于0) + /// + public class IntNumUpdateEachCD + { + private int num; + private int maxNum; + private int cd; + private long updateTime = 0; + private readonly object numLock = new(); + public IntNumUpdateEachCD(int num, int maxNum, int cd) + { + if (num < 0) Debugger.Output("Bug:IntNumUpdateEachCD.num (" + num.ToString() + ") is less than 0."); + if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateEachCD.maxNum (" + maxNum.ToString() + ") is less than 0."); + if (cd <= 0) Debugger.Output("Bug:IntNumUpdateEachCD.cd (" + cd.ToString() + ") is less than 0."); + this.num = (num < maxNum) ? num : maxNum; + this.maxNum = maxNum; + this.cd = cd; + this.updateTime = Environment.TickCount64; + } + /// + /// 默认使num=maxNum + /// + public IntNumUpdateEachCD(int maxNum, int cd) + { + if (maxNum < 0) Debugger.Output("Bug:IntNumUpdateEachCD.maxNum (" + maxNum.ToString() + ") is less than 0."); + if (cd <= 0) Debugger.Output("Bug:IntNumUpdateEachCD.cd (" + cd.ToString() + ") is less than 0."); + this.num = this.maxNum = maxNum; + this.cd = cd; + } + public IntNumUpdateEachCD() + { + this.num = this.maxNum = 0; + this.cd = int.MaxValue; + } + + public int GetMaxNum() { lock (numLock) return maxNum; } + public int GetCD() { lock (numLock) return cd; } + public int GetNum(long time) + { + lock (numLock) + { + if (num < maxNum && time - updateTime >= cd) + { + int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); + updateTime += add * cd; + return (num += add); + } + return num; + } + } + public static implicit operator int(IntNumUpdateEachCD aint) => aint.GetNum(Environment.TickCount64); + + /// + /// 应当保证该subV>=0 + /// + public int TrySub(int subV) + { + if (subV < 0) Debugger.Output("Bug:IntNumUpdateEachCD Try to sub " + subV.ToString() + ", which is less than 0."); + long time = Environment.TickCount64; + lock (numLock) + { + if (num < maxNum && time - updateTime >= cd) + { + int add = (int)Math.Min(maxNum - num, (time - updateTime) / cd); + updateTime += add * cd; + num += add; + } + if (num == maxNum) updateTime = time; + num -= subV = Math.Min(subV, num); + } + return subV; + } + /// + /// 应当保证该addV>=0 + /// + public void TryAdd(int addV) + { + if (addV < 0) Debugger.Output("Bug:IntNumUpdateEachCD Try to add " + addV.ToString() + ", which is less than 0."); + lock (numLock) + { + num += Math.Min(addV, maxNum - num); + } + } + /// + /// 若maxNum<=0则maxNum及Num设为0并返回False + /// + public bool SetMaxNumAndNum(int maxNum) + { + if (maxNum < 0) maxNum = 0; + lock (numLock) + { + this.num = this.maxNum = maxNum; + } + return maxNum > 0; + } + /// + /// 应当保证该maxnum>=0 + /// + public void SetPositiveMaxNumAndNum(int maxNum) + { + lock (numLock) + { + this.num = this.maxNum = maxNum; + } + } + /// + /// 应当保证该maxnum>=0 + /// + public void SetPositiveMaxNum(int maxNum) + { + lock (numLock) + { + if ((this.maxNum = maxNum) < num) + num = maxNum; + } + } + /// + /// 若maxNum<=0则maxNum及Num设为0并返回False + /// + public bool SetMaxNum(int maxNum) + { + if (maxNum < 0) maxNum = 0; + lock (numLock) + { + if ((this.maxNum = maxNum) < num) + num = maxNum; + } + return maxNum > 0; + } + /// + /// 若num<0则num设为0并返回False + /// + public bool SetNum(int num) + { + lock (numLock) + { + if (num < 0) + { + this.num = 0; + updateTime = Environment.TickCount64; + return false; + } + if (num < maxNum) + { + if (this.num == maxNum) updateTime = Environment.TickCount64; + this.num = num; + } + else this.num = maxNum; + return true; + } + } + /// + /// 应当保证该num>=0 + /// + public void SetPositiveNum(int num) + { + lock (numLock) + { + if (num < maxNum) + { + if (this.num == maxNum) updateTime = Environment.TickCount64; + this.num = num; + } + else this.num = maxNum; + } + } + public void SetCD(int cd) + { + lock (numLock) + { + if (cd <= 0) Debugger.Output("Bug:Set IntNumUpdateEachCD.cd to " + cd.ToString() + "."); + this.cd = cd; + } + } + } +} \ No newline at end of file diff --git a/logic/Server/CopyInfo.cs b/logic/Server/CopyInfo.cs index d9d341a6..a18d58a2 100644 --- a/logic/Server/CopyInfo.cs +++ b/logic/Server/CopyInfo.cs @@ -2,6 +2,7 @@ using GameClass.GameObj; using Preparation.Utility; using Gaming; +using Preparation.Interface; namespace Server { @@ -81,10 +82,8 @@ public static class CopyInfo }; foreach (var keyValue in player.ActiveSkillDictionary) - { - int progress = (int)((keyValue.Value.StartTime - time) + keyValue.Value.SkillCD); - msg.StudentMessage.TimeUntilSkillAvailable.Add(progress < 0 ? 0 : progress); - } + msg.StudentMessage.TimeUntilSkillAvailable.Add(keyValue.Value.SkillCD.GetRemainingTime()); + for (int i = 0; i < GameData.maxNumOfSkill - player.ActiveSkillDictionary.Count; ++i) msg.StudentMessage.TimeUntilSkillAvailable.Add(-1); @@ -124,11 +123,9 @@ public static class CopyInfo BulletType = Transformation.ToBulletType((Preparation.Utility.BulletType)player.BulletOfPlayer) } }; + foreach (var keyValue in player.ActiveSkillDictionary) - { - int progress = (int)(keyValue.Value.SkillCD + (keyValue.Value.StartTime - time)); - msg.TrickerMessage.TimeUntilSkillAvailable.Add(progress < 0 ? 0 : progress); - } + msg.TrickerMessage.TimeUntilSkillAvailable.Add(keyValue.Value.SkillCD.GetRemainingTime()); for (int i = 0; i < GameData.maxNumOfSkill - player.ActiveSkillDictionary.Count; ++i) msg.TrickerMessage.TimeUntilSkillAvailable.Add(-1); @@ -235,8 +232,7 @@ private static MessageOfObj Gate(Doorway doorway, long time) Y = doorway.Position.y } }; - int progress = ((doorway.OpenStartTime > 0) ? ((int)(time - doorway.OpenStartTime)) : 0) + doorway.OpenDegree; - msg.GateMessage.Progress = (progress > GameData.degreeOfOpenedDoorway) ? GameData.degreeOfOpenedDoorway : progress; + msg.GateMessage.Progress = (int)doorway.ProgressOfDoorway.GetProgressNow(); return msg; } private static MessageOfObj HiddenGate(EmergencyExit Exit) @@ -269,17 +265,15 @@ private static MessageOfObj Door(Door door) } private static MessageOfObj Chest(Chest chest, long time) { - MessageOfObj msg = new() + return new() { ChestMessage = new() { X = chest.Position.x, - Y = chest.Position.y + Y = chest.Position.y, + Progress = (int)(chest.OpenProgress.GetNonNegativeProgressDouble(time) * GameData.degreeOfOpenedChest) } }; - int progress = (chest.WhoOpen != null) ? (((int)(time - chest.OpenStartTime)) * chest.WhoOpen.SpeedOfOpenChest) : 0; - msg.ChestMessage.Progress = (progress > GameData.degreeOfOpenedChest) ? GameData.degreeOfOpenedChest : progress; - return msg; } } } diff --git a/logic/Server/GameServer.cs b/logic/Server/GameServer.cs index afebe2f7..35df1317 100644 --- a/logic/Server/GameServer.cs +++ b/logic/Server/GameServer.cs @@ -173,17 +173,9 @@ public void ReportGame(GameState gameState, bool requiredGaming = true) } 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 @@ private bool playerDeceased(int playerID) 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; } diff --git a/logic/Server/Server.csproj b/logic/Server/Server.csproj index b7b53efc..3fcd9f04 100644 --- a/logic/Server/Server.csproj +++ b/logic/Server/Server.csproj @@ -10,10 +10,10 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/playback/Playback/Playback.csproj b/playback/Playback/Playback.csproj index f3e0f979..1fc191c3 100644 --- a/playback/Playback/Playback.csproj +++ b/playback/Playback/Playback.csproj @@ -7,7 +7,7 @@ - + diff --git a/renovate.json b/renovate.json index 6c6bb765..1c7a4991 100644 --- a/renovate.json +++ b/renovate.json @@ -6,6 +6,7 @@ "timezone": "Asia/Shanghai", "schedule": "after 4am and before 8am every saturday", "labels": ["dependencies", "bot"], + "enabled": false, "packageRules": [ { "matchPackageNames": [