diff --git a/CAPI/cpp/API/include/AI.h b/CAPI/cpp/API/include/AI.h index cb6b373..d99063f 100644 --- a/CAPI/cpp/API/include/AI.h +++ b/CAPI/cpp/API/include/AI.h @@ -4,6 +4,10 @@ #include "API.h" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + class IAI { public: diff --git a/CAPI/cpp/API/include/API.h b/CAPI/cpp/API/include/API.h index 1b89718..b3dec9e 100644 --- a/CAPI/cpp/API/include/API.h +++ b/CAPI/cpp/API/include/API.h @@ -22,6 +22,10 @@ #include "structures.h" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + const constexpr int numOfGridPerCell = 1000; class IAI; diff --git a/CAPI/cpp/API/include/Communication.h b/CAPI/cpp/API/include/Communication.h index 2f75e43..9b2c19e 100644 --- a/CAPI/cpp/API/include/Communication.h +++ b/CAPI/cpp/API/include/Communication.h @@ -14,6 +14,10 @@ #include #include +#undef GetMessage +#undef SendMessage +#undef PeekMessage + class Logic; class Communication diff --git a/CAPI/cpp/API/include/ConcurrentQueue.hpp b/CAPI/cpp/API/include/ConcurrentQueue.hpp index 19fdf04..08e4a7c 100644 --- a/CAPI/cpp/API/include/ConcurrentQueue.hpp +++ b/CAPI/cpp/API/include/ConcurrentQueue.hpp @@ -8,6 +8,10 @@ #include #include +#undef GetMessage +#undef SendMessage +#undef PeekMessage + template class ConcurrentQueue { diff --git a/CAPI/cpp/API/include/constants.h b/CAPI/cpp/API/include/constants.h index d9407dd..51d2034 100644 --- a/CAPI/cpp/API/include/constants.h +++ b/CAPI/cpp/API/include/constants.h @@ -6,6 +6,10 @@ #define SCCI static const constexpr inline #endif +#undef GetMessage +#undef SendMessage +#undef PeekMessage + namespace Constants { SCCI int frameDuration = 50; // 每帧毫秒数 diff --git a/CAPI/cpp/API/include/logic.h b/CAPI/cpp/API/include/logic.h index 5ca41ea..a6bd187 100644 --- a/CAPI/cpp/API/include/logic.h +++ b/CAPI/cpp/API/include/logic.h @@ -31,6 +31,10 @@ #include "Communication.h" #include "ConcurrentQueue.hpp" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + // 封装了通信组件和对AI对象进行操作 class Logic : public ILogic { @@ -49,9 +53,6 @@ private: THUAI6::TrickerType trickerType; THUAI6::StudentType studentType; - // GUID信息 - std::vector playerGUIDs; - std::unique_ptr timer; std::thread tAI; // 用于运行AI的线程 diff --git a/CAPI/cpp/API/include/state.h b/CAPI/cpp/API/include/state.h index 02f5543..c27c479 100644 --- a/CAPI/cpp/API/include/state.h +++ b/CAPI/cpp/API/include/state.h @@ -9,6 +9,10 @@ #include "structures.h" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + // 存储场上的状态 struct State { diff --git a/CAPI/cpp/API/include/structures.h b/CAPI/cpp/API/include/structures.h index ccda444..5553c0d 100644 --- a/CAPI/cpp/API/include/structures.h +++ b/CAPI/cpp/API/include/structures.h @@ -8,6 +8,10 @@ #include #include +#undef GetMessage +#undef SendMessage +#undef PeekMessage + namespace THUAI6 { diff --git a/CAPI/cpp/API/include/utils.hpp b/CAPI/cpp/API/include/utils.hpp index 1044b74..de14b02 100644 --- a/CAPI/cpp/API/include/utils.hpp +++ b/CAPI/cpp/API/include/utils.hpp @@ -13,6 +13,10 @@ #include "structures.h" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + namespace AssistFunction { diff --git a/CAPI/cpp/API/src/AI.cpp b/CAPI/cpp/API/src/AI.cpp index 6895409..6152fd0 100644 --- a/CAPI/cpp/API/src/AI.cpp +++ b/CAPI/cpp/API/src/AI.cpp @@ -5,7 +5,7 @@ #include "constants.h" // 注意不要使用conio.h,Windows.h等非标准库 -// 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 +// 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新,大致一帧更新一次 extern const bool asynchronous = false; // 选手需要依次将player0到player4的职业在这里定义 diff --git a/CAPI/cpp/API/src/API.cpp b/CAPI/cpp/API/src/API.cpp index 895abcd..26926cc 100644 --- a/CAPI/cpp/API/src/API.cpp +++ b/CAPI/cpp/API/src/API.cpp @@ -1,6 +1,11 @@ #include #include "AI.h" #include "API.h" + +#undef GetMessage +#undef SendMessage +#undef PeekMessage + #define PI 3.14159265358979323846 int StudentAPI::GetFrameCount() const diff --git a/CAPI/cpp/API/src/Communication.cpp b/CAPI/cpp/API/src/Communication.cpp index f808e22..b620765 100644 --- a/CAPI/cpp/API/src/Communication.cpp +++ b/CAPI/cpp/API/src/Communication.cpp @@ -5,6 +5,10 @@ #include #include +#undef GetMessage +#undef SendMessage +#undef PeekMessage + using grpc::ClientContext; Communication::Communication(std::string sIP, std::string sPort) diff --git a/CAPI/cpp/API/src/DebugAPI.cpp b/CAPI/cpp/API/src/DebugAPI.cpp index fdeb857..34507c7 100644 --- a/CAPI/cpp/API/src/DebugAPI.cpp +++ b/CAPI/cpp/API/src/DebugAPI.cpp @@ -4,6 +4,11 @@ #include "API.h" #include "utils.hpp" #include "structures.h" + +#undef GetMessage +#undef SendMessage +#undef PeekMessage + #define PI 3.14159265358979323846 StudentDebugAPI::StudentDebugAPI(ILogic& logic, bool file, bool print, bool warnOnly, int64_t playerID) : diff --git a/CAPI/cpp/API/src/logic.cpp b/CAPI/cpp/API/src/logic.cpp index c06eb09..b3e42ab 100644 --- a/CAPI/cpp/API/src/logic.cpp +++ b/CAPI/cpp/API/src/logic.cpp @@ -8,6 +8,10 @@ #include "utils.hpp" #include "Communication.h" +#undef GetMessage +#undef SendMessage +#undef PeekMessage + extern const bool asynchronous; Logic::Logic(THUAI6::PlayerType type, int64_t ID, THUAI6::TrickerType tricker, THUAI6::StudentType student) : @@ -323,17 +327,6 @@ void Logic::ProcessMessage() case THUAI6::GameState::GameStart: logger->info("Game Start!"); - // 重新读取玩家的guid,保证人类在前屠夫在后 - playerGUIDs.clear(); - for (const auto& obj : clientMsg.obj_message()) - if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) - playerGUIDs.push_back(obj.student_message().guid()); - for (const auto& obj : clientMsg.obj_message()) - if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) - playerGUIDs.push_back(obj.tricker_message().guid()); - currentState->guids = playerGUIDs; - bufferState->guids = playerGUIDs; - // 读取地图 for (const auto& item : clientMsg.obj_message()) if (Proto2THUAI6::messageOfObjDict[item.message_of_obj_case()] == THUAI6::MessageOfObj::MapMessage) @@ -368,16 +361,6 @@ void Logic::ProcessMessage() break; case THUAI6::GameState::GameRunning: - // 重新读取玩家的guid,guid确保人类在前屠夫在后 - playerGUIDs.clear(); - for (const auto& obj : clientMsg.obj_message()) - if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) - playerGUIDs.push_back(obj.student_message().guid()); - for (const auto& obj : clientMsg.obj_message()) - if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) - playerGUIDs.push_back(obj.tricker_message().guid()); - currentState->guids = playerGUIDs; - bufferState->guids = playerGUIDs; LoadBuffer(clientMsg); break; @@ -605,9 +588,16 @@ void Logic::LoadBuffer(const protobuf::MessageToClient& message) bufferState->props.clear(); bufferState->bullets.clear(); bufferState->bombedBullets.clear(); + bufferState->guids.clear(); logger->debug("Buffer cleared!"); // 读取新的信息 + for (const auto& obj : message.obj_message()) + if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::StudentMessage) + bufferState->guids.push_back(obj.student_message().guid()); + for (const auto& obj : message.obj_message()) + if (Proto2THUAI6::messageOfObjDict[obj.message_of_obj_case()] == THUAI6::MessageOfObj::TrickerMessage) + bufferState->guids.push_back(obj.tricker_message().guid()); bufferState->gameInfo = Proto2THUAI6::Protobuf2THUAI6GameInfo(message.all_message()); LoadBufferSelf(message); for (const auto& item : message.obj_message()) @@ -689,6 +679,7 @@ bool Logic::TryConnection() bool Logic::HaveView(int gridX, int gridY, int selfX, int selfY, int viewRange) const { + std::unique_lock lock(mtxState); return AssistFunction::HaveView(viewRange, selfX, selfY, gridX, gridY, currentState->gameMap); } diff --git a/CAPI/cpp/API/src/main.cpp b/CAPI/cpp/API/src/main.cpp index ee0ecb6..611e347 100644 --- a/CAPI/cpp/API/src/main.cpp +++ b/CAPI/cpp/API/src/main.cpp @@ -4,6 +4,10 @@ #include #include +#undef GetMessage +#undef SendMessage +#undef PeekMessage + #ifdef _MSC_VER #pragma warning(disable : 4996) #endif diff --git a/CAPI/cpp/proto/Services.grpc.pb.h b/CAPI/cpp/proto/Services.grpc.pb.h old mode 100644 new mode 100755 index 7cf6d74..e99d5cc --- a/CAPI/cpp/proto/Services.grpc.pb.h +++ b/CAPI/cpp/proto/Services.grpc.pb.h @@ -25,6 +25,8 @@ #include #include +#undef SendMessage + namespace protobuf { diff --git a/CAPI/python/PyAPI/AI.py b/CAPI/python/PyAPI/AI.py index 09af335..740f7cb 100644 --- a/CAPI/python/PyAPI/AI.py +++ b/CAPI/python/PyAPI/AI.py @@ -8,7 +8,7 @@ import time class Setting: - # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新 + # 为假则play()期间确保游戏状态不更新,为真则只保证游戏状态在调用相关方法时不更新,大致一帧更新一次 @staticmethod def asynchronous() -> bool: return False diff --git a/CAPI/python/PyAPI/constants.py b/CAPI/python/PyAPI/constants.py index 5354d75..de7d171 100644 --- a/CAPI/python/PyAPI/constants.py +++ b/CAPI/python/PyAPI/constants.py @@ -301,13 +301,13 @@ class SummonGolem: class CommonAttackOfTricker: BulletBombRange = 0 - BulletAttackRange = Constants.Constants.basicAttackShortRange + BulletAttackRange = Constants.basicAttackShortRange ap = Constants.basicApOfTricker Speed = Constants.basicBulletMoveSpeed IsRemoteAttack = False CastTime = BulletAttackRange * 1000 / Speed - Backswing =Constants.basicBackswing - RecoveryFromHit =Constants.basicRecoveryFromHit + Backswing = Constants.basicBackswing + RecoveryFromHit = Constants.basicRecoveryFromHit cd = Constants.basicBackswing maxBulletNum = 1 @@ -340,4 +340,4 @@ class JumpyDumpty: BulletAttackRange = Constants.basicRemoteAttackRange * 2 ap = (int)(Constants.basicApOfTricker* 0.6) Speed = Constants.basicBulletMoveSpeed* 43 / 37 - IsRemoteAttack = False \ No newline at end of file + IsRemoteAttack = False diff --git a/CAPI/python/PyAPI/logic.py b/CAPI/python/PyAPI/logic.py index b3763c6..e1dcf7f 100644 --- a/CAPI/python/PyAPI/logic.py +++ b/CAPI/python/PyAPI/logic.py @@ -3,6 +3,7 @@ from typing import List, Union, Callable, Tuple import threading import logging import copy +import platform import proto.MessageType_pb2 as MessageType import proto.Message2Server_pb2 as Message2Server import proto.Message2Clients_pb2 as Message2Clients @@ -22,7 +23,6 @@ class Logic(ILogic): # ID self.__playerID: int = playerID - self.__playerGUIDs: List[int] = [] self.__playerType: THUAI6.PlayerType = playerType @@ -214,7 +214,7 @@ class Logic(ILogic): def GetPlayerGUIDs(self) -> List[int]: with self.__mtxState: - return copy.deepcopy(self.__playerGUIDs) + return copy.deepcopy(self.__currentState.guids) # IStudentAPI使用的接口 @@ -263,7 +263,8 @@ class Logic(ILogic): return self.__comm.EndAllAction(self.__playerID) def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool: - return AssistFunction.HaveView(viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap) + with self.__mtxState: + return AssistFunction.HaveView(viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap) # Logic内部逻辑 def __TryConnection(self) -> bool: @@ -286,15 +287,6 @@ class Logic(ILogic): if self.__gameState == THUAI6.GameState.GameStart: # 读取玩家的GUID self.__logger.info("Game start!") - self.__playerGUIDs.clear() - for obj in clientMsg.obj_message: - if obj.WhichOneof("message_of_obj") == "student_message": - self.__playerGUIDs.append(obj.student_message.guid) - for obj in clientMsg.obj_message: - if obj.WhichOneof("message_of_obj") == "tricker_message": - self.__playerGUIDs.append(obj.tricker_message.guid) - self.__currentState.guids = self.__playerGUIDs - self.__bufferState.guids = self.__playerGUIDs for obj in clientMsg.obj_message: if obj.WhichOneof("message_of_obj") == "map_message": @@ -318,15 +310,6 @@ class Logic(ILogic): elif self.__gameState == THUAI6.GameState.GameRunning: # 读取玩家的GUID - self.__playerGUIDs.clear() - for obj in clientMsg.obj_message: - if obj.WhichOneof("message_of_obj") == "student_message": - self.__playerGUIDs.append(obj.student_message.guid) - for obj in clientMsg.obj_message: - if obj.WhichOneof("message_of_obj") == "tricker_message": - self.__playerGUIDs.append(obj.tricker_message.guid) - self.__currentState.guids = self.__playerGUIDs - self.__bufferState.guids = self.__playerGUIDs self.__LoadBuffer(clientMsg) else: self.__logger.error("Unknown GameState!") @@ -467,9 +450,21 @@ class Logic(ILogic): self.__bufferState.students.clear() self.__bufferState.trickers.clear() self.__bufferState.props.clear() + self.__bufferState.bullets.clear() + self.__bufferState.bombedBullets.clear() + self.__bufferState.guids.clear() self.__logger.debug("Buffer cleared!") + + for obj in message.obj_message: + if obj.WhichOneof("message_of_obj") == "student_message": + self.__bufferState.guids.append(obj.student_message.guid) + for obj in message.obj_message: + if obj.WhichOneof("message_of_obj") == "tricker_message": + self.__bufferState.guids.append(obj.tricker_message.guid) + self.__bufferState.gameInfo = Proto2THUAI6.Protobuf2THUAI6GameInfo( message.all_message) + self.__LoadBufferSelf(message) for item in message.obj_message: self.__LoadBufferCase(item) @@ -511,9 +506,16 @@ class Logic(ILogic): formatter = logging.Formatter( "[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S') # 确保文件存在 - if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"): - os.makedirs(os.path.dirname(os.path.dirname( - os.path.realpath(__file__))) + "/logs") + # if not os.path.exists(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/logs"): + # os.makedirs(os.path.dirname(os.path.dirname( + # os.path.realpath(__file__))) + "/logs") + + if platform.system().lower() == "windows": + os.system( + f"mkdir {os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '/logs'}") + else: + os.system( + f"mkdir -p {os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + '/logs'}") fileHandler = logging.FileHandler(os.path.dirname( os.path.dirname(os.path.realpath(__file__))) + "/logs/logic" + str(self.__playerID) + "-log.txt", "w+", encoding="utf-8") diff --git a/CAPI/python/run.sh b/CAPI/python/run.sh index f0ff428..d787ec8 100755 --- a/CAPI/python/run.sh +++ b/CAPI/python/run.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0 -d -o& -python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -o& +python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 0& +# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 1 -o& # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 2& # python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 3& -# python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4& \ No newline at end of file +python PyAPI/main.py -I 172.22.32.1 -P 8888 -p 4 -d& \ No newline at end of file diff --git a/dependency/shell/run.sh b/dependency/shell/run.sh index 80c60d6..6a30e94 100644 --- a/dependency/shell/run.sh +++ b/dependency/shell/run.sh @@ -4,10 +4,10 @@ python_dir=/usr/local/PlayerCode/CAPI/python/PyAPI playback_dir=/usr/local/playback if [ $EXPOSED -eq 1 ]; then - nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $TIME --url $URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log & + nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $TIME --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 --notAllowSpectator --url $URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log & + nice -10 ./Server --port 8888 --studentCount 4 --trickerCount 1 --resultFileName $playback_dir/result --gameTimeInSecond $TIME --notAllowSpectator --url $URL --token $TOKEN --fileName $playback_dir/video --startLockFile $playback_dir/start.lock > $playback_dir/server.log 2>&1 & server_pid=$! fi sleep 5 @@ -20,9 +20,9 @@ do j=$((i - 1)) if [ -f "./player$i.py" ]; then cp -f ./player$i.py $python_dir/AI.py - nice -0 python3 $python_dir/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log & + nice -0 python3 $python_dir/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 & + 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 @@ -33,9 +33,9 @@ do j=$((i - 1)) if [ -f "./player$i.py" ]; then cp -f ./player$i.py $python_dir/AI.py - nice -0 python3 $python_dir/main.py -I 127.0.0.1 -P 8888 -p $j > $playback_dir/team$k-player$j.log & + nice -0 python3 $python_dir/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 & + 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 diff --git a/docs/CAPI接口(cpp).md b/docs/CAPI接口(cpp).md index 70bb297..bc7dac5 100644 --- a/docs/CAPI接口(cpp).md +++ b/docs/CAPI接口(cpp).md @@ -59,13 +59,13 @@ 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 - `THUAI6::PlaceType GetPlaceType(int32_t cellX, int32_t cellY)` :返回某一位置场地种类信息。场地种类详见 structure.h 。 - - `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`:查询特定位置门是否开启,没有门也返回false - - 以下指令,若查询物品当前在视野内,则返回最新进度;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 + - 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 - `int32_t GetChestProgress(int32_t cellX, int32_t cellY) const`:查询特定位置箱子开启进度 - `int32_t GetGateProgress(int32_t cellX, int32_t cellY) const`:查询特定位置校门开启进度 - `int32_t GetClassroomProgress(int32_t cellX, int32_t cellY) const`:查询特定位置教室作业完成进度 - `int32_t GetDoorProgress(int32_t cellX, int32_t cellY) const`:查询特定位置门开启状态 - - `THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const`::查询特定位置隐藏校门状态,没有隐藏校门返回THUAI6::HiddenGateState::Null + - `bool IsDoorOpen(int32_t cellX, int32_t cellY) const`:查询特定位置门是否开启,没有门/不在视野内也返回false + - `THUAI6::HiddenGateState GetHiddenGateState(int32_t cellX, int32_t cellY) const`::查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回THUAI6::HiddenGateState::Null #### 其他 - `std::shared_ptr GetGameInfo() const`:查询当前游戏状态 @@ -88,6 +88,14 @@ void PrintSelfInfo() const; ~~~ +### 部分属性解释 stuctures.h +~~~c++ + struct Player + { + std::vector props;//大小固定为3,空的位置为NullPropType + } +~~~ + ## 接口一览 ~~~c++ // 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 diff --git a/docs/CAPI接口(python).md b/docs/CAPI接口(python).md index 1b36a28..a2f53e4 100644 --- a/docs/CAPI接口(python).md +++ b/docs/CAPI接口(python).md @@ -72,14 +72,13 @@ 下面的 CellX 和 CellY 指的是地图格数,而非绝对坐标。 - `def GetPlaceType(self, cellX: int, cellY: int) -> THUAI6.PlaceType` :返回某一位置场地种类信息。场地种类详见 structure.h 。 - - `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`:查询特定位置门是否开启,没有门也返回false - - 以下指令,若查询物品当前在视野内,则返回最新进度;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 + - 以下指令,若查询物品当前在视野内,则返回最新进度/状态;若物品当前不在视野内、但曾经出现在视野内,则返回最后一次看到时的进度/状态;若物品从未出现在视野内,或查询位置没有对应的物品,则返回 -1。 - `def GetChestProgress(self, cellX: int, cellY: int) -> int`:查询特定位置箱子开启进度 - `def GetGateProgress(self, cellX: int, cellY: int) -> int`:查询特定位置校门开启进度 - `def GetClassroomProgress(self, cellX: int, cellY: int) -> int`:查询特定位置教室作业完成进度 - `def GetDoorProgress(self, cellX: int, cellY: int) -> int`:查询特定位置门开启状态 - - `def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`::查询特定位置隐藏校门状态,没有隐藏校门返回THUAI6::HiddenGateState::Null - + - `def IsDoorOpen(self, cellX: int, cellY: int) -> bool`:查询特定位置门是否开启,没有门/不在视野内也返回false + - `def GetHiddenGateState(self, cellX: int, cellY: int) -> THUAI6.HiddenGateState`::查询特定位置隐藏校门状态,没有隐藏校门/不在视野内返回THUAI6::HiddenGateState::Null #### 其他 @@ -105,6 +104,13 @@ def PrintSelfInfo(self) -> None: ~~~ +### 部分属性解释 stuctures.h +~~~python +class Player: + def __init__(self, **kwargs) -> None: + self.prop: List[PropType] = []//大小固定为3,空的位置为NullPropType +~~~ + ## 接口一览 ~~~python diff --git a/docs/GameRules.md b/docs/GameRules.md index c88870f..80cb1f1 100644 --- a/docs/GameRules.md +++ b/docs/GameRules.md @@ -64,7 +64,7 @@ CellX=\frac{x}{1000},CellY=\frac{y}{1000} $$ - 格子有对应区域类型:陆地、墙、草地、教室、校门、隐藏校门、门、窗、箱子 -- 隐藏校门刷新点的区域类型始终为隐藏校门 +- 任何格子的区域类型(PlaceType)始终不变,所有隐藏校门刷新点的区域类型均为隐藏校门 ### 人物 - 人物直径为800 @@ -360,10 +360,12 @@ $$ ### 信息相关 - Bgm在没有符合条件的情况下,值为0。 + - 不能给自己发信息 ### 技能 - CD冷却计时是在开始使用技能的瞬间开始的 - Klee的小炸弹有碰撞体积 +- 除了切换攻击类型的技能,在不能执行指令的状态下(包括翻窗)均不能使用技能 ### 职业 - 学生职业可以重复选择 \ No newline at end of file diff --git a/docs/QandA.md b/docs/QandA.md index ae141da..a190c91 100644 --- a/docs/QandA.md +++ b/docs/QandA.md @@ -22,11 +22,6 @@ Q:卡死在第一帧不动 A:大概率是你的代码死循环了 -Q: 怎么开始游戏? - -A: -需要确保学生阵营和捣蛋鬼阵营的人数都达到Server.cmd中设定的值。人数不足也可以打开WPF,参考使用文档,修改RunGUIClient.cmd的参数,然后运行RunGUIClient.cmd,这样可以通过WPF运行部分客户端,来达到人数限制。 - ## C++ Q:显示API项目已卸载 @@ -54,6 +49,15 @@ Q:CAPI编译不通过(第二种) A:查看`.\win\CAPI\cpp\`文件夹下是否有`lib`文件夹,没有则https://cloud.tsinghua.edu.cn/d/6972138f641d4e81a446/ 下载并复制粘贴 +Q:编译好慢啊 + +A: +1. 尽量不要改其他文件,甚至连点下保存都别点 +2. 不要点重新生成,要点生成 +3. 开启下图选项 +![CompileFaster](https://raw.githubusercontent.com/shangfengh/THUAI6/new/resource/CompileFaster.png) + + ## Python ### grpc版本更新失败 @@ -76,4 +80,4 @@ A:初赛结束会调数值及机制,增加新角色 Q:初赛后会修改什么呢? -A:技能冷却时间等属性设为不可见;出生点随机性或可选性;增强教师等职业,削弱职业;规范Debug信息 \ No newline at end of file +A:技能冷却时间等属性设为不可见;出生点随机性或可选性;增强教师等职业,削弱职业;规范Debug信息;提供不同格式的信息传递;增加职业;优化游戏结束条件;角色毅力值清空不在使捣蛋鬼产生BGM \ No newline at end of file diff --git a/docs/使用文档.md b/docs/使用文档.md index 12aa824..b9f3d46 100644 --- a/docs/使用文档.md +++ b/docs/使用文档.md @@ -14,7 +14,7 @@ - Windows:先查看`.\win\CAPI\cpp\`文件夹下是否有`lib`文件夹,没有则https://cloud.tsinghua.edu.cn/d/6972138f641d4e81a446/ 下载并复制粘贴 - Linux:首先自行安装`gRPC`,具体方法可以参考官方教程https://grpc.io/docs/languages/cpp/quickstart/。 - 然后在`CAPI\cpp\API\src\AI.cpp`中编写代码 - - 选手不应当修改`AI.cpp`中原来有的代码,除了`void AI::play(IStudentAPI& api)`和`void AI::play(ITrickerAPI& api)` + - 选手不应当修改`AI.cpp`中原来有的代码,除了`void AI::play(IStudentAPI& api)`和`void AI::play(ITrickerAPI& api)`,及修改asynchronous的返回值 - 每帧执行一次`AI::play(IStudentAPI& api)`或`AI::play(ITrickerAPI& api)`(除非执行该函数超过一帧50ms),获取的信息都是这一帧的开始的状态 - 选手可以在`AI.cpp`内`void AI::play`外新增函数和变量 - Windows:然后用Visual Studio打开`CAPI\cpp\CAPI.sln`编译,注意使用Debug模式 @@ -26,7 +26,7 @@ - 首先在Python环境下运行`GeneratePythonProto.cmd`,以安装必要的包、并生成对应的grpc python文件 - 然后在`CAPI\python\PyAPI\AI.py`中编写代码 - - 选手不应当修改`AI.py`中原来有的代码,除了`StudentPlay(self, api: IStudentAPI)`和`TrickerPlay(self, api: ITrickerAPI)` + - 选手不应当修改`AI.py`中原来有的代码,除了`StudentPlay(self, api: IStudentAPI)`和`TrickerPlay(self, api: ITrickerAPI)`,及修改asynchronous的返回值 - 每帧执行一次`AI::play(IStudentAPI& api)`或`AI::play(ITrickerAPI& api)`(除非执行该函数超过一帧50ms),获取的信息都是这一帧的开始的状态 - 选手可以在`AI.py`内新增函数和变量 - Windows:最后通过运行`RunPython.cmd`执行比赛代码 diff --git a/logic/Client/Client.csproj b/logic/Client/Client.csproj index 6af7e5c..abcb9a6 100644 --- a/logic/Client/Client.csproj +++ b/logic/Client/Client.csproj @@ -5,7 +5,7 @@ net6.0-windows enable true - EESAST.ico + eesast_software_trans_enlarged.ico @@ -13,7 +13,7 @@ - + diff --git a/logic/Client/MainWindow.xaml b/logic/Client/MainWindow.xaml index beacab5..e1326a7 100644 --- a/logic/Client/MainWindow.xaml +++ b/logic/Client/MainWindow.xaml @@ -5,7 +5,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Client" mc:Ignorable="d" - Title="ClientVI" Height="738" Width="850" KeyDown="KeyBoardControl" Background="White" ResizeMode="CanResizeWithGrip" WindowStyle="None" MouseLeftButtonDown="DragWindow" MouseDoubleClick="Attack" MinHeight="738" MinWidth="1100"> + Title="ClientVI" Height="738" Width="850" KeyDown="KeyBoardControl" Background="White" ResizeMode="CanResizeWithGrip" WindowStyle="None" MouseLeftButtonDown="DragWindow" MouseDoubleClick="Attack" MinHeight="738" MinWidth="1100" + SizeChanged="ZoomMap"> diff --git a/logic/Client/MainWindow.xaml.cs b/logic/Client/MainWindow.xaml.cs index 4ab6665..340fbd4 100644 --- a/logic/Client/MainWindow.xaml.cs +++ b/logic/Client/MainWindow.xaml.cs @@ -34,8 +34,6 @@ namespace Client { public MainWindow() { - unitHeight = unitWidth = unit = 13; - bonusflag = true; timer = new DispatcherTimer { Interval = new TimeSpan(50000) // 每50ms刷新一次 @@ -60,6 +58,10 @@ namespace Client listOfGate = new List(); listOfHiddenGate = new List(); WindowStartupLocation = WindowStartupLocation.CenterScreen; + unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; + unitFontsize = unit / 13; + unitHeight = UpperLayerOfMap.ActualHeight / 50; + unitWidth = UpperLayerOfMap.ActualWidth / 50; ReactToCommandline(); } @@ -194,6 +196,7 @@ namespace Client 0 => PlayerType.NullPlayerType, 1 => PlayerType.StudentPlayer, 2 => PlayerType.TrickerPlayer, + _ => PlayerType.NullPlayerType }; playerMsg.PlayerType = playerType; if (Convert.ToInt64(comInfo[3]) == 1) @@ -268,9 +271,9 @@ namespace Client { TextBox icon = new() { - FontSize = 10, - Width = 20, - Height = 20, + FontSize = 7 * unitFontsize, + Width = unitWidth, + Height = unitHeight, Text = text, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, @@ -282,37 +285,23 @@ namespace Client UpperLayerOfMap.Children.Add(icon); } - private void ZoomMap() - { - for (int i = 0; i < 50; i++) - { - for (int j = 0; j < 50; j++) - { - if (mapPatches[i, j] != null && (mapPatches[i, j].Width != UpperLayerOfMap.ActualWidth / 50 || mapPatches[i, j].Height != UpperLayerOfMap.ActualHeight / 50)) - { - mapPatches[i, j].Width = UpperLayerOfMap.ActualWidth / 50; - mapPatches[i, j].Height = UpperLayerOfMap.ActualHeight / 50; - mapPatches[i, j].HorizontalAlignment = HorizontalAlignment.Left; - mapPatches[i, j].VerticalAlignment = VerticalAlignment.Top; - mapPatches[i, j].Margin = new Thickness(UpperLayerOfMap.ActualWidth / 50 * j, UpperLayerOfMap.ActualHeight / 50 * i, 0, 0); - } - } - } - } - - private void ZoomMapAtFirst() + private void ZoomMap(object sender, SizeChangedEventArgs e) { + unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; + unitFontsize = unit / 13; + unitHeight = UpperLayerOfMap.ActualHeight / 50; + unitWidth = UpperLayerOfMap.ActualWidth / 50; for (int i = 0; i < 50; i++) { for (int j = 0; j < 50; j++) { if (mapPatches[i, j] != null) { - mapPatches[i, j].Width = UpperLayerOfMap.ActualWidth / 50; - mapPatches[i, j].Height = UpperLayerOfMap.ActualHeight / 50; + mapPatches[i, j].Width = unitWidth; + mapPatches[i, j].Height = unitHeight; mapPatches[i, j].HorizontalAlignment = HorizontalAlignment.Left; mapPatches[i, j].VerticalAlignment = VerticalAlignment.Top; - mapPatches[i, j].Margin = new Thickness(UpperLayerOfMap.ActualWidth / 50 * j, UpperLayerOfMap.ActualHeight / 50 * i, 0, 0); + mapPatches[i, j].Margin = new Thickness(unitWidth * j, unitHeight * i, 0, 0); } } } @@ -330,7 +319,7 @@ namespace Client Height = unitHeight, HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, - Margin = new Thickness(Width * (j), Height * (i), 0, 0) + Margin = new Thickness(unitWidth * j, unitHeight * i, 0, 0)//unitWidth cannot be replaced by Width }; switch (defaultMap[i, j]) { @@ -697,7 +686,7 @@ namespace Client MaxButton.Content = "🗖"; foreach (var obj in listOfHuman) { - if (obj.PlayerId < GameData.numOfStudent && obj.StudentType != StudentType.Robot) + if (!isDataFixed[obj.PlayerId] && obj.PlayerId < GameData.numOfStudent && obj.StudentType != StudentType.Robot) { IStudentType occupation = (IStudentType)OccupationFactory.FindIOccupation(Transformation.ToStudentType(obj.StudentType)); totalLife[obj.PlayerId] = occupation.MaxHp; @@ -709,60 +698,52 @@ namespace Client coolTime[i, obj.PlayerId] = iActiveSkill.SkillCD; ++i; } + isDataFixed[obj.PlayerId] = true; } } foreach (var obj in listOfButcher) { - IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); - int j = 0; - foreach (var skill in occupation1.ListOfIActiveSkill) + if (!isDataFixed[obj.PlayerId]) { - var iActiveSkill = SkillFactory.FindIActiveSkill(skill); - coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; - ++j; + IGhostType occupation1 = (IGhostType)OccupationFactory.FindIOccupation(Transformation.ToTrickerType(obj.TrickerType)); + int j = 0; + foreach (var skill in occupation1.ListOfIActiveSkill) + { + var iActiveSkill = SkillFactory.FindIActiveSkill(skill); + coolTime[j, GameData.numOfStudent] = iActiveSkill.SkillCD; + ++j; + } + isDataFixed[obj.PlayerId] = true; } } if (StatusBarsOfSurvivor != null) { for (int i = 0; i < GameData.numOfStudent; i++) { - StatusBarsOfSurvivor[i].SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); StatusBarsOfSurvivor[i].NewData(totalLife, totalDeath, coolTime); } } if (StatusBarsOfHunter != null) { - StatusBarsOfHunter.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); StatusBarsOfHunter.NewData(totalLife, totalDeath, coolTime); } - if (StatusBarsOfCircumstance != null) - StatusBarsOfCircumstance.SetFontSize(12 * UpperLayerOfMap.ActualHeight / 650); // 完成窗口信息更新 + if (StatusBarsOfSurvivor != null) + { + for (int i = 0; i < GameData.numOfStudent; i++) + { + StatusBarsOfSurvivor[i].SetFontSize(12 * unitFontsize); + } + } + if (StatusBarsOfHunter != null) + StatusBarsOfHunter.SetFontSize(12 * unitFontsize); + if (StatusBarsOfCircumstance != null) + StatusBarsOfCircumstance.SetFontSize(12 * unitFontsize); if (!isClientStocked) { - unit = Math.Sqrt(UpperLayerOfMap.ActualHeight * UpperLayerOfMap.ActualWidth) / 50; - unitHeight = UpperLayerOfMap.ActualHeight / 50; - unitWidth = UpperLayerOfMap.ActualWidth / 50; try { - // if (log != null) - //{ - // string temp = ""; - // for (int i = 0; i < dataDict[GameObjType.Character].Count; i++) - // { - // temp += Convert.ToString(dataDict[GameObjType.Character][i].MessageOfCharacter.TeamID) + "\n"; - // } - // log.Content = temp; - // } UpperLayerOfMap.Children.Clear(); - // if ((communicator == null || !communicator.Client.IsConnected) && !isPlaybackMode) - //{ - // UnderLayerOfMap.Children.Clear(); - // throw new Exception("Client is unconnected."); - // } - // else - //{ - foreach (var data in listOfAll) { StatusBarsOfCircumstance.SetValue(data, gateOpened, isEmergencyDrawed, isEmergencyOpened, playerID, isPlaybackMode); @@ -770,7 +751,6 @@ namespace Client if (!hasDrawed && mapFlag) { DrawMap(); - ZoomMapAtFirst(); } foreach (var data in listOfHuman) { @@ -791,7 +771,7 @@ namespace Client icon.Fill = Brushes.Gray; TextBox num = new() { - FontSize = 7 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 7 * unitFontsize, Width = 2 * radiusTimes * unitWidth, Height = 2 * radiusTimes * unitHeight, Text = Convert.ToString(data.PlayerId), @@ -943,7 +923,7 @@ namespace Client int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfFixedGenerator); TextBox icon = new() { - FontSize = 8 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 8 * unitFontsize, Width = unitWidth, Height = unitHeight, Text = Convert.ToString(deg), @@ -965,7 +945,7 @@ namespace Client int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedChest); TextBox icon = new() { - FontSize = 8 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 8 * unitFontsize, Width = unitWidth, Height = unitHeight, Text = Convert.ToString(deg), @@ -987,7 +967,7 @@ namespace Client int deg = (int)(100.0 * data.Progress / Preparation.Utility.GameData.degreeOfOpenedDoorway); TextBox icon = new() { - FontSize = 8 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 8 * unitFontsize, Width = unitWidth, Height = unitHeight, Text = Convert.ToString(deg), @@ -1009,7 +989,7 @@ namespace Client { TextBox icon = new() { - FontSize = 9 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 9 * unitFontsize, Width = unitWidth, Height = unitHeight, HorizontalAlignment = HorizontalAlignment.Left, @@ -1042,7 +1022,7 @@ namespace Client isEmergencyOpened = true; TextBox icon = new() { - FontSize = 9 * UpperLayerOfMap.ActualHeight / 650, + FontSize = 9 * unitFontsize, Width = unitWidth, Height = unitHeight, Text = Convert.ToString("🔓"), @@ -1056,8 +1036,6 @@ namespace Client UpperLayerOfMap.Children.Add(icon); } } - //} - ZoomMap(); } catch (Exception exc) { @@ -1440,7 +1418,6 @@ namespace Client private MessageOfTricker? butcher = null; private bool humanOrButcher;//true for human - private bool bonusflag; private bool mapFlag = false; private bool hasDrawed = false; public int[,] defaultMap = new int[,] { @@ -1502,7 +1479,8 @@ namespace Client bool isSpectatorMode = false; bool isEmergencyOpened = false; bool isEmergencyDrawed = false; - bool isDataFixed = false; + bool[] isDataFixed = new bool[5] { false, false, false, false, false }; + double unitFontsize = 10; const double radiusTimes = 1.0 * Preparation.Utility.GameData.characterRadius / Preparation.Utility.GameData.numOfPosGridPerCell; const double bulletRadiusTimes = 1.0 * GameData.bulletRadius / Preparation.Utility.GameData.numOfPosGridPerCell; private int[] totalLife = new int[4] { 100, 100, 100, 100 }, totalDeath = new int[4] { 100, 100, 100, 100 }; diff --git a/logic/Client/eesast_software_trans.ico b/logic/Client/eesast_software_trans.ico new file mode 100644 index 0000000..2544246 Binary files /dev/null and b/logic/Client/eesast_software_trans.ico differ diff --git a/logic/Client/eesast_software_trans_enlarged.ico b/logic/Client/eesast_software_trans_enlarged.ico new file mode 100644 index 0000000..cbafc97 Binary files /dev/null and b/logic/Client/eesast_software_trans_enlarged.ico differ diff --git a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs index 672e40f..2b3df5c 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.Ghost.cs @@ -1,6 +1,5 @@ using Preparation.Interface; using Preparation.Utility; -using System; namespace GameClass.GameObj { diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs index 1a5eafa..953ddb3 100644 --- a/logic/GameClass/GameObj/Map/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -3,7 +3,6 @@ using System.Threading; using Preparation.Interface; using Preparation.Utility; using System; -using GameClass.GameObj; namespace GameClass.GameObj { @@ -13,29 +12,47 @@ namespace GameClass.GameObj private readonly Dictionary birthPointList; // 出生点列表 public Dictionary BirthPointList => birthPointList; - private object lockForNum = new(); + private readonly object lockForNum = new(); private void WhenStudentNumChange() { if (numOfDeceasedStudent + numOfEscapedStudent == GameData.numOfStudent) { Timer.IsGaming = false; + return; } - if (GameData.numOfStudent - NumOfDeceasedStudent - NumOfEscapedStudent == 1) + if (GameData.numOfStudent - numOfDeceasedStudent - numOfEscapedStudent == 1) { - GameObjLockDict[GameObjType.EmergencyExit].EnterReadLock(); + GameObjLockDict[GameObjType.Character].EnterReadLock(); try { - foreach (EmergencyExit emergencyExit in GameObjDict[GameObjType.EmergencyExit]) - if (emergencyExit.CanOpen) + foreach (Character player in GameObjDict[GameObjType.Character]) + if (player.PlayerState == PlayerStateType.Addicted) { - emergencyExit.IsOpen = true; + Timer.IsGaming = false; break; } } finally { - GameObjLockDict[GameObjType.EmergencyExit].ExitReadLock(); + GameObjLockDict[GameObjType.Character].ExitReadLock(); + } + if (Timer.IsGaming) + { + GameObjLockDict[GameObjType.EmergencyExit].EnterWriteLock(); + try + { + foreach (EmergencyExit emergencyExit in GameObjDict[GameObjType.EmergencyExit]) + if (emergencyExit.CanOpen) + { + emergencyExit.IsOpen = true; + break; + } + } + finally + { + GameObjLockDict[GameObjType.EmergencyExit].ExitWriteLock(); + } } } } diff --git a/logic/GameEngine/CollisionChecker.cs b/logic/GameEngine/CollisionChecker.cs index 046d5bc..1a7a2e3 100644 --- a/logic/GameEngine/CollisionChecker.cs +++ b/logic/GameEngine/CollisionChecker.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Numerics; using System.Threading; using Preparation.Interface; using Preparation.Utility; diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index e86e429..0db4748 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -6,7 +6,6 @@ using Preparation.Utility; using GameEngine; using Preparation.Interface; using Timothy.FrameRateTask; -using System.Numerics; namespace Gaming { diff --git a/logic/Gaming/SkillManager/SkillManager.cs b/logic/Gaming/SkillManager/SkillManager.cs index 9cf6f09..59fc7e3 100644 --- a/logic/Gaming/SkillManager/SkillManager.cs +++ b/logic/Gaming/SkillManager/SkillManager.cs @@ -17,44 +17,31 @@ namespace Gaming switch (activeSkillType) { case ActiveSkillType.BecomeInvisible: - BecomeInvisible(character); - break; + return BecomeInvisible(character); case ActiveSkillType.UseKnife: - UseKnife(character); - break; + return UseKnife(character); case ActiveSkillType.Howl: - Howl(character); - break; + return Howl(character); case ActiveSkillType.CanBeginToCharge: - CanBeginToCharge(character); - break; + return CanBeginToCharge(character); case ActiveSkillType.Inspire: - Inspire(character); - break; + return Inspire(character); case ActiveSkillType.Encourage: - Encourage(character); - break; + return Encourage(character); case ActiveSkillType.Punish: - Punish(character); - break; + return Punish(character); case ActiveSkillType.JumpyBomb: - JumpyBomb(character); - break; + return JumpyBomb(character); case ActiveSkillType.WriteAnswers: - WriteAnswers(character); - break; + return WriteAnswers(character); case ActiveSkillType.SummonGolem: - SummonGolem(character); - break; + return SummonGolem(character); case ActiveSkillType.UseRobot: - UseRobot(character); - break; + return UseRobot(character); case ActiveSkillType.Rouse: - Rouse(character); - break; + return Rouse(character); case ActiveSkillType.ShowTime: - ShowTime(character); - break; + return ShowTime(character); default: return false; } diff --git a/logic/Server/PlaybackServer.cs b/logic/Server/PlaybackServer.cs index 1506b97..449c1ed 100644 --- a/logic/Server/PlaybackServer.cs +++ b/logic/Server/PlaybackServer.cs @@ -5,6 +5,7 @@ using System.Threading; using Timothy.FrameRateTask; using Gaming; using Grpc.Core; +using System.Collections.Concurrent; namespace Server { @@ -12,13 +13,31 @@ namespace Server { protected readonly ArgumentOptions options; private int[,] teamScore; - private Dictionary semaDict = new(); + private ConcurrentDictionary semaDict = new(); private object semaDictLock = new(); private MessageToClient? currentGameInfo = new(); + private MessageOfObj currentMapMsg = new(); private uint spectatorMinPlayerID = 2023; private List spectatorList = new List(); public int TeamCount => options.TeamCount; private MessageWriter? mwr = null; + private object spetatorJoinLock = new(); + protected object spectatorLock = new object(); + protected bool isSpectatorJoin = false; + protected bool IsSpectatorJoin + { + get + { + lock (spectatorLock) + return isSpectatorJoin; + } + + set + { + lock (spectatorLock) + isSpectatorJoin = value; + } + } private bool IsGaming { get; set; } private int[] finalScore; public int[] FinalScore @@ -38,18 +57,20 @@ namespace Server public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter responseStream, ServerCallContext context) { - if (request.PlayerId >= spectatorMinPlayerID) + if (request.PlayerId >= spectatorMinPlayerID && options.NotAllowSpectator == false) { // 观战模式 - uint tp = (uint)request.PlayerId; - if (!spectatorList.Contains(tp)) + lock (spetatorJoinLock) // 具体原因见另一个上锁的地方 { - spectatorList.Add(tp); - Console.WriteLine("A new spectator comes to watch this game."); - var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); - lock (semaDictLock) + if (semaDict.TryAdd(request.PlayerId, (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)))) + { + Console.WriteLine("A new spectator comes to watch this game."); + IsSpectatorJoin = true; + } + else { - semaDict.Add(request.PlayerId, temp); + Console.WriteLine($"Duplicated Spectator ID {request.PlayerId}"); + return; } } do @@ -63,15 +84,32 @@ namespace Server //Console.WriteLine("Send!"); } } + catch (InvalidOperationException) + { + if (semaDict.TryRemove(request.PlayerId, out var semas)) + { + try + { + semas.Item1.Release(); + semas.Item2.Release(); + } + catch { } + Console.WriteLine($"The spectator {request.PlayerId} exited"); + } + } catch (Exception) { - //Console.WriteLine(ex); + // Console.WriteLine(ex); } finally { - semaDict[request.PlayerId].Item2.Release(); + try + { + semaDict[request.PlayerId].Item2.Release(); + } + catch { } } - } while (IsGaming == true); + } while (IsGaming); return; } } @@ -79,6 +117,16 @@ namespace Server public void ReportGame(MessageToClient? msg) { currentGameInfo = msg; + if (currentGameInfo != null && currentGameInfo.GameState == GameState.GameStart) + { + currentMapMsg = currentGameInfo.ObjMessage[0]; + } + + if (currentGameInfo != null && IsSpectatorJoin) + { + currentGameInfo.ObjMessage.Add(currentMapMsg); + IsSpectatorJoin = false; + } foreach (var kvp in semaDict) { diff --git a/logic/Server/RpcServices.cs b/logic/Server/RpcServices.cs index 1507269..ca07351 100644 --- a/logic/Server/RpcServices.cs +++ b/logic/Server/RpcServices.cs @@ -1,7 +1,6 @@ using Grpc.Core; using Protobuf; using System.Threading; -using Timothy.FrameRateTask; using System; using System.Net.Http.Headers; using Gaming; diff --git a/logic/cmd/PlaybackServer.cmd b/logic/cmd/PlaybackServer.cmd index c3dd0bb..916c9ea 100644 --- a/logic/cmd/PlaybackServer.cmd +++ b/logic/cmd/PlaybackServer.cmd @@ -1,6 +1,2 @@ @echo off -::start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --ip 0.0.0.0 --port 8888 --studentCount 4 --trickerCount 1 --gameTimeInSecond 600 --fileName test --playback true - -ping -n 2 127.0.0.1 > NUL - -start cmd /k ..\Client\bin\Debug\net6.0-windows\Client.exe --cl --port 8888 --characterID 2030 \ No newline at end of file +start cmd /k ..\Server\bin\Debug\net6.0\Server.exe --port 8888 --fileName .\ladder1.thuaipb --playback --playbackSpeed 4.0 diff --git a/resource/CompileFaster.png b/resource/CompileFaster.png new file mode 100644 index 0000000..f27537d Binary files /dev/null and b/resource/CompileFaster.png differ diff --git a/resource/eesast_software.png b/resource/eesast_software.png new file mode 100644 index 0000000..5d980f2 Binary files /dev/null and b/resource/eesast_software.png differ diff --git a/resource/eesast_software_trans.ico b/resource/eesast_software_trans.ico new file mode 100644 index 0000000..2544246 Binary files /dev/null and b/resource/eesast_software_trans.ico differ diff --git a/resource/eesast_software_trans.png b/resource/eesast_software_trans.png new file mode 100644 index 0000000..6d7d511 Binary files /dev/null and b/resource/eesast_software_trans.png differ diff --git a/resource/eesast_software_trans_enlarged.ico b/resource/eesast_software_trans_enlarged.ico new file mode 100644 index 0000000..cbafc97 Binary files /dev/null and b/resource/eesast_software_trans_enlarged.ico differ