diff --git a/CAPI/cpp/API/include/Communication.h b/CAPI/cpp/API/include/Communication.h index c4b98c7..0946ff4 100644 --- a/CAPI/cpp/API/include/Communication.h +++ b/CAPI/cpp/API/include/Communication.h @@ -57,6 +57,9 @@ private: bool haveNewMessage = false; protobuf::MessageToClient message2Client; std::mutex mtxMessage; + std::mutex mtxLimit; + int counter; + static constexpr const int limit = 50; std::condition_variable cvMessage; }; diff --git a/CAPI/cpp/API/src/Communication.cpp b/CAPI/cpp/API/src/Communication.cpp index 09156d2..0a79229 100644 --- a/CAPI/cpp/API/src/Communication.cpp +++ b/CAPI/cpp/API/src/Communication.cpp @@ -20,6 +20,12 @@ Communication::Communication(std::string sIP, std::string sPort) bool Communication::Move(int64_t time, double angle, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::MoveRes moveResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufMove(time, angle, playerID); @@ -32,6 +38,12 @@ bool Communication::Move(int64_t time, double angle, int64_t playerID) bool Communication::PickProp(THUAI6::PropType prop, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes pickPropResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); @@ -44,6 +56,12 @@ bool Communication::PickProp(THUAI6::PropType prop, int64_t playerID) bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes usePropResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); @@ -56,6 +74,12 @@ bool Communication::UseProp(THUAI6::PropType prop, int64_t playerID) bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes throwPropResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufProp(prop, playerID); @@ -68,6 +92,12 @@ bool Communication::ThrowProp(THUAI6::PropType prop, int64_t playerID) bool Communication::UseSkill(int32_t skillID, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes useSkillResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufSkill(skillID, playerID); @@ -80,6 +110,12 @@ bool Communication::UseSkill(int32_t skillID, int64_t playerID) bool Communication::SendMessage(int64_t toID, std::string message, bool binary, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes sendMessageResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufSend(message, toID, binary, playerID); @@ -92,6 +128,12 @@ bool Communication::SendMessage(int64_t toID, std::string message, bool binary, bool Communication::OpenDoor(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes openDoorResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -104,6 +146,12 @@ bool Communication::OpenDoor(int64_t playerID) bool Communication::CloseDoor(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes closeDoorResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -116,6 +164,12 @@ bool Communication::CloseDoor(int64_t playerID) bool Communication::SkipWindow(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes skipWindowResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -128,6 +182,12 @@ bool Communication::SkipWindow(int64_t playerID) bool Communication::StartOpenGate(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes startOpenGateResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -140,6 +200,12 @@ bool Communication::StartOpenGate(int64_t playerID) bool Communication::StartOpenChest(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes startOpenChestResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -152,6 +218,12 @@ bool Communication::StartOpenChest(int64_t playerID) bool Communication::EndAllAction(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes endAllActionResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -164,6 +236,12 @@ bool Communication::EndAllAction(int64_t playerID) bool Communication::Graduate(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes graduateResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -176,6 +254,12 @@ bool Communication::Graduate(int64_t playerID) bool Communication::StartLearning(int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes startLearningResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufID(playerID); @@ -188,6 +272,12 @@ bool Communication::StartLearning(int64_t playerID) bool Communication::StartRouseMate(int64_t playerID, int64_t mateID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes saveStudentResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID); @@ -200,6 +290,12 @@ bool Communication::StartRouseMate(int64_t playerID, int64_t mateID) bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes healStudentResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufTreatAndRescue(playerID, mateID); @@ -212,6 +308,12 @@ bool Communication::StartEncourageMate(int64_t playerID, int64_t mateID) bool Communication::Attack(double angle, int64_t playerID) { + { + std::lock_guard lock(mtxLimit); + if (counter > limit) + return false; + counter++; + } protobuf::BoolRes attackResult; ClientContext context; auto request = THUAI62Proto::THUAI62ProtobufAttack(angle, playerID); @@ -229,9 +331,7 @@ bool Communication::TryConnection(int64_t playerID) auto request = THUAI62Proto::THUAI62ProtobufID(playerID); auto status = THUAI6Stub->TryConnection(&context, request, &reply); if (status.ok()) - { return true; - } else return false; } @@ -254,6 +354,7 @@ void Communication::AddPlayer(int64_t playerID, THUAI6::PlayerType playerType, T auto MessageReader = THUAI6Stub->AddPlayer(&context, playerMsg); protobuf::MessageToClient buffer2Client; + counter = 0; while (MessageReader->Read(&buffer2Client)) { @@ -261,6 +362,10 @@ void Communication::AddPlayer(int64_t playerID, THUAI6::PlayerType playerType, T std::lock_guard lock(mtxMessage); message2Client = std::move(buffer2Client); haveNewMessage = true; + { + std::lock_guard lock(mtxLimit); + counter = 0; + } } cvMessage.notify_one(); } diff --git a/CAPI/cpp/proto/Services.grpc.pb.h b/CAPI/cpp/proto/Services.grpc.pb.h index 7cf6d74..e99d5cc 100644 --- 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/API.py b/CAPI/python/PyAPI/API.py index cbade53..d1a6317 100644 --- a/CAPI/python/PyAPI/API.py +++ b/CAPI/python/PyAPI/API.py @@ -6,7 +6,6 @@ from typing import List, cast, Tuple, Union class StudentAPI(IStudentAPI, IGameTimer): - def __init__(self, logic: ILogic) -> None: self.__logic = logic self.__pool = ThreadPoolExecutor(20) @@ -133,7 +132,13 @@ class StudentAPI(IStudentAPI, IGameTimer): return self.__logic.GetGameInfo() def HaveView(self, gridX: int, gridY: int) -> bool: - return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange) + return self.__logic.HaveView( + gridX, + gridY, + self.GetSelfInfo().x, + self.GetSelfInfo().y, + self.GetSelfInfo().viewRange, + ) # 用于DEBUG的输出函数,仅在DEBUG模式下有效 @@ -182,7 +187,6 @@ class StudentAPI(IStudentAPI, IGameTimer): class TrickerAPI(ITrickerAPI, IGameTimer): - def __init__(self, logic: ILogic) -> None: self.__logic = logic self.__pool = ThreadPoolExecutor(20) @@ -309,7 +313,13 @@ class TrickerAPI(ITrickerAPI, IGameTimer): return self.__logic.GetGameInfo() def HaveView(self, gridX: int, gridY: int) -> bool: - return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange) + return self.__logic.HaveView( + gridX, + gridY, + self.GetSelfInfo().x, + self.GetSelfInfo().y, + self.GetSelfInfo().viewRange, + ) # 用于DEBUG的输出函数,仅在DEBUG模式下有效 diff --git a/CAPI/python/PyAPI/Communication.py b/CAPI/python/PyAPI/Communication.py index 19dfb04..2b05710 100644 --- a/CAPI/python/PyAPI/Communication.py +++ b/CAPI/python/PyAPI/Communication.py @@ -19,17 +19,25 @@ class BoolErrorHandler(IErrorHandler): class Communication: def __init__(self, sIP: str, sPort: str): - aim = sIP + ':' + sPort + aim = sIP + ":" + sPort channel = grpc.insecure_channel(aim) self.__THUAI6Stub = Services.AvailableServiceStub(channel) self.__haveNewMessage = False self.__cvMessage = threading.Condition() self.__message2Client: Message2Clients.MessageToClient + self.__mtxLimit = threading.Lock() + self.__counter = 0 + self.__limit = 50 def Move(self, time: int, angle: float, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 moveResult = self.__THUAI6Stub.Move( - THUAI62Proto.THUAI62ProtobufMove(time, angle, playerID)) + THUAI62Proto.THUAI62ProtobufMove(time, angle, playerID) + ) except grpc.RpcError as e: return False else: @@ -37,8 +45,13 @@ class Communication: def PickProp(self, propType: THUAI6.PropType, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 pickResult = self.__THUAI6Stub.PickProp( - THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) + THUAI62Proto.THUAI62ProtobufProp(propType, playerID) + ) except grpc.RpcError as e: return False else: @@ -46,8 +59,13 @@ class Communication: def UseProp(self, propType: THUAI6.PropType, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 useResult = self.__THUAI6Stub.UseProp( - THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) + THUAI62Proto.THUAI62ProtobufProp(propType, playerID) + ) except grpc.RpcError as e: return False else: @@ -55,8 +73,13 @@ class Communication: def ThrowProp(self, propType: THUAI6.PropType, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 throwResult = self.__THUAI6Stub.ThrowProp( - THUAI62Proto.THUAI62ProtobufProp(propType, playerID)) + THUAI62Proto.THUAI62ProtobufProp(propType, playerID) + ) except grpc.RpcError as e: return False else: @@ -64,8 +87,13 @@ class Communication: def UseSkill(self, skillID: int, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 useResult = self.__THUAI6Stub.UseSkill( - THUAI62Proto.THUAI62ProtobufSkill(skillID, playerID)) + THUAI62Proto.THUAI62ProtobufSkill(skillID, playerID) + ) except grpc.RpcError as e: return False else: @@ -73,8 +101,13 @@ class Communication: def SendMessage(self, toID: int, message: Union[str, bytes], playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 sendResult = self.__THUAI6Stub.SendMessage( - THUAI62Proto.THUAI62ProtobufSend(message, toID, playerID)) + THUAI62Proto.THUAI62ProtobufSend(message, toID, playerID) + ) except grpc.RpcError as e: return False else: @@ -82,8 +115,13 @@ class Communication: def Graduate(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 escapeResult = self.__THUAI6Stub.Graduate( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -91,8 +129,13 @@ class Communication: def StartLearning(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 learnResult = self.__THUAI6Stub.StartLearning( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -100,8 +143,13 @@ class Communication: def StartEncourageMate(self, playerID: int, mateID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 helpResult = self.__THUAI6Stub.StartTreatMate( - THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) + THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID) + ) except grpc.RpcError as e: return False else: @@ -109,8 +157,13 @@ class Communication: def StartRouseMate(self, playerID: int, mateID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 helpResult = self.__THUAI6Stub.StartRescueMate( - THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID)) + THUAI62Proto.THUAI62ProtobufTreatAndRescue(playerID, mateID) + ) except grpc.RpcError as e: return False else: @@ -118,8 +171,13 @@ class Communication: def Attack(self, angle: float, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 attackResult = self.__THUAI6Stub.Attack( - THUAI62Proto.THUAI62ProtobufAttack(angle, playerID)) + THUAI62Proto.THUAI62ProtobufAttack(angle, playerID) + ) except grpc.RpcError as e: return False else: @@ -127,8 +185,13 @@ class Communication: def OpenDoor(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 openResult = self.__THUAI6Stub.OpenDoor( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -136,8 +199,13 @@ class Communication: def CloseDoor(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 closeResult = self.__THUAI6Stub.CloseDoor( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -145,8 +213,13 @@ class Communication: def SkipWindow(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 skipResult = self.__THUAI6Stub.SkipWindow( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -154,8 +227,13 @@ class Communication: def StartOpenGate(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 openResult = self.__THUAI6Stub.StartOpenGate( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -163,8 +241,13 @@ class Communication: def StartOpenChest(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 openResult = self.__THUAI6Stub.StartOpenChest( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -172,8 +255,13 @@ class Communication: def EndAllAction(self, playerID: int) -> bool: try: + with self.__mtxLimit: + if self.__counter > self.__limit: + return False + self.__counter += 1 endResult = self.__THUAI6Stub.EndAllAction( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -182,7 +270,8 @@ class Communication: def TryConnection(self, playerID: int) -> bool: try: connectResult = self.__THUAI6Stub.TryConnection( - THUAI62Proto.THUAI62ProtobufID(playerID)) + THUAI62Proto.THUAI62ProtobufID(playerID) + ) except grpc.RpcError as e: return False else: @@ -202,12 +291,15 @@ class Communication: else: studentType = THUAI6.StudentType.NullStudentType playerMsg = THUAI62Proto.THUAI62ProtobufPlayer( - playerID, playerType, studentType, Setting.trickerType()) + playerID, playerType, studentType, Setting.trickerType() + ) for msg in self.__THUAI6Stub.AddPlayer(playerMsg): with self.__cvMessage: self.__haveNewMessage = True self.__message2Client = msg self.__cvMessage.notify() + with self.__mtxLimit: + self.__counter = 0 except grpc.RpcError as e: return diff --git a/CAPI/python/PyAPI/DebugAPI.py b/CAPI/python/PyAPI/DebugAPI.py index dec6f45..891d35a 100644 --- a/CAPI/python/PyAPI/DebugAPI.py +++ b/CAPI/python/PyAPI/DebugAPI.py @@ -10,22 +10,34 @@ from PyAPI.Interface import ILogic, IStudentAPI, ITrickerAPI, IGameTimer, IAI class StudentDebugAPI(IStudentAPI, IGameTimer): - - def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None: + def __init__( + self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int + ) -> None: self.__logic = logic self.__pool = ThreadPoolExecutor(20) self.__startPoint = datetime.datetime.now() self.__logger = logging.getLogger("api " + str(playerID)) self.__logger.setLevel(logging.DEBUG) formatter = logging.Formatter( - "[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S') + "[%(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") - - fileHandler = logging.FileHandler(os.path.dirname( - os.path.dirname(os.path.realpath(__file__))) + "/logs/api-" + str(playerID) + "-log.txt", mode="w+", encoding="utf-8") + 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" + ) + + fileHandler = logging.FileHandler( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + "/logs/api-" + + str(playerID) + + "-log.txt", + mode="w+", + encoding="utf-8", + ) screenHandler = logging.StreamHandler() if file: fileHandler.setLevel(logging.DEBUG) @@ -43,13 +55,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: self.__logger.info( - f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms") + f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms" + ) def logMove() -> bool: result = self.__logic.Move(timeInMilliseconds, angle) if not result: - self.__logger.warning( - f"Move: failed at {self.__GetTime()}ms") + self.__logger.warning(f"Move: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logMove) @@ -69,14 +81,12 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): return self.Move(timeInMilliseconds, 0) def Attack(self, angle: float) -> Future[bool]: - self.__logger.info( - f"Attack: angle = {angle}, called at {self.__GetTime()}ms") + self.__logger.info(f"Attack: angle = {angle}, called at {self.__GetTime()}ms") def logAttack() -> bool: result = self.__logic.Attack(angle) if not result: - self.__logger.warning( - f"Attack: failed at {self.__GetTime()}ms") + self.__logger.warning(f"Attack: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logAttack) @@ -85,131 +95,119 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logPick() -> bool: result = self.__logic.PickProp(propType) if not result: - self.__logger.warning( - f"PickProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"PickProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logPick) def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logUse() -> bool: result = self.__logic.UseProp(propType) if not result: - self.__logger.warning( - f"UseProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"UseProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logUse) def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logThrow() -> bool: result = self.__logic.ThrowProp(propType) if not result: - self.__logger.warning( - f"ThrowProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"ThrowProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logThrow) def UseSkill(self, skillID: int) -> Future[bool]: self.__logger.info( - f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") + f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms" + ) def logUse() -> bool: result = self.__logic.UseSkill(skillID) if not result: - self.__logger.warning( - f"UseSkill: failed at {self.__GetTime()}ms") + self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logUse) # 与地图交互相关 def OpenDoor(self) -> Future[bool]: - self.__logger.info( - f"OpenDoor: called at {self.__GetTime()}ms") + self.__logger.info(f"OpenDoor: called at {self.__GetTime()}ms") def logOpen() -> bool: result = self.__logic.OpenDoor() if not result: - self.__logger.warning( - f"OpenDoor: failed at {self.__GetTime()}ms") + self.__logger.warning(f"OpenDoor: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logOpen) def CloseDoor(self) -> Future[bool]: - self.__logger.info( - f"CloseDoor: called at {self.__GetTime()}ms") + self.__logger.info(f"CloseDoor: called at {self.__GetTime()}ms") def logClose() -> bool: result = self.__logic.CloseDoor() if not result: - self.__logger.warning( - f"CloseDoor: failed at {self.__GetTime()}ms") + self.__logger.warning(f"CloseDoor: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logClose) def SkipWindow(self) -> Future[bool]: - self.__logger.info( - f"SkipWindow: called at {self.__GetTime()}ms") + self.__logger.info(f"SkipWindow: called at {self.__GetTime()}ms") def logSkip() -> bool: result = self.__logic.SkipWindow() if not result: - self.__logger.warning( - f"SkipWindow: failed at {self.__GetTime()}ms") + self.__logger.warning(f"SkipWindow: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logSkip) def StartOpenGate(self) -> Future[bool]: - self.__logger.info( - f"StartOpenGate: called at {self.__GetTime()}ms") + self.__logger.info(f"StartOpenGate: called at {self.__GetTime()}ms") def logStart() -> bool: result = self.__logic.StartOpenGate() if not result: - self.__logger.warning( - f"StartOpenGate: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartOpenGate: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStart) def StartOpenChest(self) -> Future[bool]: - self.__logger.info( - f"StartOpenChest: called at {self.__GetTime()}ms") + self.__logger.info(f"StartOpenChest: called at {self.__GetTime()}ms") def logStart() -> bool: result = self.__logic.StartOpenChest() if not result: - self.__logger.warning( - f"StartOpenChest: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartOpenChest: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStart) def EndAllAction(self) -> Future[bool]: - self.__logger.info( - f"EndAllAction: called at {self.__GetTime()}ms") + self.__logger.info(f"EndAllAction: called at {self.__GetTime()}ms") def logEnd() -> bool: result = self.__logic.EndAllAction() if not result: - self.__logger.warning( - f"EndAllAction: failed at {self.__GetTime()}ms") + self.__logger.warning(f"EndAllAction: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logEnd) @@ -218,40 +216,35 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]: self.__logger.info( - f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms") + f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms" + ) def logSend() -> bool: result = self.__logic.SendMessage(toID, message) if not result: - self.__logger.warning( - f"SendMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"SendMessage: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logSend) def HaveMessage(self) -> bool: - self.__logger.info( - f"HaveMessage: called at {self.__GetTime()}ms") + self.__logger.info(f"HaveMessage: called at {self.__GetTime()}ms") result = self.__logic.HaveMessage() if not result: - self.__logger.warning( - f"HaveMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"HaveMessage: failed at {self.__GetTime()}ms") return result def GetMessage(self) -> Tuple[int, Union[str, bytes]]: - self.__logger.info( - f"GetMessage: called at {self.__GetTime()}ms") + self.__logger.info(f"GetMessage: called at {self.__GetTime()}ms") result = self.__logic.GetMessage() if result[0] == -1: - self.__logger.warning( - f"GetMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"GetMessage: failed at {self.__GetTime()}ms") return result # 等待下一帧 def Wait(self) -> bool: - self.__logger.info( - f"Wait: called at {self.__GetTime()}ms") + self.__logger.info(f"Wait: called at {self.__GetTime()}ms") if self.__logic.GetCounter() == -1: return False else: @@ -305,7 +298,13 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): return self.__logic.GetGameInfo() def HaveView(self, gridX: int, gridY: int) -> bool: - return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange) + return self.__logic.HaveView( + gridX, + gridY, + self.GetSelfInfo().x, + self.GetSelfInfo().y, + self.GetSelfInfo().viewRange, + ) # 用于DEBUG的输出函数,仅在DEBUG模式下有效 @@ -316,20 +315,26 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): for student in self.__logic.GetStudents(): self.__logger.info("******Student Info******") self.__logger.info( - f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}") + f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}" + ) self.__logger.info( - f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}") + f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}" + ) self.__logger.info( - f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}") + f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}" + ) studentProp = "" for prop in student.prop: studentProp += prop.name + ", " self.__logger.info( - f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}") + f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}" + ) self.__logger.info( - f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") + f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}" + ) self.__logger.info( - f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") + f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}" + ) studentBuff = "" for buff in student.buff: studentBuff += buff.name + ", " @@ -340,18 +345,23 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): for tricker in self.__logic.GetTrickers(): self.__logger.info("******Tricker Info******") self.__logger.info( - f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}") + f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}" + ) self.__logger.info( - f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}") + f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}" + ) self.__logger.info( - f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}") + f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}" + ) trickerProp = "" for prop in tricker.prop: trickerProp += prop.name + ", " self.__logger.info( - f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}") + f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}" + ) self.__logger.info( - f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}") + f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}" + ) trickerBuff = "" for buff in tricker.buff: trickerBuff += buff.name + ", " @@ -362,27 +372,34 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): for prop in self.__logic.GetProps(): self.__logger.info("******Prop Info******") self.__logger.info( - f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}") + f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}" + ) self.__logger.info("*********************") def PrintSelfInfo(self) -> None: student = cast(THUAI6.Student, self.__logic.GetSelfInfo()) self.__logger.info("******Student Info******") self.__logger.info( - f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}") + f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}" + ) self.__logger.info( - f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}") + f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}" + ) self.__logger.info( - f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}") + f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}" + ) studentProp = "" for prop in student.prop: studentProp += prop.name + ", " self.__logger.info( - f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}") + f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}" + ) self.__logger.info( - f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") + f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}" + ) self.__logger.info( - f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") + f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}" + ) studentBuff = "" for buff in student.buff: studentBuff += buff.name + ", " @@ -392,53 +409,47 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): # 人类阵营的特殊函数 def Graduate(self) -> Future[bool]: - self.__logger.info( - f"Graduate: called at {self.__GetTime()}ms") + self.__logger.info(f"Graduate: called at {self.__GetTime()}ms") def logGraduate() -> bool: result = self.__logic.Graduate() if not result: - self.__logger.warning( - f"Graduate: failed at {self.__GetTime()}ms") + self.__logger.warning(f"Graduate: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logGraduate) def StartLearning(self) -> Future[bool]: - self.__logger.info( - f"StartLearning: called at {self.__GetTime()}ms") + self.__logger.info(f"StartLearning: called at {self.__GetTime()}ms") def logStart() -> bool: result = self.__logic.StartLearning() if not result: - self.__logger.warning( - f"StartLearning: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartLearning: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStart) def StartEncourageMate(self, mateID: int) -> Future[bool]: - self.__logger.info( - f"StartEncourageMate: called at {self.__GetTime()}ms") + self.__logger.info(f"StartEncourageMate: called at {self.__GetTime()}ms") def logStartEncourageMate() -> bool: result = self.__logic.StartEncourageMate(mateID) if not result: self.__logger.warning( - f"StartEncourageMate: failed at {self.__GetTime()}ms") + f"StartEncourageMate: failed at {self.__GetTime()}ms" + ) return result return self.__pool.submit(logStartEncourageMate) def StartRouseMate(self, mateID: int) -> Future[bool]: - self.__logger.info( - f"StartRouseMate: called at {self.__GetTime()}ms") + self.__logger.info(f"StartRouseMate: called at {self.__GetTime()}ms") def logStartRouseMate() -> bool: result = self.__logic.StartRouseMate(mateID) if not result: - self.__logger.warning( - f"StartRouseMate: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartRouseMate: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStartRouseMate) @@ -449,7 +460,9 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): # Timer用 def __GetTime(self) -> float: - return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(milliseconds=1) + return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta( + milliseconds=1 + ) def StartTimer(self) -> None: self.__startPoint = datetime.datetime.now() @@ -464,21 +477,33 @@ class StudentDebugAPI(IStudentAPI, IGameTimer): class TrickerDebugAPI(ITrickerAPI, IGameTimer): - - def __init__(self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int) -> None: + def __init__( + self, logic: ILogic, file: bool, screen: bool, warnOnly: bool, playerID: int + ) -> None: self.__logic = logic self.__pool = ThreadPoolExecutor(20) self.__logger = logging.getLogger("api " + str(playerID)) self.__logger.setLevel(logging.DEBUG) formatter = logging.Formatter( - "[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S') + "[%(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") - - fileHandler = logging.FileHandler(os.path.dirname( - os.path.dirname(os.path.realpath(__file__))) + "/logs/api-" + str(playerID) + "-log.txt", mode="w+", encoding="utf-8") + 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" + ) + + fileHandler = logging.FileHandler( + os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + "/logs/api-" + + str(playerID) + + "-log.txt", + mode="w+", + encoding="utf-8", + ) screenHandler = logging.StreamHandler() if file: fileHandler.setLevel(logging.DEBUG) @@ -496,13 +521,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): def Move(self, timeInMilliseconds: int, angle: float) -> Future[bool]: self.__logger.info( - f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms") + f"Move: timeInMilliseconds = {timeInMilliseconds}, angle = {angle}, called at {self.__GetTime()}ms" + ) def logMove() -> bool: result = self.__logic.Move(timeInMilliseconds, angle) if not result: - self.__logger.warning( - f"Move: failed at {self.__GetTime()}ms") + self.__logger.warning(f"Move: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logMove) @@ -524,14 +549,12 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): # 道具和技能相关 def Attack(self, angle: float) -> Future[bool]: - self.__logger.info( - f"Attack: angle = {angle}, called at {self.__GetTime()}ms") + self.__logger.info(f"Attack: angle = {angle}, called at {self.__GetTime()}ms") def logAttack() -> bool: result = self.__logic.Attack(angle) if not result: - self.__logger.warning( - f"Attack: failed at {self.__GetTime()}ms") + self.__logger.warning(f"Attack: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logAttack) @@ -540,131 +563,119 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): def PickProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"PickProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logPick() -> bool: result = self.__logic.PickProp(propType) if not result: - self.__logger.warning( - f"PickProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"PickProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logPick) def UseProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"UseProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logUse() -> bool: result = self.__logic.UseProp(propType) if not result: - self.__logger.warning( - f"UseProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"UseProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logUse) def ThrowProp(self, propType: THUAI6.PropType) -> Future[bool]: self.__logger.info( - f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms") + f"ThrowProp: prop = {propType.name}, called at {self.__GetTime()}ms" + ) def logThrow() -> bool: result = self.__logic.ThrowProp(propType) if not result: - self.__logger.warning( - f"ThrowProp: failed at {self.__GetTime()}ms") + self.__logger.warning(f"ThrowProp: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logThrow) def UseSkill(self, skillID: int) -> Future[bool]: self.__logger.info( - f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms") + f"UseSkill: skillID = {skillID}, called at {self.__GetTime()}ms" + ) def logUse() -> bool: result = self.__logic.UseSkill(skillID) if not result: - self.__logger.warning( - f"UseSkill: failed at {self.__GetTime()}ms") + self.__logger.warning(f"UseSkill: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logUse) # 与地图交互相关 def OpenDoor(self) -> Future[bool]: - self.__logger.info( - f"OpenDoor: called at {self.__GetTime()}ms") + self.__logger.info(f"OpenDoor: called at {self.__GetTime()}ms") def logOpen() -> bool: result = self.__logic.OpenDoor() if not result: - self.__logger.warning( - f"OpenDoor: failed at {self.__GetTime()}ms") + self.__logger.warning(f"OpenDoor: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logOpen) def CloseDoor(self) -> Future[bool]: - self.__logger.info( - f"CloseDoor: called at {self.__GetTime()}ms") + self.__logger.info(f"CloseDoor: called at {self.__GetTime()}ms") def logClose() -> bool: result = self.__logic.CloseDoor() if not result: - self.__logger.warning( - f"CloseDoor: failed at {self.__GetTime()}ms") + self.__logger.warning(f"CloseDoor: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logClose) def SkipWindow(self) -> Future[bool]: - self.__logger.info( - f"SkipWindow: called at {self.__GetTime()}ms") + self.__logger.info(f"SkipWindow: called at {self.__GetTime()}ms") def logSkip() -> bool: result = self.__logic.SkipWindow() if not result: - self.__logger.warning( - f"SkipWindow: failed at {self.__GetTime()}ms") + self.__logger.warning(f"SkipWindow: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logSkip) def StartOpenGate(self) -> Future[bool]: - self.__logger.info( - f"StartOpenGate: called at {self.__GetTime()}ms") + self.__logger.info(f"StartOpenGate: called at {self.__GetTime()}ms") def logStart() -> bool: result = self.__logic.StartOpenGate() if not result: - self.__logger.warning( - f"StartOpenGate: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartOpenGate: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStart) def StartOpenChest(self) -> Future[bool]: - self.__logger.info( - f"StartOpenChest: called at {self.__GetTime()}ms") + self.__logger.info(f"StartOpenChest: called at {self.__GetTime()}ms") def logStart() -> bool: result = self.__logic.StartOpenChest() if not result: - self.__logger.warning( - f"StartOpenChest: failed at {self.__GetTime()}ms") + self.__logger.warning(f"StartOpenChest: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logStart) def EndAllAction(self) -> Future[bool]: - self.__logger.info( - f"EndAllAction: called at {self.__GetTime()}ms") + self.__logger.info(f"EndAllAction: called at {self.__GetTime()}ms") def logEnd() -> bool: result = self.__logic.EndAllAction() if not result: - self.__logger.warning( - f"EndAllAction: failed at {self.__GetTime()}ms") + self.__logger.warning(f"EndAllAction: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logEnd) @@ -673,40 +684,35 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): def SendMessage(self, toID: int, message: Union[str, bytes]) -> Future[bool]: self.__logger.info( - f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms") + f"SendMessage: toID = {toID}, message = {message}, called at {self.__GetTime()}ms" + ) def logSend() -> bool: result = self.__logic.SendMessage(toID, message) if not result: - self.__logger.warning( - f"SendMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"SendMessage: failed at {self.__GetTime()}ms") return result return self.__pool.submit(logSend) def HaveMessage(self) -> bool: - self.__logger.info( - f"HaveMessage: called at {self.__GetTime()}ms") + self.__logger.info(f"HaveMessage: called at {self.__GetTime()}ms") result = self.__logic.HaveMessage() if not result: - self.__logger.warning( - f"HaveMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"HaveMessage: failed at {self.__GetTime()}ms") return result def GetMessage(self) -> Tuple[int, Union[str, bytes]]: - self.__logger.info( - f"GetMessage: called at {self.__GetTime()}ms") + self.__logger.info(f"GetMessage: called at {self.__GetTime()}ms") result = self.__logic.GetMessage() if result[0] == -1: - self.__logger.warning( - f"GetMessage: failed at {self.__GetTime()}ms") + self.__logger.warning(f"GetMessage: failed at {self.__GetTime()}ms") return result # 等待下一帧 def Wait(self) -> bool: - self.__logger.info( - f"Wait: called at {self.__GetTime()}ms") + self.__logger.info(f"Wait: called at {self.__GetTime()}ms") if self.__logic.GetCounter() == -1: return False else: @@ -760,7 +766,13 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): return self.__logic.GetGameInfo() def HaveView(self, gridX: int, gridY: int) -> bool: - return self.__logic.HaveView(gridX, gridY, self.GetSelfInfo().x, self.GetSelfInfo().y, self.GetSelfInfo().viewRange) + return self.__logic.HaveView( + gridX, + gridY, + self.GetSelfInfo().x, + self.GetSelfInfo().y, + self.GetSelfInfo().viewRange, + ) # 用于DEBUG的输出函数,仅在DEBUG模式下有效 @@ -771,20 +783,26 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): for student in self.__logic.GetStudents(): self.__logger.info("******Student Info******") self.__logger.info( - f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}") + f"playerID={student.playerID}, GUID={student.guid}, x={student.x}, y={student.y}" + ) self.__logger.info( - f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}") + f"speed={student.speed}, view range={student.viewRange}, radius={student.radius}" + ) self.__logger.info( - f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}") + f"score={student.score}, facing direction={student.facingDirection}, skill time={student.timeUntilSkillAvailable}" + ) studentProp = "" for prop in student.prop: studentProp += prop.name + ", " self.__logger.info( - f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}") + f"state={student.playerState.name}, bullet={student.bulletType.name}, prop={studentProp}" + ) self.__logger.info( - f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}") + f"type={student.studentType.name}, determination={student.determination}, addiction={student.addiction}, danger alert={student.dangerAlert}" + ) self.__logger.info( - f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}") + f"learning speed={student.learningSpeed}, encourage speed={student.encourageSpeed}, encourage progress={student.encourageProgress}, rouse progress={student.rouseProgress}" + ) studentBuff = "" for buff in student.buff: studentBuff += buff.name + ", " @@ -795,18 +813,23 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): for tricker in self.__logic.GetTrickers(): self.__logger.info("******Tricker Info******") self.__logger.info( - f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}") + f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}" + ) self.__logger.info( - f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}") + f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}" + ) self.__logger.info( - f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}") + f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}" + ) trickerProp = "" for prop in tricker.prop: trickerProp += prop.name + ", " self.__logger.info( - f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}") + f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}" + ) self.__logger.info( - f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}") + f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}" + ) trickerBuff = "" for buff in tricker.buff: trickerBuff += buff.name + ", " @@ -817,25 +840,31 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): for prop in self.__logic.GetProps(): self.__logger.info("******Prop Info******") self.__logger.info( - f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}") + f"GUID={prop.guid}, x={prop.x}, y={prop.y}, facing direction={prop.facingDirection}" + ) self.__logger.info("*********************") def PrintSelfInfo(self) -> None: tricker = cast(THUAI6.Tricker, self.__logic.GetSelfInfo()) self.__logger.info("******Tricker Info******") self.__logger.info( - f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}") + f"playerID={tricker.playerID}, GUID={tricker.guid}, x={tricker.x}, y={tricker.y}" + ) self.__logger.info( - f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}") + f"speed={tricker.speed}, view range={tricker.viewRange}, radius={tricker.radius}" + ) self.__logger.info( - f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}") + f"score={tricker.score}, facing direction={tricker.facingDirection}, skill time={tricker.timeUntilSkillAvailable}" + ) trickerProp = "" for prop in tricker.prop: trickerProp += prop.name + ", " self.__logger.info( - f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}") + f"state={tricker.playerState.name}, bullet={tricker.bulletType.name}, prop={trickerProp}" + ) self.__logger.info( - f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}") + f"type={tricker.trickerType.name}, trick desire={tricker.trickDesire}, class volume={tricker.classVolume}" + ) trickerBuff = "" for buff in tricker.buff: trickerBuff += buff.name + ", " @@ -850,7 +879,9 @@ class TrickerDebugAPI(ITrickerAPI, IGameTimer): # Timer用 def __GetTime(self) -> float: - return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta(milliseconds=1) + return (datetime.datetime.now() - self.__startPoint) / datetime.timedelta( + milliseconds=1 + ) def StartTimer(self) -> None: self.__startPoint = datetime.datetime.now() diff --git a/CAPI/python/PyAPI/Interface.py b/CAPI/python/PyAPI/Interface.py index c408c61..43c2965 100644 --- a/CAPI/python/PyAPI/Interface.py +++ b/CAPI/python/PyAPI/Interface.py @@ -5,7 +5,6 @@ import PyAPI.structures as THUAI6 class ILogic(metaclass=ABCMeta): - # IAPI统一可用的接口 @abstractmethod @@ -155,12 +154,13 @@ class ILogic(metaclass=ABCMeta): pass @abstractmethod - def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool: + def HaveView( + self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int + ) -> bool: pass class IAPI(metaclass=ABCMeta): - # 选手可执行的操作 # 指挥本角色进行移动,`timeInMilliseconds` 为移动时间,单位为毫秒;`angleInRadian` 表示移动的方向,单位是弧度,使用极坐标——竖直向下方向为 x 轴,水平向右方向为 y 轴 @@ -346,7 +346,6 @@ class IAPI(metaclass=ABCMeta): class IStudentAPI(IAPI, metaclass=ABCMeta): - # 人类阵营的特殊函数 @abstractmethod @@ -371,7 +370,6 @@ class IStudentAPI(IAPI, metaclass=ABCMeta): class ITrickerAPI(IAPI, metaclass=ABCMeta): - # 屠夫阵营的特殊函数 @abstractmethod @@ -390,7 +388,6 @@ class IAI(metaclass=ABCMeta): class IGameTimer(metaclass=ABCMeta): - # 用于计时的接口 @abstractmethod diff --git a/CAPI/python/PyAPI/State.py b/CAPI/python/PyAPI/State.py index d493617..f29b6cf 100644 --- a/CAPI/python/PyAPI/State.py +++ b/CAPI/python/PyAPI/State.py @@ -15,6 +15,7 @@ class State: self.mapInfo = THUAI6.GameMap() self.gameInfo = THUAI6.GameInfo() self.guids = [] + teamScore: int self: Union[THUAI6.Student, THUAI6.Tricker] diff --git a/CAPI/python/PyAPI/constants.py b/CAPI/python/PyAPI/constants.py index de7d171..cdd92cd 100644 --- a/CAPI/python/PyAPI/constants.py +++ b/CAPI/python/PyAPI/constants.py @@ -10,18 +10,18 @@ class NoInstance: class Constants(NoInstance): frameDuration = 50 # 每帧毫秒数 numOfGridPerCell = 1000 # 单位坐标数 - rows = 50 # 地图行数 - cols = 50 # 地图列数 + rows = 50 # 地图行数 + cols = 50 # 地图列数 numOfClassroom = 10 # 教室数量 - numOfChest = 8 # 宝箱数量 + numOfChest = 8 # 宝箱数量 maxClassroomProgress = 10000000 # 教室最大进度 - maxDoorProgress = 10000000 # 开关门最大进度 - maxChestProgress = 10000000 # 宝箱最大进度 - maxGateProgress = 18000 # 大门最大进度 + maxDoorProgress = 10000000 # 开关门最大进度 + maxChestProgress = 10000000 # 宝箱最大进度 + maxGateProgress = 18000 # 大门最大进度 - numOfRequiredClassroomForGate = 7 # 打开大门需要完成的教室数量 + numOfRequiredClassroomForGate = 7 # 打开大门需要完成的教室数量 numOfRequiredClassroomForHiddenGate = 3 # 打开隐藏门需要完成的教室数量 # 人物属性相关 @@ -52,16 +52,16 @@ class Constants(NoInstance): # 攻击相关 basicApOfTricker = 1500000 - basicCD = 3000 # 初始子弹冷却 - basicCastTime = 500 # 基本前摇时间 - basicBackswing = 800 # 基本后摇时间 + basicCD = 3000 # 初始子弹冷却 + basicCastTime = 500 # 基本前摇时间 + basicBackswing = 800 # 基本后摇时间 basicRecoveryFromHit = 3700 # 基本命中攻击恢复时长 basicStunnedTimeOfStudent = 4300 - basicBulletMoveSpeed = 7400 # 基本子弹移动速度 + basicBulletMoveSpeed = 7400 # 基本子弹移动速度 basicRemoteAttackRange = 6000 # 基本远程攻击范围 - basicAttackShortRange = 2200 # 基本近程攻击范围 - basicBulletBombRange = 2000 # 基本子弹爆炸范围 + basicAttackShortRange = 2200 # 基本近程攻击范围 + basicBulletBombRange = 2000 # 基本子弹爆炸范围 # 道具相关 @@ -96,10 +96,10 @@ class Assassin: concealment = 1.5 * Constants.basicConcealment alertnessRadius = (int)(1.3 * Constants.basicTrickerAlertnessRadius) viewRange = (int)(1.2 * Constants.basicTrickerViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) + 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -108,10 +108,10 @@ class Klee: concealment = 1.0 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) viewRange = (int)(1.0 * Constants.basicTrickerViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) + 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) @@ -120,10 +120,10 @@ class ANoisyPerson: concealment = 0.8 * Constants.basicConcealment alertnessRadius = (int)(0.9 * Constants.basicTrickerAlertnessRadius) viewRange = (int)(1.0 * Constants.basicTrickerViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.1 * Constants.basicTrickerSpeedOfClimbingThroughWindows) + 1.1 * Constants.basicTrickerSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.1 * Constants.basicSpeedOfOpenChest) @@ -132,10 +132,10 @@ class Idol: concealment = 0.75 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicTrickerAlertnessRadius) viewRange = (int)(1.1 * Constants.basicTrickerViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows) + 1.0 * Constants.basicTrickerSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -148,10 +148,10 @@ class Athlete: concealment = 0.9 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) viewRange = (int)(1.1 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.2 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 1.2 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -164,10 +164,10 @@ class Teacher: concealment = 0.5 * Constants.basicConcealment alertnessRadius = (int)(0.5 * Constants.basicStudentAlertnessRadius) viewRange = (int)(0.9 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 0.5 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 0.5 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -180,10 +180,10 @@ class StraightAStudent: concealment = 0.9 * Constants.basicConcealment alertnessRadius = (int)(0.9 * Constants.basicStudentAlertnessRadius) viewRange = (int)(0.9 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 0.83333 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 0.83333 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -196,10 +196,10 @@ class Robot: concealment = 1.0 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) viewRange = (int)(1.0 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 0.0016 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 0.0016 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -212,10 +212,10 @@ class TechOtaku: concealment = 1.0 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) viewRange = (int)(0.9 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 1.0 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(1.0 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 0.75 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 0.75 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(1.0 * Constants.basicSpeedOfOpenChest) @@ -228,10 +228,10 @@ class Sunshine: concealment = 1.0 * Constants.basicConcealment alertnessRadius = (int)(1.0 * Constants.basicStudentAlertnessRadius) viewRange = (int)(1.0 * Constants.basicStudentViewRange) - speedOfOpeningOrLocking = (int)( - 0.7 * Constants.basicSpeedOfOpeningOrLocking) + speedOfOpeningOrLocking = (int)(0.7 * Constants.basicSpeedOfOpeningOrLocking) speedOfClimbingThroughWindows = (int)( - 1.0 * Constants.basicStudentSpeedOfClimbingThroughWindows) + 1.0 * Constants.basicStudentSpeedOfClimbingThroughWindows + ) speedOfOpenChest = (int)(0.9 * Constants.basicSpeedOfOpenChest) @@ -299,6 +299,7 @@ class SummonGolem: skillCD = (int)(1.0 * Constants.commonSkillCD) durationTime = (int)(0.0 * Constants.commonSkillTime) + class CommonAttackOfTricker: BulletBombRange = 0 BulletAttackRange = Constants.basicAttackShortRange @@ -310,34 +311,37 @@ class CommonAttackOfTricker: RecoveryFromHit = Constants.basicRecoveryFromHit cd = Constants.basicBackswing maxBulletNum = 1 - + + class FlyingKnife: BulletBombRange = 0 BulletAttackRange = Constants.basicRemoteAttackRange * 13 - ap = Constants.basicApOfTricker* 4 / 5 - Speed = Constants.basicBulletMoveSpeed* 25 / 10 + ap = Constants.basicApOfTricker * 4 / 5 + Speed = Constants.basicBulletMoveSpeed * 25 / 10 IsRemoteAttack = True CastTime = Constants.basicCastTime * 4 / 5 - Backswing =0 - RecoveryFromHit =0 + Backswing = 0 + RecoveryFromHit = 0 cd = Constants.basicBackswing / 2 maxBulletNum = 1 - + + class BombBomb: BulletBombRange = Constants.basicBulletBombRange BulletAttackRange = Constants.basicAttackShortRange ap = Constants.basicApOfTricker * 6 / 5 - Speed = Constants.basicBulletMoveSpeed* 30 / 37 + Speed = Constants.basicBulletMoveSpeed * 30 / 37 IsRemoteAttack = False CastTime = BulletAttackRange * 1000 / Speed - Backswing =Constants.basicRecoveryFromHit - RecoveryFromHit =Constants.basicRecoveryFromHit + Backswing = Constants.basicRecoveryFromHit + RecoveryFromHit = Constants.basicRecoveryFromHit cd = Constants.basicCD maxBulletNum = 1 - + + class JumpyDumpty: BulletBombRange = Constants.basicBulletBombRange / 2 BulletAttackRange = Constants.basicRemoteAttackRange * 2 - ap = (int)(Constants.basicApOfTricker* 0.6) - Speed = Constants.basicBulletMoveSpeed* 43 / 37 + ap = (int)(Constants.basicApOfTricker * 0.6) + Speed = Constants.basicBulletMoveSpeed * 43 / 37 IsRemoteAttack = False diff --git a/CAPI/python/PyAPI/logic.py b/CAPI/python/PyAPI/logic.py index 5f034ab..128cfdb 100644 --- a/CAPI/python/PyAPI/logic.py +++ b/CAPI/python/PyAPI/logic.py @@ -20,7 +20,6 @@ from PyAPI.Interface import ILogic, IGameTimer class Logic(ILogic): def __init__(self, playerID: int, playerType: THUAI6.PlayerType) -> None: - # ID self.__playerID: int = playerID @@ -103,7 +102,12 @@ class Logic(ILogic): def GetPlaceType(self, x: int, y: int) -> THUAI6.PlaceType: with self.__mtxState: - if x < 0 or x >= len(self.__currentState.gameMap) or y < 0 or y >= len(self.__currentState.gameMap[0]): + if ( + x < 0 + or x >= len(self.__currentState.gameMap) + or y < 0 + or y >= len(self.__currentState.gameMap[0]) + ): self.__logger.warning("Invalid position") return THUAI6.PlaceType.NullPlaceType self.__logger.debug("Called GetPlaceType") @@ -149,7 +153,9 @@ class Logic(ILogic): with self.__mtxState: self.__logger.debug("Called GetHiddenGateState") if (x, y) in self.__currentState.mapInfo.hiddenGateState: - return copy.deepcopy(self.__currentState.mapInfo.hiddenGateState[(x, y)]) + return copy.deepcopy( + self.__currentState.mapInfo.hiddenGateState[(x, y)] + ) else: self.__logger.warning("HiddenGate not found") return THUAI6.HiddenGateState.Null @@ -262,9 +268,13 @@ class Logic(ILogic): self.__logger.debug("Called EndAllAction") return self.__comm.EndAllAction(self.__playerID) - def HaveView(self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int) -> bool: + def HaveView( + self, gridX: int, gridY: int, selfX: int, selfY: int, viewRange: int + ) -> bool: with self.__mtxState: - return AssistFunction.HaveView(viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap) + return AssistFunction.HaveView( + viewRange, selfX, selfY, gridX, gridY, self.__currentState.gameMap + ) # Logic内部逻辑 def __TryConnection(self) -> bool: @@ -281,8 +291,7 @@ class Logic(ILogic): # 读取消息,无消息时此处阻塞 clientMsg = self.__comm.GetMessage2Client() self.__logger.debug("Get message from server!") - self.__gameState = Proto2THUAI6.gameStateDict[ - clientMsg.game_state] + self.__gameState = Proto2THUAI6.gameStateDict[clientMsg.game_state] if self.__gameState == THUAI6.GameState.GameStart: # 读取玩家的GUID @@ -294,8 +303,7 @@ class Logic(ILogic): for row in obj.map_message.row: col: List[THUAI6.PlaceType] = [] for place in row.col: - col.append( - Proto2THUAI6.placeTypeDict[place]) + col.append(Proto2THUAI6.placeTypeDict[place]) gameMap.append(col) self.__currentState.gameMap = gameMap self.__bufferState.gameMap = gameMap @@ -330,127 +338,238 @@ class Logic(ILogic): if item.WhichOneof("message_of_obj") == "student_message": if item.student_message.player_id == self.__playerID: self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Student( - item.student_message) - self.__bufferState.students.append( - self.__bufferState.self) + item.student_message + ) + self.__bufferState.students.append(self.__bufferState.self) else: self.__bufferState.students.append( - Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) + Proto2THUAI6.Protobuf2THUAI6Student(item.student_message) + ) self.__logger.debug("Add Student!") else: for item in message.obj_message: if item.WhichOneof("message_of_obj") == "tricker_message": if item.tricker_message.player_id == self.__playerID: self.__bufferState.self = Proto2THUAI6.Protobuf2THUAI6Tricker( - item.tricker_message) - self.__bufferState.trickers.append( - self.__bufferState.self) + item.tricker_message + ) + self.__bufferState.trickers.append(self.__bufferState.self) else: self.__bufferState.trickers.append( - Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)) + Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message) + ) self.__logger.debug("Add Tricker!") def __LoadBufferCase(self, item: Message2Clients.MessageOfObj) -> None: - if self.__playerType == THUAI6.PlayerType.StudentPlayer and item.WhichOneof("message_of_obj") == "tricker_message": + if ( + self.__playerType == THUAI6.PlayerType.StudentPlayer + and item.WhichOneof("message_of_obj") == "tricker_message" + ): if MessageType.TRICKER_INVISIBLE in item.tricker_message.buff: return - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.tricker_message.x, item.tricker_message.y, self.__bufferState.gameMap): + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.tricker_message.x, + item.tricker_message.y, + self.__bufferState.gameMap, + ): self.__bufferState.trickers.append( - Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message)) + Proto2THUAI6.Protobuf2THUAI6Tricker(item.tricker_message) + ) self.__logger.debug("Add Tricker!") - elif self.__playerType == THUAI6.PlayerType.TrickerPlayer and item.WhichOneof("message_of_obj") == "student_message": + elif ( + self.__playerType == THUAI6.PlayerType.TrickerPlayer + and item.WhichOneof("message_of_obj") == "student_message" + ): if THUAI6.TrickerBuffType.Clairaudience in self.__bufferState.self.buff: self.__bufferState.students.append( - Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) + Proto2THUAI6.Protobuf2THUAI6Student(item.student_message) + ) self.__logger.debug("Add Student!") return if MessageType.STUDENT_INVISIBLE in item.student_message.buff: return - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.student_message.x, item.student_message.y, self.__bufferState.gameMap): + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.student_message.x, + item.student_message.y, + self.__bufferState.gameMap, + ): self.__bufferState.students.append( - Proto2THUAI6.Protobuf2THUAI6Student(item.student_message)) + Proto2THUAI6.Protobuf2THUAI6Student(item.student_message) + ) self.__logger.debug("Add Student!") elif item.WhichOneof("message_of_obj") == "prop_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.prop_message.x, item.prop_message.y, self.__bufferState.gameMap): + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.prop_message.x, + item.prop_message.y, + self.__bufferState.gameMap, + ): self.__bufferState.props.append( - Proto2THUAI6.Protobuf2THUAI6Prop(item.prop_message)) + Proto2THUAI6.Protobuf2THUAI6Prop(item.prop_message) + ) self.__logger.debug("Add Prop!") elif item.WhichOneof("message_of_obj") == "bullet_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.bullet_message.x, item.bullet_message.y, self.__bufferState.gameMap): + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.bullet_message.x, + item.bullet_message.y, + self.__bufferState.gameMap, + ): self.__bufferState.bullets.append( - Proto2THUAI6.Protobuf2THUAI6Bullet(item.bullet_message)) + Proto2THUAI6.Protobuf2THUAI6Bullet(item.bullet_message) + ) self.__logger.debug("Add Bullet!") elif item.WhichOneof("message_of_obj") == "classroom_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.classroom_message.x, item.classroom_message.y, self.__bufferState.gameMap): - pos = (AssistFunction.GridToCell( - item.classroom_message.x), AssistFunction.GridToCell(item.classroom_message.y)) + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.classroom_message.x, + item.classroom_message.y, + self.__bufferState.gameMap, + ): + pos = ( + AssistFunction.GridToCell(item.classroom_message.x), + AssistFunction.GridToCell(item.classroom_message.y), + ) if pos not in self.__bufferState.mapInfo.classroomState: - self.__bufferState.mapInfo.classroomState[pos] = item.classroom_message.progress + self.__bufferState.mapInfo.classroomState[ + pos + ] = item.classroom_message.progress self.__logger.debug("Add Classroom!") else: - self.__bufferState.mapInfo.classroomState[pos] = item.classroom_message.progress + self.__bufferState.mapInfo.classroomState[ + pos + ] = item.classroom_message.progress self.__logger.debug("Update Classroom!") elif item.WhichOneof("message_of_obj") == "chest_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.chest_message.x, item.chest_message.y, self.__bufferState.gameMap): - pos = (AssistFunction.GridToCell( - item.chest_message.x), AssistFunction.GridToCell(item.chest_message.y)) + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.chest_message.x, + item.chest_message.y, + self.__bufferState.gameMap, + ): + pos = ( + AssistFunction.GridToCell(item.chest_message.x), + AssistFunction.GridToCell(item.chest_message.y), + ) if pos not in self.__bufferState.mapInfo.chestState: - self.__bufferState.mapInfo.chestState[pos] = item.chest_message.progress - self.__logger.debug( - f"Add Chest at {pos[0]}, {pos[1]}") + self.__bufferState.mapInfo.chestState[ + pos + ] = item.chest_message.progress + self.__logger.debug(f"Add Chest at {pos[0]}, {pos[1]}") else: - self.__bufferState.mapInfo.chestState[pos] = item.chest_message.progress - self.__logger.debug( - f"Update Chest at {pos[0]}, {pos[1]}") + self.__bufferState.mapInfo.chestState[ + pos + ] = item.chest_message.progress + self.__logger.debug(f"Update Chest at {pos[0]}, {pos[1]}") elif item.WhichOneof("message_of_obj") == "door_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.door_message.x, item.door_message.y, self.__bufferState.gameMap): - pos = (AssistFunction.GridToCell( - item.door_message.x), AssistFunction.GridToCell(item.door_message.y)) + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.door_message.x, + item.door_message.y, + self.__bufferState.gameMap, + ): + pos = ( + AssistFunction.GridToCell(item.door_message.x), + AssistFunction.GridToCell(item.door_message.y), + ) if pos not in self.__bufferState.mapInfo.doorState: - self.__bufferState.mapInfo.doorState[pos] = item.door_message.is_open - self.__bufferState.mapInfo.doorProgress[pos] = item.door_message.progress + self.__bufferState.mapInfo.doorState[ + pos + ] = item.door_message.is_open + self.__bufferState.mapInfo.doorProgress[ + pos + ] = item.door_message.progress self.__logger.debug("Add Door!") else: - self.__bufferState.mapInfo.doorState[pos] = item.door_message.is_open - self.__bufferState.mapInfo.doorProgress[pos] = item.door_message.progress + self.__bufferState.mapInfo.doorState[ + pos + ] = item.door_message.is_open + self.__bufferState.mapInfo.doorProgress[ + pos + ] = item.door_message.progress self.__logger.debug("Update Door!") elif item.WhichOneof("message_of_obj") == "hidden_gate_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.hidden_gate_message.x, item.hidden_gate_message.y, self.__bufferState.gameMap): - pos = (AssistFunction.GridToCell( - item.hidden_gate_message.x), AssistFunction.GridToCell(item.hidden_gate_message.y)) + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.hidden_gate_message.x, + item.hidden_gate_message.y, + self.__bufferState.gameMap, + ): + pos = ( + AssistFunction.GridToCell(item.hidden_gate_message.x), + AssistFunction.GridToCell(item.hidden_gate_message.y), + ) if pos not in self.__bufferState.mapInfo.hiddenGateState: - self.__bufferState.mapInfo.hiddenGateState[pos] = Proto2THUAI6.Bool2HiddenGateState( - item.hidden_gate_message.opened) + self.__bufferState.mapInfo.hiddenGateState[ + pos + ] = Proto2THUAI6.Bool2HiddenGateState( + item.hidden_gate_message.opened + ) self.__logger.debug("Add HiddenGate!") else: - self.__bufferState.mapInfo.hiddenGateState[pos] = Proto2THUAI6.Bool2HiddenGateState( - item.hidden_gate_message.opened) + self.__bufferState.mapInfo.hiddenGateState[ + pos + ] = Proto2THUAI6.Bool2HiddenGateState( + item.hidden_gate_message.opened + ) self.__logger.debug("Update HiddenGate!") elif item.WhichOneof("message_of_obj") == "gate_message": - if AssistFunction.HaveView(self.__bufferState.self.viewRange, self.__bufferState.self.x, self.__bufferState.self.y, item.gate_message.x, item.gate_message.y, self.__bufferState.gameMap): - pos = (AssistFunction.GridToCell( - item.gate_message.x), AssistFunction.GridToCell(item.gate_message.y)) + if AssistFunction.HaveView( + self.__bufferState.self.viewRange, + self.__bufferState.self.x, + self.__bufferState.self.y, + item.gate_message.x, + item.gate_message.y, + self.__bufferState.gameMap, + ): + pos = ( + AssistFunction.GridToCell(item.gate_message.x), + AssistFunction.GridToCell(item.gate_message.y), + ) if pos not in self.__bufferState.mapInfo.gateState: - self.__bufferState.mapInfo.gateState[pos] = item.gate_message.progress + self.__bufferState.mapInfo.gateState[ + pos + ] = item.gate_message.progress self.__logger.debug("Add Gate!") else: - self.__bufferState.mapInfo.gateState[pos] = item.gate_message.progress + self.__bufferState.mapInfo.gateState[ + pos + ] = item.gate_message.progress self.__logger.debug("Update Gate!") elif item.WhichOneof("message_of_obj") == "news_message": if item.news_message.to_id == self.__playerID: if item.news_message.WhichOneof("news") == "text_message": self.__messageQueue.put( - (item.news_message.from_id, item.news_message.text_message)) + (item.news_message.from_id, item.news_message.text_message) + ) self.__logger.debug("Add News!") elif item.news_message.WhichOneof("news") == "binary_message": self.__messageQueue.put( - (item.news_message.from_id, item.news_message.binary_message)) + (item.news_message.from_id, item.news_message.binary_message) + ) self.__logger.debug("Add News!") else: self.__logger.error("Unknown News!") else: - self.__logger.debug( - "Unknown Message!") + self.__logger.debug("Unknown Message!") def __LoadBuffer(self, message: Message2Clients.MessageToClient) -> None: with self.__cvBuffer: @@ -470,14 +589,18 @@ class Logic(ILogic): self.__bufferState.guids.append(obj.tricker_message.guid) self.__bufferState.gameInfo = Proto2THUAI6.Protobuf2THUAI6GameInfo( - message.all_message) + message.all_message + ) self.__LoadBufferSelf(message) for item in message.obj_message: self.__LoadBufferCase(item) if Setting.asynchronous(): with self.__mtxState: - self.__currentState, self.__bufferState = self.__bufferState, self.__currentState + self.__currentState, self.__bufferState = ( + self.__bufferState, + self.__currentState, + ) self.__counterState = self.__counterBuffer self.__logger.info("Update state!") self.__freshed = True @@ -496,7 +619,10 @@ class Logic(ILogic): with self.__cvBuffer: self.__cvBuffer.wait_for(lambda: self.__bufferUpdated) with self.__mtxState: - self.__bufferState, self.__currentState = self.__currentState, self.__bufferState + self.__bufferState, self.__currentState = ( + self.__currentState, + self.__bufferState, + ) self.__counterState = self.__counterBuffer self.__bufferUpdated = False self.__logger.info("Update state!") @@ -506,12 +632,21 @@ class Logic(ILogic): with self.__cvBuffer: self.__cvBuffer.wait_for(lambda: self.__freshed) - def Main(self, createAI: Callable, IP: str, port: str, file: bool, screen: bool, warnOnly: bool) -> None: - + def Main( + self, + createAI: Callable, + IP: str, + port: str, + file: bool, + screen: bool, + warnOnly: bool, + ) -> None: # 建立日志组件 self.__logger.setLevel(logging.DEBUG) formatter = logging.Formatter( - "[%(name)s] [%(asctime)s.%(msecs)03d] [%(levelname)s] %(message)s", '%H:%M:%S') + "[%(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( @@ -519,13 +654,21 @@ class Logic(ILogic): if platform.system().lower() == "windows": os.system( - f"mkdir \"{os.path.dirname(os.path.dirname(os.path.realpath(__file__)))}\\logs\"") + 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") + 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", + ) screenHandler = logging.StreamHandler() if file: fileHandler.setLevel(logging.DEBUG) @@ -555,13 +698,15 @@ class Logic(ILogic): self.__timer = StudentAPI(self) else: self.__timer = StudentDebugAPI( - self, file, screen, warnOnly, self.__playerID) + self, file, screen, warnOnly, self.__playerID + ) elif self.__playerType == THUAI6.PlayerType.TrickerPlayer: if not file and not screen: self.__timer = TrickerAPI(self) else: self.__timer = TrickerDebugAPI( - self, file, screen, warnOnly, self.__playerID) + self, file, screen, warnOnly, self.__playerID + ) # 构建AI线程 def AIThread(): @@ -583,7 +728,8 @@ class Logic(ILogic): if self.__TryConnection(): self.__logger.info( - "Connect to the server successfully, AI thread will be started.") + "Connect to the server successfully, AI thread will be started." + ) self.__threadAI = threading.Thread(target=AIThread) self.__threadAI.start() self.__ProcessMessage() diff --git a/CAPI/python/PyAPI/main.py b/CAPI/python/PyAPI/main.py index 621dc55..b351ffe 100644 --- a/CAPI/python/PyAPI/main.py +++ b/CAPI/python/PyAPI/main.py @@ -1,8 +1,8 @@ import os import sys + sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) -sys.path.append(os.path.dirname(os.path.dirname( - os.path.realpath(__file__))) + '/proto') +sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + "/proto") from PyAPI.Interface import IAI from PyAPI.AI import AI @@ -42,19 +42,50 @@ def THUAI6Main(argv: List[str], AIBuilder: Callable) -> None: screen: bool = True warnOnly: bool = False parser = argparse.ArgumentParser( - description="THUAI6 Python Interface Commandline Parameter Introduction") - parser.add_argument("-I", type=str, required=True, - help="Server`s IP 127.0.0.1 in default", dest="sIP", default="127.0.0.1") - parser.add_argument("-P", type=str, required=True, - help="Server`s Port 8888 in default", dest="sPort", default="8888") - parser.add_argument("-p", type=int, required=True, - help="Player`s ID", dest="pID", choices=[0, 1, 2, 3, 4]) - parser.add_argument("-d", action='store_true', - help="Set this flag to save the debug log to ./logs folder", dest="file") - parser.add_argument("-o", action='store_true', - help="Set this flag to print the debug log to the screen", dest="screen") - parser.add_argument("-w", action='store_true', - help="Set this flag to only print warning on the screen", dest="warnOnly") + description="THUAI6 Python Interface Commandline Parameter Introduction" + ) + parser.add_argument( + "-I", + type=str, + required=True, + help="Server`s IP 127.0.0.1 in default", + dest="sIP", + default="127.0.0.1", + ) + parser.add_argument( + "-P", + type=str, + required=True, + help="Server`s Port 8888 in default", + dest="sPort", + default="8888", + ) + parser.add_argument( + "-p", + type=int, + required=True, + help="Player`s ID", + dest="pID", + choices=[0, 1, 2, 3, 4], + ) + parser.add_argument( + "-d", + action="store_true", + help="Set this flag to save the debug log to ./logs folder", + dest="file", + ) + parser.add_argument( + "-o", + action="store_true", + help="Set this flag to print the debug log to the screen", + dest="screen", + ) + parser.add_argument( + "-w", + action="store_true", + help="Set this flag to only print warning on the screen", + dest="warnOnly", + ) args = parser.parse_args() pID = args.pID sIP = args.sIP @@ -79,5 +110,5 @@ def CreateAI(pID: int) -> IAI: return AI(pID) -if __name__ == '__main__': +if __name__ == "__main__": THUAI6Main(sys.argv, CreateAI) diff --git a/CAPI/python/PyAPI/utils.py b/CAPI/python/PyAPI/utils.py index 15ad8a3..1b05b2e 100644 --- a/CAPI/python/PyAPI/utils.py +++ b/CAPI/python/PyAPI/utils.py @@ -15,7 +15,6 @@ class NoInstance: class AssistFunction(NoInstance): - @staticmethod def CellToGrid(cell: int) -> int: return cell * numOfGridPerCell + numOfGridPerCell // 2 @@ -25,14 +24,19 @@ class AssistFunction(NoInstance): return grid // numOfGridPerCell @staticmethod - def HaveView(viewRange: int, x: int, y: int, newX: int, newY: int, map: List[List[THUAI6.PlaceType]]) -> bool: + def HaveView( + viewRange: int, + x: int, + y: int, + newX: int, + newY: int, + map: List[List[THUAI6.PlaceType]], + ) -> bool: deltaX: int = newX - x deltaY: int = newY - y distance: float = deltaX**2 + deltaY**2 - myPlace = map[AssistFunction.GridToCell( - x)][AssistFunction.GridToCell(y)] - newPlace = map[AssistFunction.GridToCell( - newX)][AssistFunction.GridToCell(newY)] + myPlace = map[AssistFunction.GridToCell(x)][AssistFunction.GridToCell(y)] + newPlace = map[AssistFunction.GridToCell(newX)][AssistFunction.GridToCell(newY)] if myPlace != THUAI6.PlaceType.Grass and newPlace == THUAI6.PlaceType.Grass: return False if distance <= viewRange * viewRange: @@ -47,7 +51,12 @@ class AssistFunction(NoInstance): for i in range(divide): selfX += dx selfY += dy - if map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))] != THUAI6.PlaceType.Grass: + if ( + map[AssistFunction.GridToCell(int(selfX))][ + AssistFunction.GridToCell(int(selfY)) + ] + != THUAI6.PlaceType.Grass + ): return False else: return True @@ -55,7 +64,12 @@ class AssistFunction(NoInstance): for i in range(divide): selfX += dx selfY += dy - if map[AssistFunction.GridToCell(int(selfX))][AssistFunction.GridToCell(int(selfY))] == THUAI6.PlaceType.Wall: + if ( + map[AssistFunction.GridToCell(int(selfX))][ + AssistFunction.GridToCell(int(selfY)) + ] + == THUAI6.PlaceType.Wall + ): return False else: return True @@ -76,12 +90,14 @@ class Proto2THUAI6(NoInstance): MessageType.DOOR3: THUAI6.PlaceType.Door3, MessageType.DOOR5: THUAI6.PlaceType.Door5, MessageType.DOOR6: THUAI6.PlaceType.Door6, - MessageType.CHEST: THUAI6.PlaceType.Chest, } + MessageType.CHEST: THUAI6.PlaceType.Chest, + } shapeTypeDict: Final[dict] = { MessageType.NULL_SHAPE_TYPE: THUAI6.ShapeType.NullShapeType, MessageType.SQUARE: THUAI6.ShapeType.Square, - MessageType.CIRCLE: THUAI6.ShapeType.Circle} + MessageType.CIRCLE: THUAI6.ShapeType.Circle, + } propTypeDict: Final[dict] = { MessageType.NULL_PROP_TYPE: THUAI6.PropType.NullPropType, @@ -92,12 +108,14 @@ class Proto2THUAI6(NoInstance): MessageType.ADD_HP_OR_AP: THUAI6.PropType.AddHpOrAp, MessageType.ADD_LIFE_OR_CLAIRAUDIENCE: THUAI6.PropType.AddLifeOrClairaudience, MessageType.SHIELD_OR_SPEAR: THUAI6.PropType.ShieldOrSpear, - MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness, } + MessageType.RECOVERY_FROM_DIZZINESS: THUAI6.PropType.RecoveryFromDizziness, + } playerTypeDict: Final[dict] = { MessageType.NULL_PLAYER_TYPE: THUAI6.PlayerType.NullPlayerType, MessageType.STUDENT_PLAYER: THUAI6.PlayerType.StudentPlayer, - MessageType.TRICKER_PLAYER: THUAI6.PlayerType.TrickerPlayer, } + MessageType.TRICKER_PLAYER: THUAI6.PlayerType.TrickerPlayer, + } studentTypeDict: Final[dict] = { MessageType.NULL_STUDENT_TYPE: THUAI6.StudentType.NullStudentType, @@ -106,21 +124,24 @@ class Proto2THUAI6(NoInstance): MessageType.STRAIGHT_A_STUDENT: THUAI6.StudentType.StraightAStudent, MessageType.ROBOT: THUAI6.StudentType.Robot, MessageType.TECH_OTAKU: THUAI6.StudentType.TechOtaku, - MessageType.SUNSHINE: THUAI6.StudentType.Sunshine, } + MessageType.SUNSHINE: THUAI6.StudentType.Sunshine, + } trickerTypeDict: Final[dict] = { MessageType.NULL_TRICKER_TYPE: THUAI6.TrickerType.NullTrickerType, MessageType.ASSASSIN: THUAI6.TrickerType.Assassin, MessageType.KLEE: THUAI6.TrickerType.Klee, MessageType.A_NOISY_PERSON: THUAI6.TrickerType.ANoisyPerson, - MessageType.IDOL: THUAI6.TrickerType.Idol, } + MessageType.IDOL: THUAI6.TrickerType.Idol, + } studentBuffTypeDict: Final[dict] = { MessageType.NULL_SBUFF_TYPE: THUAI6.StudentBuffType.NullStudentBuffType, MessageType.STUDENT_ADD_SPEED: THUAI6.StudentBuffType.AddSpeed, MessageType.ADD_LIFE: THUAI6.StudentBuffType.AddLife, MessageType.SHIELD: THUAI6.StudentBuffType.Shield, - MessageType.STUDENT_INVISIBLE: THUAI6.StudentBuffType.Invisible, } + MessageType.STUDENT_INVISIBLE: THUAI6.StudentBuffType.Invisible, + } trickerBuffTypeDict: Final[dict] = { MessageType.NULL_TBUFF_TYPE: THUAI6.TrickerBuffType.NullTrickerBuffType, @@ -128,7 +149,8 @@ class Proto2THUAI6(NoInstance): MessageType.SPEAR: THUAI6.TrickerBuffType.Spear, MessageType.ADD_AP: THUAI6.TrickerBuffType.AddAp, MessageType.CLAIRAUDIENCE: THUAI6.TrickerBuffType.Clairaudience, - MessageType.TRICKER_INVISIBLE: THUAI6.TrickerBuffType.Invisible, } + MessageType.TRICKER_INVISIBLE: THUAI6.TrickerBuffType.Invisible, + } playerStateDict: Final[dict] = { MessageType.NULL_STATUS: THUAI6.PlayerState.NullState, @@ -149,13 +171,15 @@ class Proto2THUAI6(NoInstance): MessageType.CLIMBING: THUAI6.PlayerState.Climbing, MessageType.OPENING_A_CHEST: THUAI6.PlayerState.OpeningAChest, MessageType.USING_SPECIAL_SKILL: THUAI6.PlayerState.UsingSpecialSkill, - MessageType.OPENING_A_GATE: THUAI6.PlayerState.OpeningAGate, } + MessageType.OPENING_A_GATE: THUAI6.PlayerState.OpeningAGate, + } gameStateDict: Final[dict] = { MessageType.NULL_GAME_STATE: THUAI6.GameState.NullGameState, MessageType.GAME_START: THUAI6.GameState.GameStart, MessageType.GAME_RUNNING: THUAI6.GameState.GameRunning, - MessageType.GAME_END: THUAI6.GameState.GameEnd} + MessageType.GAME_END: THUAI6.GameState.GameEnd, + } bulletTypeDict: Final[dict] = { MessageType.NULL_BULLET_TYPE: THUAI6.BulletType.NullBulletType, @@ -163,11 +187,14 @@ class Proto2THUAI6(NoInstance): MessageType.BOMB_BOMB: THUAI6.BulletType.BombBomb, MessageType.COMMON_ATTACK_OF_TRICKER: THUAI6.BulletType.CommonAttackOfTricker, MessageType.JUMPY_DUMPTY: THUAI6.BulletType.JumpyDumpty, - MessageType.ATOM_BOMB: THUAI6.BulletType.AtomBomb, } + MessageType.ATOM_BOMB: THUAI6.BulletType.AtomBomb, + } # 用于将Proto的对象转为THUAI6的对象 - @ staticmethod - def Protobuf2THUAI6Tricker(trickerMsg: Message2Clients.MessageOfTricker) -> THUAI6.Tricker: + @staticmethod + def Protobuf2THUAI6Tricker( + trickerMsg: Message2Clients.MessageOfTricker, + ) -> THUAI6.Tricker: tricker = THUAI6.Tricker() tricker.x = trickerMsg.x tricker.y = trickerMsg.y @@ -192,8 +219,10 @@ class Proto2THUAI6(NoInstance): tricker.playerType = THUAI6.PlayerType.TrickerPlayer return tricker - @ staticmethod - def Protobuf2THUAI6Student(studentMsg: Message2Clients.MessageOfStudent) -> THUAI6.Student: + @staticmethod + def Protobuf2THUAI6Student( + studentMsg: Message2Clients.MessageOfStudent, + ) -> THUAI6.Student: student = THUAI6.Student() student.x = studentMsg.x student.y = studentMsg.y @@ -223,7 +252,7 @@ class Proto2THUAI6(NoInstance): student.playerType = THUAI6.PlayerType.StudentPlayer return student - @ staticmethod + @staticmethod def Protobuf2THUAI6Prop(propMsg: Message2Clients.MessageOfProp) -> THUAI6.Prop: prop = THUAI6.Prop() prop.x = propMsg.x @@ -233,7 +262,7 @@ class Proto2THUAI6(NoInstance): prop.facingDirection = propMsg.facing_direction return prop - @ staticmethod + @staticmethod def Protobuf2THUAI6GameInfo(allMsg: Message2Clients.MessageOfAll): gameInfo = THUAI6.GameInfo() gameInfo.gameTime = allMsg.game_time @@ -244,8 +273,10 @@ class Proto2THUAI6(NoInstance): gameInfo.trickerScore = allMsg.tricker_score return gameInfo - @ staticmethod - def Protobuf2THUAI6Bullet(bulletMsg: Message2Clients.MessageOfBullet) -> THUAI6.Bullet: + @staticmethod + def Protobuf2THUAI6Bullet( + bulletMsg: Message2Clients.MessageOfBullet, + ) -> THUAI6.Bullet: bullet = THUAI6.Bullet() bullet.x = bulletMsg.x bullet.y = bulletMsg.y @@ -256,8 +287,10 @@ class Proto2THUAI6(NoInstance): bullet.bombRange = bulletMsg.bomb_range return bullet - @ staticmethod - def Protobuf2THUAI6BombedBullet(bulletMsg: Message2Clients.MessageOfBombedBullet) -> THUAI6.BombedBullet: + @staticmethod + def Protobuf2THUAI6BombedBullet( + bulletMsg: Message2Clients.MessageOfBombedBullet, + ) -> THUAI6.BombedBullet: bullet = THUAI6.BombedBullet() bullet.x = bulletMsg.x bullet.y = bulletMsg.y @@ -267,7 +300,7 @@ class Proto2THUAI6(NoInstance): bullet.bombRange = bulletMsg.bomb_range return bullet - @ staticmethod + @staticmethod def Bool2HiddenGateState(gateMsg: bool) -> THUAI6.HiddenGateState: if gateMsg: return THUAI6.HiddenGateState.Opened @@ -288,12 +321,14 @@ class THUAI62Proto(NoInstance): THUAI6.PlaceType.Door5: MessageType.DOOR5, THUAI6.PlaceType.Door6: MessageType.DOOR6, THUAI6.PlaceType.Chest: MessageType.CHEST, - THUAI6.PlaceType.Window: MessageType.WINDOW, } + THUAI6.PlaceType.Window: MessageType.WINDOW, + } playerTypeDict: Final[dict] = { THUAI6.PlayerType.NullPlayerType: MessageType.NULL_PLAYER_TYPE, THUAI6.PlayerType.StudentPlayer: MessageType.STUDENT_PLAYER, - THUAI6.PlayerType.TrickerPlayer: MessageType.TRICKER_PLAYER} + THUAI6.PlayerType.TrickerPlayer: MessageType.TRICKER_PLAYER, + } studentTypeDict: Final[dict] = { THUAI6.StudentType.NullStudentType: MessageType.NULL_STUDENT_TYPE, @@ -302,14 +337,16 @@ class THUAI62Proto(NoInstance): THUAI6.StudentType.StraightAStudent: MessageType.STRAIGHT_A_STUDENT, THUAI6.StudentType.Robot: MessageType.ROBOT, THUAI6.StudentType.TechOtaku: MessageType.TECH_OTAKU, - THUAI6.StudentType.Sunshine: MessageType.SUNSHINE, } + THUAI6.StudentType.Sunshine: MessageType.SUNSHINE, + } trickerTypeDict: Final[dict] = { THUAI6.TrickerType.NullTrickerType: MessageType.NULL_TRICKER_TYPE, THUAI6.TrickerType.Assassin: MessageType.ASSASSIN, THUAI6.TrickerType.Klee: MessageType.KLEE, THUAI6.TrickerType.ANoisyPerson: MessageType.A_NOISY_PERSON, - THUAI6.TrickerType.Idol: MessageType.IDOL, } + THUAI6.TrickerType.Idol: MessageType.IDOL, + } propTypeDict: Final[dict] = { THUAI6.PropType.NullPropType: MessageType.NULL_PROP_TYPE, @@ -319,44 +356,70 @@ class THUAI62Proto(NoInstance): THUAI6.PropType.AddHpOrAp: MessageType.ADD_HP_OR_AP, THUAI6.PropType.AddLifeOrClairaudience: MessageType.ADD_LIFE_OR_CLAIRAUDIENCE, THUAI6.PropType.AddSpeed: MessageType.ADD_SPEED, - THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR, } + THUAI6.PropType.ShieldOrSpear: MessageType.SHIELD_OR_SPEAR, + } # 用于将THUAI6的对象转为Proto的对象 - @ staticmethod - def THUAI62ProtobufPlayer(playerID: int, playerType: THUAI6.PlayerType, studentType: THUAI6.StudentType, trickerType: THUAI6.TrickerType) -> Message2Server.PlayerMsg: + @staticmethod + def THUAI62ProtobufPlayer( + playerID: int, + playerType: THUAI6.PlayerType, + studentType: THUAI6.StudentType, + trickerType: THUAI6.TrickerType, + ) -> Message2Server.PlayerMsg: if playerType == THUAI6.PlayerType.StudentPlayer: - return Message2Server.PlayerMsg(player_id=playerID, player_type=MessageType.STUDENT_PLAYER, student_type=THUAI62Proto.studentTypeDict[studentType]) + return Message2Server.PlayerMsg( + player_id=playerID, + player_type=MessageType.STUDENT_PLAYER, + student_type=THUAI62Proto.studentTypeDict[studentType], + ) else: - return Message2Server.PlayerMsg(player_id=playerID, player_type=MessageType.TRICKER_PLAYER, tricker_type=THUAI62Proto.trickerTypeDict[trickerType]) + return Message2Server.PlayerMsg( + player_id=playerID, + player_type=MessageType.TRICKER_PLAYER, + tricker_type=THUAI62Proto.trickerTypeDict[trickerType], + ) - @ staticmethod + @staticmethod def THUAI62ProtobufID(playerID: int) -> Message2Server.IDMsg: return Message2Server.IDMsg(player_id=playerID) - @ staticmethod + @staticmethod def THUAI62ProtobufMove(time: int, angle: float, id: int) -> Message2Server.MoveMsg: - return Message2Server.MoveMsg(player_id=id, angle=angle, time_in_milliseconds=time) + return Message2Server.MoveMsg( + player_id=id, angle=angle, time_in_milliseconds=time + ) - @ staticmethod - def THUAI62ProtobufTreatAndRescue(playerID: int, mateID: int) -> Message2Server.TreatAndRescueMsg: + @staticmethod + def THUAI62ProtobufTreatAndRescue( + playerID: int, mateID: int + ) -> Message2Server.TreatAndRescueMsg: return Message2Server.TreatAndRescueMsg(player_id=playerID, to_player_id=mateID) - @ staticmethod + @staticmethod def THUAI62ProtobufProp(prop: THUAI6.PropType, id: int) -> Message2Server.PropMsg: - return Message2Server.PropMsg(player_id=id, prop_type=THUAI62Proto.propTypeDict[prop]) + return Message2Server.PropMsg( + player_id=id, prop_type=THUAI62Proto.propTypeDict[prop] + ) - @ staticmethod - def THUAI62ProtobufSend(msg: Union[str, bytes], toID: int, id: int) -> Message2Server.SendMsg: + @staticmethod + def THUAI62ProtobufSend( + msg: Union[str, bytes], toID: int, id: int + ) -> Message2Server.SendMsg: if isinstance(msg, str): - return Message2Server.SendMsg(player_id=id, to_player_id=toID, text_message=msg) + return Message2Server.SendMsg( + player_id=id, to_player_id=toID, text_message=msg + ) elif isinstance(msg, bytes): - return Message2Server.SendMsg(player_id=id, to_player_id=toID, binary_message=msg) + return Message2Server.SendMsg( + player_id=id, to_player_id=toID, binary_message=msg + ) - @ staticmethod + @staticmethod def THUAI62ProtobufAttack(angle: float, id: int) -> Message2Server.AttackMsg: return Message2Server.AttackMsg(player_id=id, angle=angle) - @ staticmethod + @staticmethod def THUAI62ProtobufSkill(skillID: int, id: int) -> Message2Server.SkillMsg: return Message2Server.SkillMsg(player_id=id, skill_id=skillID) diff --git a/docs/游戏机制与平衡性调整更新草案.md b/docs/游戏机制与平衡性调整更新草案.md index c4bd4b8..24a3f1b 100644 --- a/docs/游戏机制与平衡性调整更新草案.md +++ b/docs/游戏机制与平衡性调整更新草案.md @@ -1,12 +1,12 @@ # 游戏机制与平衡性调整更新草案 -v1.2 +v1.3 ## 说明 - 该草案尚未完全确定,请大家不要过分依靠该文档进行修改自己的代码 - 有任何问题都可以在选手群中提出建议和讨论 ## 游戏接口 - 删除structures.h中Player的属性place + - 删除structures.h中Player的属性place ## 游戏规则 @@ -71,7 +71,7 @@ v1.2 - CD:0s,持续时间:0s - TechOtaku的Robot的PlayerId = TechOtaku的PlayerId + n×5(一局游戏理论人数),其中1<=n<=3(自己的n为0) - 新造的Robot的PlayerId的n总是尽量小 - - 每使用一次UseRobot,TechOtaku就切换一次操控的角色,若之前操控角色的PlayerId =TechOtaku的PlayerId + j×5,其PlayerId变为PlayerId =TechOtaku的PlayerId + ((j+1)mod 4 )×5,若其不可用,则再次进行这样的操作 + - 每使用一次UseRobot,TechOtaku就切换一次操控的角色(具体来说,若之前操控角色的PlayerId =TechOtaku的PlayerId + j×5,其PlayerId变为PlayerId =TechOtaku的PlayerId + ((j+1)mod 4 )×5,若其不可用,则自动再次进行这样的操作) 修改后: | 学生职业 | 教师Teacher | 健身狂Athlete |学霸StraightAStudent | 开心果Sunshine | 机器人Robot | 技术宅TechOtaku | diff --git a/docs/版本更新说明.md b/docs/版本更新说明.md index 22cf437..bc59a20 100644 --- a/docs/版本更新说明.md +++ b/docs/版本更新说明.md @@ -3,7 +3,7 @@ # 说明 - 只说明对于选手较为重要的修改 -# 等待更新的更改 +# 5月6日更新 - docs:添加了 游戏机制与平衡性调整更新草案.pdf - docs:添加了 版本更新说明.pdf - docs&hotfix: 修正了GameRules.pdf中学生翻窗速度的错误 diff --git a/logic/GameClass/GameObj/Bullet/BombedBullet.cs b/logic/GameClass/GameObj/Bullet/BombedBullet.cs index b4afd1f..492ecb8 100644 --- a/logic/GameClass/GameObj/Bullet/BombedBullet.cs +++ b/logic/GameClass/GameObj/Bullet/BombedBullet.cs @@ -3,7 +3,7 @@ namespace GameClass.GameObj { // 为方便界面组做子弹爆炸特效,现引入“爆炸中的子弹”,在每帧发送给界面组 - public sealed class BombedBullet : GameObj + public sealed class BombedBullet : Immovable { public override ShapeType Shape => ShapeType.Circle; public override bool IsRigid => false; @@ -15,7 +15,7 @@ namespace GameClass.GameObj { this.bulletHasBombed = bullet; this.MappingID = bullet.ID; - this.FacingDirection = bullet.FacingDirection; + this.facingDirection = bullet.FacingDirection; } } } diff --git a/logic/GameClass/GameObj/Bullet/Bullet.cs b/logic/GameClass/GameObj/Bullet/Bullet.cs index acada59..ff512bc 100644 --- a/logic/GameClass/GameObj/Bullet/Bullet.cs +++ b/logic/GameClass/GameObj/Bullet/Bullet.cs @@ -44,7 +44,7 @@ namespace GameClass.GameObj public Bullet(Character player, int radius, XY Position) : base(Position, radius, GameObjType.Bullet) { - this.CanMove = true; + this.canMove = true; this.moveSpeed = this.Speed; this.hasSpear = player.TryUseSpear(); this.Parent = player; diff --git a/logic/GameClass/GameObj/Character/Character.Skill.cs b/logic/GameClass/GameObj/Character/Character.Skill.cs index c835a6d..0ae23db 100644 --- a/logic/GameClass/GameObj/Character/Character.Skill.cs +++ b/logic/GameClass/GameObj/Character/Character.Skill.cs @@ -57,7 +57,7 @@ namespace GameClass.GameObj protected Character(XY initPos, int initRadius, CharacterType characterType) : base(initPos, initRadius, GameObjType.Character) { - this.CanMove = true; + this.canMove = true; this.score = 0; this.buffManager = new BuffManager(); this.occupation = OccupationFactory.FindIOccupation(characterType); diff --git a/logic/GameClass/GameObj/Character/Character.cs b/logic/GameClass/GameObj/Character/Character.cs index aa799eb..ba00643 100644 --- a/logic/GameClass/GameObj/Character/Character.cs +++ b/logic/GameClass/GameObj/Character/Character.cs @@ -2,9 +2,6 @@ using Preparation.Utility; using System; using System.Collections.Generic; -using System.Numerics; -using System.Runtime.InteropServices; -using System.Threading; namespace GameClass.GameObj { @@ -58,10 +55,20 @@ namespace GameClass.GameObj /// 进行一次攻击 /// /// 攻击操作发出的子弹 - public Bullet? Attack(XY pos) + public Bullet? Attack(double angle) { if (TrySubBulletNum()) - return BulletFactory.GetBullet(this, pos); + { + XY res = Position + new XY // 子弹紧贴人物生成。 + ( + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(BulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1), + (int)(Math.Abs((Radius + BulletFactory.BulletRadius(BulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1) + ); + Bullet? bullet = BulletFactory.GetBullet(this, res); + if (bullet == null) return null; + facingDirection = new(angle, bullet.BulletAttackRange); + return bullet; + } else return null; } @@ -362,9 +369,9 @@ namespace GameClass.GameObj lock (gameObjLock) { playerState = playerStateType; - CanMove = false; + canMove = false; IsResetting = true; - Position = GameData.PosWhoDie; + position = GameData.PosWhoDie; } } #endregion diff --git a/logic/GameClass/GameObj/GameObj.cs b/logic/GameClass/GameObj/GameObj.cs index d9db633..0b7727c 100644 --- a/logic/GameClass/GameObj/GameObj.cs +++ b/logic/GameClass/GameObj/GameObj.cs @@ -9,6 +9,8 @@ namespace GameClass.GameObj /// public abstract class GameObj : IGameObj { + private ReaderWriterLockSlim gameObjReaderWriterLock = new(); + public ReaderWriterLockSlim GameObjReaderWriterLock => gameObjReaderWriterLock; protected readonly object gameObjLock = new(); public object GameLock => gameObjLock; @@ -22,53 +24,18 @@ namespace GameClass.GameObj public long ID { get; } protected XY position; - public virtual XY Position { get; set; } + public abstract XY Position { get; } - private XY facingDirection = new(1, 0); - public XY FacingDirection - { - get - { - lock (gameObjLock) - return facingDirection; - } - set - { - lock (gameObjLock) - facingDirection = value; - } - } + protected XY facingDirection = new(1, 0); + public abstract XY FacingDirection { get; } + + protected bool canMove; + public abstract bool CanMove { get; } public abstract bool IsRigid { get; } public abstract ShapeType Shape { get; } - private bool canMove; - public bool CanMove - { - get => canMove; - set - { - lock (gameObjLock) - { - canMove = value; - } - } - } - - private bool isResetting; - public bool IsResetting - { - get => isResetting; - set - { - lock (gameObjLock) - { - isResetting = value; - } - } - } - public int Radius { get; } public virtual bool IgnoreCollideExecutor(IGameObj targetObj) => false; diff --git a/logic/GameClass/GameObj/Immovable.cs b/logic/GameClass/GameObj/Immovable.cs new file mode 100644 index 0000000..6662d98 --- /dev/null +++ b/logic/GameClass/GameObj/Immovable.cs @@ -0,0 +1,19 @@ +using Preparation.Interface; +using Preparation.Utility; + +namespace GameClass.GameObj +{ + public abstract class Immovable : GameObj + { + + public override XY Position => position; + + public override XY FacingDirection => facingDirection; + + public override bool CanMove => false; + + public Immovable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) + { + } + } +} diff --git a/logic/GameClass/GameObj/Map/Chest.cs b/logic/GameClass/GameObj/Map/Chest.cs index b302e44..69a1ed9 100644 --- a/logic/GameClass/GameObj/Map/Chest.cs +++ b/logic/GameClass/GameObj/Map/Chest.cs @@ -1,22 +1,20 @@ using Preparation.Utility; -using System.Collections.Generic; namespace GameClass.GameObj { /// /// 箱子 /// - public class Chest : GameObj + public class Chest : Immovable { public Chest(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Chest) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; - private Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; + private readonly Prop[] propInChest = new Prop[GameData.maxNumOfPropInChest] { new NullProp(), new NullProp() }; public Prop[] PropInChest => propInChest; private int openStartTime = 0; @@ -25,7 +23,7 @@ namespace GameClass.GameObj public Character? WhoOpen => whoOpen; public void Open(int startTime, Character character) { - lock (gameObjLock) + lock (GameObjReaderWriterLock) { openStartTime = startTime; whoOpen = character; @@ -33,7 +31,7 @@ namespace GameClass.GameObj } public void StopOpen() { - lock (gameObjLock) + lock (GameObjReaderWriterLock) { openStartTime = 0; whoOpen = null; diff --git a/logic/GameClass/GameObj/Map/Door.cs b/logic/GameClass/GameObj/Map/Door.cs index c03f160..2410421 100644 --- a/logic/GameClass/GameObj/Map/Door.cs +++ b/logic/GameClass/GameObj/Map/Door.cs @@ -6,7 +6,7 @@ namespace GameClass.GameObj /// /// 门 /// - public class Door : GameObj + public class Door : Immovable { public Door(XY initPos, PlaceType placeType) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Door) @@ -24,7 +24,6 @@ namespace GameClass.GameObj doorNum = 6; break; } - this.CanMove = false; } private readonly int doorNum; diff --git a/logic/GameClass/GameObj/Map/Doorway.cs b/logic/GameClass/GameObj/Map/Doorway.cs index 4c5f78d..71b2017 100644 --- a/logic/GameClass/GameObj/Map/Doorway.cs +++ b/logic/GameClass/GameObj/Map/Doorway.cs @@ -6,12 +6,11 @@ namespace GameClass.GameObj /// /// 出口 /// - public class Doorway : GameObj + public class Doorway : Immovable { public Doorway(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Doorway) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; diff --git a/logic/GameClass/GameObj/Map/EmergencyExit.cs b/logic/GameClass/GameObj/Map/EmergencyExit.cs index 02414c4..5e51daf 100644 --- a/logic/GameClass/GameObj/Map/EmergencyExit.cs +++ b/logic/GameClass/GameObj/Map/EmergencyExit.cs @@ -6,12 +6,11 @@ namespace GameClass.GameObj /// /// 紧急出口 /// - public class EmergencyExit : GameObj + public class EmergencyExit : Immovable { public EmergencyExit(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.EmergencyExit) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; diff --git a/logic/GameClass/GameObj/Map/Generator.cs b/logic/GameClass/GameObj/Map/Generator.cs index fe3fbd5..204c0f3 100644 --- a/logic/GameClass/GameObj/Map/Generator.cs +++ b/logic/GameClass/GameObj/Map/Generator.cs @@ -5,12 +5,11 @@ namespace GameClass.GameObj /// /// 发电机 /// - public class Generator : GameObj + public class Generator : Immovable { public Generator(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Generator) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; diff --git a/logic/GameClass/GameObj/Map/Map.cs b/logic/GameClass/GameObj/Map/Map.cs index 3cfbf49..3a3869f 100644 --- a/logic/GameClass/GameObj/Map/Map.cs +++ b/logic/GameClass/GameObj/Map/Map.cs @@ -8,7 +8,6 @@ namespace GameClass.GameObj { public partial class Map : IMap { - private readonly Dictionary birthPointList; // 出生点列表 public Dictionary BirthPointList => birthPointList; diff --git a/logic/GameClass/GameObj/Map/Wall.cs b/logic/GameClass/GameObj/Map/Wall.cs index 1cef8e9..8001b8d 100644 --- a/logic/GameClass/GameObj/Map/Wall.cs +++ b/logic/GameClass/GameObj/Map/Wall.cs @@ -5,12 +5,11 @@ namespace GameClass.GameObj /// /// 墙体 /// - public class Wall : GameObj + public class Wall : Immovable { public Wall(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Wall) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; diff --git a/logic/GameClass/GameObj/Map/Window.cs b/logic/GameClass/GameObj/Map/Window.cs index a71e05f..864e58f 100644 --- a/logic/GameClass/GameObj/Map/Window.cs +++ b/logic/GameClass/GameObj/Map/Window.cs @@ -6,12 +6,11 @@ namespace GameClass.GameObj /// /// 窗 /// - public class Window : GameObj + public class Window : Immovable { public Window(XY initPos) : base(initPos, GameData.numOfPosGridPerCell / 2, GameObjType.Window) { - this.CanMove = false; } public override bool IsRigid => true; public override ShapeType Shape => ShapeType.Square; diff --git a/logic/GameClass/GameObj/Moveable.cs b/logic/GameClass/GameObj/Moveable.cs index 4184fa2..b134e67 100644 --- a/logic/GameClass/GameObj/Moveable.cs +++ b/logic/GameClass/GameObj/Moveable.cs @@ -4,43 +4,102 @@ using System.Threading; namespace GameClass.GameObj { - /// - /// 一切游戏元素的总基类,与THUAI4不同,继承IMoveable接口(出于一切物体其实都是可运动的指导思想)——LHR - /// public abstract class Moveable : GameObj, IMoveable { protected readonly object moveObjLock = new(); public object MoveLock => moveObjLock; + private ReaderWriterLockSlim moveReaderWriterLock = new(); + public ReaderWriterLockSlim MoveReaderWriterLock => moveReaderWriterLock; public override XY Position { get { - lock (gameObjLock) + lock (moveObjLock) return position; } - set + } + + public override XY FacingDirection + { + get { - lock (gameObjLock) - { - position = value; - } + lock (moveObjLock) + return facingDirection; } } private bool isMoving; public bool IsMoving { - get => isMoving; + get + { + lock (moveObjLock) + return isMoving; + } set { - lock (gameObjLock) + lock (moveObjLock) { isMoving = value; } } } + // 移动,改变坐标 + public long MovingSetPos(XY moveVec) + { + if (moveVec.x != 0 || moveVec.y != 0) + lock (moveObjLock) + { + facingDirection = moveVec; + this.position += moveVec; + } + return moveVec * moveVec; + } + + public void ReSetPos(XY position) + { + lock (moveObjLock) + { + this.position = position; + } + } + + public override bool CanMove + { + get + { + lock (moveReaderWriterLock) + return canMove; + } + } + + public void ReSetCanMove(bool value) + { + lock (moveReaderWriterLock) + { + canMove = value; + } + } + + private bool isResetting; + public bool IsResetting + { + get + { + lock (moveReaderWriterLock) + return isResetting; + } + set + { + lock (moveReaderWriterLock) + { + isResetting = value; + } + } + } + public bool IsAvailable => !IsMoving && CanMove && !IsResetting; // 是否能接收指令 protected int moveSpeed; @@ -49,10 +108,14 @@ namespace GameClass.GameObj /// public int MoveSpeed { - get => moveSpeed; + get + { + lock (moveReaderWriterLock) + return moveSpeed; + } set { - lock (gameObjLock) + lock (moveReaderWriterLock) { moveSpeed = value; } @@ -63,26 +126,6 @@ namespace GameClass.GameObj /// public int OrgMoveSpeed { get; protected set; } - // 移动,改变坐标 - public long MovingSetPos(XY moveVec) - { - if (moveVec.x != 0 || moveVec.y != 0) - lock (gameObjLock) - { - FacingDirection = moveVec; - this.Position += moveVec; - } - return moveVec * moveVec; - } - - /// - /// 设置移动速度 - /// - /// 新速度 - public void SetMoveSpeed(int newMoveSpeed) - { - MoveSpeed = newMoveSpeed; - } /* /// /// 复活时数据重置 /// @@ -98,11 +141,6 @@ namespace GameClass.GameObj this.Place= place; } }*/ - /// - /// 为了使IgnoreCollide多态化并使GameObj能不报错地继承IMoveable - /// 在xfgg点播下设计了这个抽象辅助方法,在具体类中实现 - /// - /// 依具体类及该方法参数而定,默认为false public Moveable(XY initPos, int initRadius, GameObjType initType) : base(initPos, initRadius, initType) { } diff --git a/logic/GameClass/GameObj/ObjOfCharacter.cs b/logic/GameClass/GameObj/ObjOfCharacter.cs index 1004891..7bdcba1 100644 --- a/logic/GameClass/GameObj/ObjOfCharacter.cs +++ b/logic/GameClass/GameObj/ObjOfCharacter.cs @@ -1,5 +1,7 @@ -using Preparation.Interface; +using Google.Protobuf.WellKnownTypes; +using Preparation.Interface; using Preparation.Utility; +using System.Threading; namespace GameClass.GameObj { @@ -8,13 +10,21 @@ namespace GameClass.GameObj /// public abstract class ObjOfCharacter : Moveable, IObjOfCharacter { + private ReaderWriterLockSlim objOfCharacterReaderWriterLock = new(); + public ReaderWriterLockSlim ObjOfCharacterReaderWriterLock => objOfCharacterReaderWriterLock; private ICharacter? parent = null; // 主人 public ICharacter? Parent { - get => parent; + get + { + lock (objOfCharacterReaderWriterLock) + { + return parent; + } + } set { - lock (gameObjLock) + lock (objOfCharacterReaderWriterLock) { parent = value; } diff --git a/logic/GameClass/GameObj/OutOfBoundBlock.cs b/logic/GameClass/GameObj/OutOfBoundBlock.cs index 3cd88eb..8f0c45d 100644 --- a/logic/GameClass/GameObj/OutOfBoundBlock.cs +++ b/logic/GameClass/GameObj/OutOfBoundBlock.cs @@ -6,12 +6,11 @@ namespace GameClass.GameObj /// /// 逻辑墙 /// - public class OutOfBoundBlock : GameObj, IOutOfBound + public class OutOfBoundBlock : Immovable, IOutOfBound { public OutOfBoundBlock(XY initPos) : base(initPos, int.MaxValue, GameObjType.OutOfBoundBlock) { - this.CanMove = false; } public override bool IsRigid => true; diff --git a/logic/GameClass/GameObj/PickedProp.cs b/logic/GameClass/GameObj/PickedProp.cs index 221e6e8..c924c82 100644 --- a/logic/GameClass/GameObj/PickedProp.cs +++ b/logic/GameClass/GameObj/PickedProp.cs @@ -1,23 +1,18 @@ using Preparation.Utility; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace GameClass.GameObj { // 为方便界面组做道具拾起特效,现引入“被捡起的道具”,在每帧发送给界面组 - public class PickedProp : GameObj + public class PickedProp : Immovable { public override ShapeType Shape => ShapeType.Circle; public override bool IsRigid => false; public long MappingID { get; } - public Prop PropHasPicked; + public readonly Prop propHasPicked; public PickedProp(Prop prop) : base(prop.Position, prop.Radius, GameObjType.PickedProp) { - this.PropHasPicked = prop; + this.propHasPicked = prop; this.MappingID = prop.ID; } } diff --git a/logic/GameClass/GameObj/Prop.cs b/logic/GameClass/GameObj/Prop.cs index c3626de..93848e8 100644 --- a/logic/GameClass/GameObj/Prop.cs +++ b/logic/GameClass/GameObj/Prop.cs @@ -22,7 +22,7 @@ namespace GameClass.GameObj public Prop(XY initPos, int radius = GameData.PropRadius) : base(initPos, radius, GameObjType.Prop) { - this.CanMove = false; + this.canMove = false; this.moveSpeed = GameData.PropMoveSpeed; } } diff --git a/logic/Gaming/ActionManager.cs b/logic/Gaming/ActionManager.cs index 2e142e1..5fe275c 100644 --- a/logic/Gaming/ActionManager.cs +++ b/logic/Gaming/ActionManager.cs @@ -267,7 +267,7 @@ namespace Gaming { Prop prop = chestToOpen.PropInChest[i]; chestToOpen.PropInChest[i] = new NullProp(); - prop.Position = player.Position; + prop.ReSetPos(player.Position); gameMap.Add(prop); } } @@ -323,7 +323,7 @@ namespace Gaming return; } - player.Position = windowToPlayer + windowForClimb.Position; + player.ReSetPos(windowToPlayer + windowForClimb.Position); player.MoveSpeed = player.SpeedOfClimbingThroughWindows; moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle()); @@ -339,7 +339,7 @@ namespace Gaming ) .Start(); XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer; - player.Position = PosJumpOff; + player.ReSetPos(PosJumpOff); player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed); windowForClimb.WhoIsClimbing = null; // gameMap.Remove(addWall); diff --git a/logic/Gaming/AttackManager.cs b/logic/Gaming/AttackManager.cs index 69203b1..f35960a 100644 --- a/logic/Gaming/AttackManager.cs +++ b/logic/Gaming/AttackManager.cs @@ -33,7 +33,7 @@ namespace Gaming 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 = false; + obj.ReSetCanMove(false); } ); this.characterManager = characterManager; @@ -66,7 +66,7 @@ namespace Gaming public bool TryRemoveBullet(Bullet bullet) { - bullet.CanMove = false; + bullet.ReSetCanMove(false); if (gameMap.Remove(bullet)) { if (bullet.BulletBombRange > 0) @@ -172,20 +172,14 @@ namespace Gaming Debugger.Output(player, player.CharacterType.ToString() + "Attack in " + player.BulletOfPlayer.ToString()); Debugger.Output(player, player.Position.ToString() + " " + player.Radius.ToString() + " " + BulletFactory.BulletRadius(player.BulletOfPlayer).ToString()); - XY res = player.Position + new XY // 子弹紧贴人物生成。 - ( - (int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Cos(angle))) * ((Math.Cos(angle) > 0) ? 1 : -1), - (int)(Math.Abs((player.Radius + BulletFactory.BulletRadius(player.BulletOfPlayer)) * Math.Sin(angle))) * ((Math.Sin(angle) > 0) ? 1 : -1) - ); - Bullet? bullet = player.Attack(res); + Bullet? bullet = player.Attack(angle); if (bullet != null) { - player.FacingDirection = new(angle, bullet.BulletAttackRange); Debugger.Output(bullet, "Attack in " + bullet.Position.ToString()); bullet.AP += player.TryAddAp() ? GameData.ApPropAdd : 0; - bullet.CanMove = true; + bullet.ReSetCanMove(true); gameMap.Add(bullet); moveEngine.MoveObj(bullet, (int)((bullet.BulletAttackRange - player.Radius - BulletFactory.BulletRadius(player.BulletOfPlayer)) * 1000 / bullet.MoveSpeed), angle); // 这里时间参数除出来的单位要是ms if (bullet.CastTime > 0) diff --git a/logic/Gaming/CharacterManager .cs b/logic/Gaming/CharacterManager .cs index 92593a4..31a5b18 100644 --- a/logic/Gaming/CharacterManager .cs +++ b/logic/Gaming/CharacterManager .cs @@ -407,7 +407,7 @@ namespace Gaming Prop? prop = player.UseProp(i); if (prop != null) { - prop.Position = player.Position; + prop.ReSetPos(player.Position); gameMap.Add(prop); } } diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 8378eba..8a0dd98 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -316,7 +316,7 @@ namespace Gaming { foreach (Character player in gameMap.GameObjDict[GameObjType.Character]) { - player.CanMove = false; + player.ReSetCanMove(false); } } gameMap.GameObjDict[keyValuePair.Key].Clear(); diff --git a/logic/Gaming/PropManager.cs b/logic/Gaming/PropManager.cs index 57b18c9..9bf9714 100644 --- a/logic/Gaming/PropManager.cs +++ b/logic/Gaming/PropManager.cs @@ -124,7 +124,7 @@ namespace Gaming if (prop.GetPropType() == PropType.Null) return; - prop.Position = player.Position; + prop.ReSetPos(player.Position); gameMap.Add(prop); } diff --git a/logic/Preparation/Interface/IGameObj.cs b/logic/Preparation/Interface/IGameObj.cs index 58c4141..017226f 100644 --- a/logic/Preparation/Interface/IGameObj.cs +++ b/logic/Preparation/Interface/IGameObj.cs @@ -10,8 +10,7 @@ namespace Preparation.Interface public XY FacingDirection { get; } public bool IsRigid { get; } public ShapeType Shape { get; } - public bool CanMove { get; set; } - public bool IsResetting { get; set; } // reviving + public bool CanMove { get; } public int Radius { get; } // if Square, Radius equals half length of one side public bool IgnoreCollideExecutor(IGameObj targetObj); // 忽略碰撞,在具体类中实现 } diff --git a/logic/Preparation/Interface/IMoveable.cs b/logic/Preparation/Interface/IMoveable.cs index d9c5e3a..7abe556 100644 --- a/logic/Preparation/Interface/IMoveable.cs +++ b/logic/Preparation/Interface/IMoveable.cs @@ -8,8 +8,10 @@ namespace Preparation.Interface object MoveLock { get; } public int MoveSpeed { get; } public bool IsMoving { get; set; } + public bool IsResetting { get; set; } // reviving public bool IsAvailable { get; } public long MovingSetPos(XY moveVec); + public void ReSetCanMove(bool value); public bool WillCollideWith(IGameObj? targetObj, XY nextPos) // 检查下一位置是否会和目标物碰撞 { if (targetObj == null)