feat(go): ✨ experimantal capi framework for go
dev
| @@ -0,0 +1,5 @@ | |||
| # experimental/CAPI | |||
| ## 简介 | |||
| 实验性选手接口 | |||
| @@ -0,0 +1,28 @@ | |||
| # If you prefer the allow list template instead of the deny list, see community template: | |||
| # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore | |||
| # | |||
| # Binaries for programs and plugins | |||
| *.exe | |||
| *.exe~ | |||
| *.dll | |||
| *.so | |||
| *.dylib | |||
| # Test binary, built with `go test -c` | |||
| *.test | |||
| # Output of the go coverage tool, specifically when used with LiteIDE | |||
| *.out | |||
| # Dependency directories (remove the comment below to include it) | |||
| # vendor/ | |||
| # Go workspace file | |||
| go.work | |||
| # Protocol Buffers generated code directory | |||
| /API/proto/ | |||
| # Executables | |||
| /API/API | |||
| /API/API.exe | |||
| @@ -0,0 +1,15 @@ | |||
| package main | |||
| import ( | |||
| thuai6 "API/thuai6" | |||
| ) | |||
| const Asynchronous = false | |||
| func GetStudentType() []thuai6.StudentType { | |||
| return []thuai6.StudentType{thuai6.Athlete, thuai6.Teacher, thuai6.StraightAStudent, thuai6.Sunshine} | |||
| } | |||
| func GetTrickerType() thuai6.TrickerType { | |||
| return thuai6.Assassin | |||
| } | |||
| @@ -0,0 +1,80 @@ | |||
| package core | |||
| import ( | |||
| thuai6 "API/thuai6" | |||
| ) | |||
| type ILogic interface { | |||
| Move(time int64, angle float64) bool | |||
| TryConnection() bool | |||
| GetStudentSelfInfo() *thuai6.Student | |||
| GetTrickerSelfInfo() *thuai6.Tricker | |||
| } | |||
| type StudentAPI struct { | |||
| logic ILogic | |||
| } | |||
| type TrickerAPI struct { | |||
| logic ILogic | |||
| } | |||
| func NewStudentAPI(logic ILogic) *StudentAPI { | |||
| return &StudentAPI{ | |||
| logic: logic, | |||
| } | |||
| } | |||
| func NewTrickerAPI(logic ILogic) *TrickerAPI { | |||
| return &TrickerAPI{ | |||
| logic: logic, | |||
| } | |||
| } | |||
| func (api *StudentAPI) Move(timeInMilliseconds int64, angleInRadian float64) <-chan bool { | |||
| res := make(chan bool, 1) | |||
| go func() { | |||
| res <- api.logic.Move(timeInMilliseconds, angleInRadian) | |||
| }() | |||
| return res | |||
| } | |||
| func (api *StudentAPI) GetSelfInfo() *thuai6.Student { | |||
| return api.logic.GetStudentSelfInfo() | |||
| } | |||
| func (api *StudentAPI) StartTimer() { | |||
| // Nothing | |||
| } | |||
| func (api *StudentAPI) EndTimer() { | |||
| // Nothing | |||
| } | |||
| func (api *StudentAPI) Play(ai thuai6.IAI) { | |||
| ai.StudentPlay(api) | |||
| } | |||
| func (api *TrickerAPI) Move(timeInMilliseconds int64, angleInRadian float64) <-chan bool { | |||
| res := make(chan bool, 1) | |||
| go func() { | |||
| res <- api.logic.Move(timeInMilliseconds, angleInRadian) | |||
| }() | |||
| return res | |||
| } | |||
| func (api *TrickerAPI) GetSelfInfo() *thuai6.Tricker { | |||
| return api.logic.GetTrickerSelfInfo() | |||
| } | |||
| func (api *TrickerAPI) StartTimer() { | |||
| // Nothing | |||
| } | |||
| func (api *TrickerAPI) EndTimer() { | |||
| // Nothing | |||
| } | |||
| func (api *TrickerAPI) Play(ai thuai6.IAI) { | |||
| ai.TrickerPlay(api) | |||
| } | |||
| @@ -0,0 +1,97 @@ | |||
| package core | |||
| import ( | |||
| pb "API/proto" | |||
| thuai6 "API/thuai6" | |||
| "context" | |||
| "sync" | |||
| "google.golang.org/grpc" | |||
| "google.golang.org/grpc/credentials/insecure" | |||
| ) | |||
| type Communication struct { | |||
| stub pb.AvailableServiceClient | |||
| messageToClient *pb.MessageToClient | |||
| haveNewMessage bool | |||
| mtxMessage sync.Mutex | |||
| cvMessage *sync.Cond | |||
| } | |||
| func NewCommunication(ip string, port string) (*Communication, error) { | |||
| addr := ip + ":" + port | |||
| conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials())) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| stub := pb.NewAvailableServiceClient(conn) | |||
| comm := &Communication{stub: stub, messageToClient: nil, haveNewMessage: false, mtxMessage: sync.Mutex{}} | |||
| comm.cvMessage = sync.NewCond(&comm.mtxMessage) | |||
| return comm, nil | |||
| } | |||
| func (self *Communication) TryConnection(playerID int64) bool { | |||
| res, err := self.stub.TryConnection(context.Background(), &pb.IDMsg{PlayerId: playerID}) | |||
| if err != nil { | |||
| // TODO: do loggings | |||
| return false | |||
| } | |||
| return res.GetActSuccess() | |||
| } | |||
| func (self *Communication) AddPlayer(playerID int64, playerType thuai6.PlayerType, studentType thuai6.StudentType, trickerType thuai6.TrickerType) { | |||
| var playerMsg pb.PlayerMsg | |||
| playerMsg.PlayerId = playerID | |||
| playerMsg.PlayerType = thuai6.PlayerTypeToProto[playerType] | |||
| if playerType == thuai6.StudentPlayer { | |||
| playerMsg.JobType = &pb.PlayerMsg_StudentType{StudentType: thuai6.StudentTypeToProto[studentType]} | |||
| } else if playerType == thuai6.TrickerPlayer { | |||
| playerMsg.JobType = &pb.PlayerMsg_TrickerType{TrickerType: thuai6.TrickerTypeToProto[trickerType]} | |||
| } else { | |||
| // TODO: Report ERROR | |||
| } | |||
| go func() { | |||
| msgReader, err := self.stub.AddPlayer(context.Background(), &playerMsg) | |||
| if err != nil { | |||
| // TODO: Report ERROR | |||
| } | |||
| for { | |||
| msg, err := msgReader.Recv() | |||
| if err != nil { | |||
| break | |||
| } | |||
| func() { | |||
| self.mtxMessage.Lock() | |||
| defer self.mtxMessage.Unlock() | |||
| self.messageToClient = msg | |||
| self.haveNewMessage = true | |||
| self.cvMessage.Signal() | |||
| }() | |||
| } | |||
| }() | |||
| } | |||
| func (self *Communication) Move(time int64, angle float64, playerID int64) bool { | |||
| res, err := self.stub.Move(context.Background(), &pb.MoveMsg{ | |||
| TimeInMilliseconds: time, | |||
| Angle: angle, | |||
| PlayerId: playerID, | |||
| }) | |||
| if err != nil { | |||
| // TODO: do loggings | |||
| return false | |||
| } | |||
| return res.GetActSuccess() | |||
| } | |||
| func (self *Communication) GetMessageToClient() pb.MessageToClient { | |||
| self.mtxMessage.Lock() | |||
| defer self.mtxMessage.Unlock() | |||
| for !self.haveNewMessage { | |||
| self.cvMessage.Wait() | |||
| } | |||
| self.haveNewMessage = false | |||
| return *self.messageToClient | |||
| } | |||
| @@ -0,0 +1,202 @@ | |||
| package core | |||
| import ( | |||
| "errors" | |||
| "sync" | |||
| "sync/atomic" | |||
| main "API" | |||
| pb "API/proto" | |||
| thuai6 "API/thuai6" | |||
| ) | |||
| type Logic struct { | |||
| communication *Communication | |||
| playerType thuai6.PlayerType | |||
| playerID int64 | |||
| studentType thuai6.StudentType | |||
| trickerType thuai6.TrickerType | |||
| timer thuai6.IGameTimer | |||
| mtxAI sync.Mutex | |||
| mtxState sync.Mutex | |||
| mtxBuffer sync.Mutex | |||
| cvBuffer *sync.Cond | |||
| cvAI *sync.Cond | |||
| state [2]thuai6.State | |||
| currentState *thuai6.State | |||
| bufferState *thuai6.State | |||
| counterState int32 | |||
| counterBuffer int32 | |||
| gameState thuai6.GameState | |||
| aiStart bool | |||
| bufferUpdated bool | |||
| aiLoop int32 | |||
| freshed int32 | |||
| } | |||
| func NewLogic(playerType thuai6.PlayerType, id int64, tricker thuai6.TrickerType, student thuai6.StudentType) *Logic { | |||
| logic := &Logic{ | |||
| communication: nil, | |||
| playerType: playerType, | |||
| playerID: id, | |||
| studentType: student, | |||
| trickerType: tricker, | |||
| timer: nil, | |||
| mtxAI: sync.Mutex{}, | |||
| mtxState: sync.Mutex{}, | |||
| mtxBuffer: sync.Mutex{}, | |||
| cvBuffer: nil, | |||
| cvAI: nil, | |||
| state: [2]thuai6.State{}, | |||
| counterState: 0, | |||
| counterBuffer: 0, | |||
| gameState: thuai6.NullGameState, | |||
| aiStart: false, | |||
| bufferUpdated: true, | |||
| aiLoop: 1, | |||
| freshed: 0, | |||
| } | |||
| logic.currentState = &logic.state[0] | |||
| logic.bufferState = &logic.state[1] | |||
| logic.cvBuffer = sync.NewCond(&logic.mtxBuffer) | |||
| logic.cvAI = sync.NewCond(&logic.mtxAI) | |||
| return logic | |||
| } | |||
| func (logic *Logic) GetStudentSelfInfo() *thuai6.Student { | |||
| logic.mtxState.Lock() | |||
| defer logic.mtxState.Unlock() | |||
| return logic.currentState.StudentSelf | |||
| } | |||
| func (logic *Logic) GetTrickerSelfInfo() *thuai6.Tricker { | |||
| logic.mtxState.Lock() | |||
| defer logic.mtxState.Unlock() | |||
| return logic.currentState.TrickerSelf | |||
| } | |||
| func (logic *Logic) Move(time int64, angle float64) bool { | |||
| return logic.communication.Move(time, angle, logic.playerID) | |||
| } | |||
| func (logic *Logic) TryConnection() bool { | |||
| return logic.communication.TryConnection(logic.playerID) | |||
| } | |||
| func (logic *Logic) Wait() { | |||
| atomic.StoreInt32(&logic.freshed, 0) | |||
| logic.mtxBuffer.Lock() | |||
| defer logic.mtxBuffer.Unlock() | |||
| for atomic.LoadInt32(&logic.freshed) == 0 { | |||
| logic.cvBuffer.Wait() | |||
| } | |||
| } | |||
| func (logic *Logic) Update() { | |||
| if main.Asynchronous == false { | |||
| logic.mtxBuffer.Lock() | |||
| defer logic.mtxBuffer.Unlock() | |||
| for logic.bufferUpdated == false { | |||
| logic.cvBuffer.Wait() | |||
| } | |||
| // Go 的 defer 是函数推出时执行而非 block 退出时执行,故包装一层 func | |||
| func() { | |||
| logic.mtxState.Lock() | |||
| defer logic.mtxState.Unlock() | |||
| var tmpState = logic.currentState | |||
| logic.currentState = logic.bufferState | |||
| logic.bufferState = tmpState | |||
| logic.counterState = logic.counterBuffer | |||
| }() | |||
| logic.bufferUpdated = false | |||
| } | |||
| } | |||
| func (logic *Logic) ProcessMessage() <-chan int { | |||
| endChan := make(chan int, 1) | |||
| go func() { | |||
| logic.communication.AddPlayer(logic.playerID, logic.playerType, logic.studentType, logic.trickerType) | |||
| for logic.gameState != thuai6.GameEnd { | |||
| clientMsg := logic.communication.GetMessageToClient() | |||
| logic.gameState = thuai6.GameStateToTHUAI6[clientMsg.GameState] | |||
| switch logic.gameState { | |||
| case thuai6.GameStart: | |||
| playerGUIDs := make([]int64, 0) | |||
| for _, obj := range clientMsg.ObjMessage { | |||
| switch obj.MessageOfObj.(type) { | |||
| case *(pb.MessageOfObj_StudentMessage): | |||
| playerGUIDs = append(playerGUIDs, obj.GetStudentMessage().GetGuid()) | |||
| } | |||
| } | |||
| for _, obj := range clientMsg.ObjMessage { | |||
| switch obj.MessageOfObj.(type) { | |||
| case *(pb.MessageOfObj_TrickerMessage): | |||
| playerGUIDs = append(playerGUIDs, obj.GetTrickerMessage().GetGuid()) | |||
| } | |||
| } | |||
| logic.currentState.Guids = playerGUIDs | |||
| logic.bufferState.Guids = playerGUIDs | |||
| // TODO: LoadBuffer | |||
| break | |||
| case thuai6.GameEnd: | |||
| } | |||
| } | |||
| // TODO: ProcessMessage | |||
| endChan <- 0 | |||
| }() | |||
| return endChan | |||
| } | |||
| func (logic *Logic) Main(createAI thuai6.CreateAIFunc, ip string, port string) error { | |||
| comm, err := NewCommunication(ip, port) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| logic.communication = comm | |||
| if logic.playerType == thuai6.StudentPlayer { | |||
| logic.timer = NewStudentAPI(logic) | |||
| } else if logic.playerType == thuai6.TrickerPlayer { | |||
| logic.timer = NewTrickerAPI(logic) | |||
| } else { | |||
| return errors.New("Invalid player type") | |||
| } | |||
| aiThread := func() { | |||
| // Go 的 defer 是函数推出时执行而非 block 退出时执行,故包装一层 func | |||
| func() { | |||
| logic.mtxAI.Lock() | |||
| defer logic.mtxAI.Unlock() | |||
| for logic.aiStart == false { | |||
| logic.cvAI.Wait() | |||
| } | |||
| }() | |||
| ai := createAI(logic.playerID) | |||
| for atomic.LoadInt32(&logic.aiLoop) != 0 { | |||
| if main.Asynchronous { | |||
| logic.Wait() | |||
| } else { | |||
| logic.Update() | |||
| } | |||
| logic.timer.StartTimer() | |||
| logic.timer.Play(ai) | |||
| logic.timer.EndTimer() | |||
| } | |||
| } | |||
| if logic.TryConnection() == false { | |||
| atomic.StoreInt32(&logic.aiLoop, 0) | |||
| return errors.New("Connection failed.") | |||
| } | |||
| aiEnd := make(chan int, 1) | |||
| go func() { | |||
| aiThread() | |||
| aiEnd <- 1 | |||
| }() | |||
| <-logic.ProcessMessage() | |||
| <-aiEnd | |||
| return nil | |||
| } | |||
| @@ -0,0 +1,13 @@ | |||
| module API | |||
| go 1.18 | |||
| require ( | |||
| github.com/golang/protobuf v1.5.2 // indirect | |||
| golang.org/x/net v0.8.0 // indirect | |||
| golang.org/x/sys v0.6.0 // indirect | |||
| golang.org/x/text v0.8.0 // indirect | |||
| google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect | |||
| google.golang.org/grpc v1.54.0 // indirect | |||
| google.golang.org/protobuf v1.30.0 // indirect | |||
| ) | |||
| @@ -0,0 +1,19 @@ | |||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | |||
| github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= | |||
| github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= | |||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | |||
| golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= | |||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | |||
| golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | |||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | |||
| golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= | |||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | |||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | |||
| google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= | |||
| google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= | |||
| google.golang.org/grpc v1.54.0 h1:EhTqbhiYeixwWQtAEZAxmV9MGqcjEU2mFx52xCzNyag= | |||
| google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= | |||
| google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | |||
| google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | |||
| google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= | |||
| google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= | |||
| @@ -0,0 +1,18 @@ | |||
| package main | |||
| import ( | |||
| "fmt" | |||
| "log" | |||
| ) | |||
| var ch = make(chan int, 1) | |||
| func Func() { | |||
| log.Println(0) | |||
| ch <- 1 | |||
| log.Fatalln(1) | |||
| } | |||
| func main() { | |||
| fmt.Println("THUAI6") | |||
| } | |||
| @@ -0,0 +1,3 @@ | |||
| #!/usr/bin/env bash | |||
| ../../../dependency/proto/go_output.sh && go get | |||
| @@ -0,0 +1,14 @@ | |||
| package thuai6 | |||
| type IGameTimer interface { | |||
| StartTimer() | |||
| EndTimer() | |||
| Play(ai IAI) | |||
| } | |||
| type IAI interface { | |||
| StudentPlay(api IStudentAPI) | |||
| TrickerPlay(api ITrickerAPI) | |||
| } | |||
| type CreateAIFunc = func(playerID int64) IAI | |||
| @@ -0,0 +1,15 @@ | |||
| package thuai6 | |||
| type IAPI interface { | |||
| Move(timeInMilliseconds int64, angleInRadian float64) <-chan bool | |||
| } | |||
| type IStudentAPI interface { | |||
| IAPI | |||
| GetSelfInfo() *Student | |||
| } | |||
| type ITrickerAPI interface { | |||
| IAPI | |||
| GetSelfInfo() *Tricker | |||
| } | |||
| @@ -0,0 +1,7 @@ | |||
| package thuai6 | |||
| type State struct { | |||
| StudentSelf *Student | |||
| TrickerSelf *Tricker | |||
| Guids []int64 | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package thuai6 | |||
| type GameState int8 | |||
| const ( | |||
| NullGameState GameState = 0 | |||
| GameStart GameState = 1 | |||
| GameRunning GameState = 2 | |||
| GameEnd GameState = 3 | |||
| ) | |||
| type PlayerType int8 | |||
| const ( | |||
| NullPlayerType PlayerType = 0 | |||
| StudentPlayer PlayerType = 1 | |||
| TrickerPlayer PlayerType = 2 | |||
| ) | |||
| type StudentType int8 | |||
| const ( | |||
| NullStudentType StudentType = 0 | |||
| Athlete StudentType = 1 | |||
| Teacher StudentType = 2 | |||
| StraightAStudent StudentType = 3 | |||
| Robot StudentType = 4 | |||
| TechOtaku StudentType = 5 | |||
| Sunshine StudentType = 6 | |||
| ) | |||
| type TrickerType int8 | |||
| const ( | |||
| NullTrickerType TrickerType = 0 | |||
| Assassin TrickerType = 1 | |||
| Klee TrickerType = 2 | |||
| ANoisyPerson TrickerType = 3 | |||
| Idol TrickerType = 4 | |||
| ) | |||
| type Player struct { | |||
| x int32 | |||
| y int32 | |||
| } | |||
| type Student struct { | |||
| Player | |||
| determination int32 | |||
| } | |||
| type Tricker struct { | |||
| Player | |||
| trickDesire float64 | |||
| } | |||
| @@ -0,0 +1,38 @@ | |||
| package thuai6 | |||
| import ( | |||
| pb "API/proto" | |||
| ) | |||
| var ( | |||
| PlayerTypeToProto map[PlayerType]pb.PlayerType = map[PlayerType]pb.PlayerType{ | |||
| NullPlayerType: pb.PlayerType_NULL_PLAYER_TYPE, | |||
| StudentPlayer: pb.PlayerType_STUDENT_PLAYER, | |||
| TrickerPlayer: pb.PlayerType_TRICKER_PLAYER, | |||
| } | |||
| StudentTypeToProto map[StudentType]pb.StudentType = map[StudentType]pb.StudentType{ | |||
| NullStudentType: pb.StudentType_NULL_STUDENT_TYPE, | |||
| Athlete: pb.StudentType_ATHLETE, | |||
| Teacher: pb.StudentType_TEACHER, | |||
| StraightAStudent: pb.StudentType_STRAIGHT_A_STUDENT, | |||
| Robot: pb.StudentType_ROBOT, | |||
| TechOtaku: pb.StudentType_TECH_OTAKU, | |||
| Sunshine: pb.StudentType_SUNSHINE, | |||
| } | |||
| TrickerTypeToProto map[TrickerType]pb.TrickerType = map[TrickerType]pb.TrickerType{ | |||
| NullTrickerType: pb.TrickerType_NULL_TRICKER_TYPE, | |||
| Assassin: pb.TrickerType_ASSASSIN, | |||
| Klee: pb.TrickerType_KLEE, | |||
| ANoisyPerson: pb.TrickerType_A_NOISY_PERSON, | |||
| Idol: pb.TrickerType_IDOL, | |||
| } | |||
| GameStateToTHUAI6 map[pb.GameState]GameState = map[pb.GameState]GameState{ | |||
| pb.GameState_NULL_GAME_STATE: NullGameState, | |||
| pb.GameState_GAME_START: GameStart, | |||
| pb.GameState_GAME_RUNNING: GameRunning, | |||
| pb.GameState_GAME_END: GameEnd, | |||
| } | |||
| ) | |||
| @@ -0,0 +1,15 @@ | |||
| # experimental/CAPI/go | |||
| ## 简介 | |||
| 实验性选手 Go 接口 | |||
| ## 运行方法 | |||
| **注意事项**:Visual Studio Code 的 Go 语言提示**必须**以 `main` package 所在的目录为根目录,否则可能无法进行正确的代码提示。因此以下操作的根目录均为 `/CPI/go/API/` | |||
| ```bash | |||
| $ ./shell/init.sh | |||
| $ go build | |||
| $ ./API | |||
| ``` | |||
| @@ -0,0 +1,9 @@ | |||
| # experimental | |||
| ## 简介 | |||
| 进行实验性功能的探索 | |||
| ## 注意事项 | |||
| 本文件夹内的文件采用某历史版本,与新近版本未必一致。 | |||
| @@ -0,0 +1,3 @@ | |||
| # experimental/dependency | |||
| 实验性依赖文件 | |||
| @@ -0,0 +1,204 @@ | |||
| // Message2Client | |||
| syntax = "proto3"; | |||
| package protobuf; | |||
| import "MessageType.proto"; | |||
| message MessageOfStudent | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| int32 speed = 3; | |||
| int32 determination = 4; // 剩余的学习毅力,相当于血量 | |||
| int32 addiction = 5; // 沉迷程度,相当于淘汰进度 | |||
| repeated double time_until_skill_available = 6; | |||
| PlaceType place = 7; | |||
| repeated PropType prop = 8; | |||
| PlayerState player_state = 9; | |||
| int64 guid = 10; | |||
| BulletType bullet_type = 12; | |||
| int32 learning_speed = 13; // 修理电机的速度 | |||
| int32 treat_speed = 14; // 治疗的速度 | |||
| int64 player_id = 15; | |||
| int32 view_range = 16; // 视野距离 | |||
| int32 radius = 17; // 半径 | |||
| double danger_alert = 19; // 危险警报,在捣蛋鬼靠近时会有预警 | |||
| int32 score = 20; | |||
| int32 treat_progress = 21; // 治疗进度 | |||
| int32 rescue_progress = 22; // 救援进度 | |||
| StudentType student_type = 23; | |||
| double facing_direction = 24; | |||
| repeated StudentBuffType buff = 25; | |||
| } | |||
| message MessageOfTricker | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| int32 speed = 3; | |||
| repeated double time_until_skill_available = 5; | |||
| PlaceType place = 6; | |||
| repeated PropType prop = 7; | |||
| TrickerType tricker_type = 8; | |||
| int64 guid = 9; | |||
| int32 score = 10; | |||
| int64 player_id = 11; | |||
| int32 view_range = 12; // 视野距离 | |||
| int32 radius = 13; // 半径 | |||
| PlayerState player_state = 14; | |||
| double trick_desire = 15;//bgm | |||
| double class_volume = 16;//bgm | |||
| double facing_direction = 17; | |||
| BulletType bullet_type = 18; | |||
| repeated TrickerBuffType buff = 19; | |||
| } | |||
| message MessageOfBullet | |||
| { | |||
| BulletType type = 1; | |||
| int32 x = 2; | |||
| int32 y = 3; | |||
| double facing_direction = 4; | |||
| int64 guid = 5; | |||
| PlayerType team = 6; | |||
| PlaceType place = 7; | |||
| double bomb_range = 8; | |||
| int32 speed = 9; | |||
| } | |||
| message MessageOfBombedBullet //for Unity,直接继承自THUAI5 | |||
| { | |||
| BulletType type = 1; | |||
| int32 x = 2; | |||
| int32 y = 3; | |||
| double facing_direction = 4; | |||
| int64 mapping_id = 5; | |||
| double bomb_range = 6; | |||
| } | |||
| message MessageOfProp // 可拾取道具的信息 | |||
| { | |||
| PropType type = 1; | |||
| int32 x = 2; | |||
| int32 y = 3; | |||
| double facing_direction = 4; | |||
| int64 guid = 5; | |||
| PlaceType place = 6; | |||
| } | |||
| message MessageOfPickedProp //for Unity,直接继承自THUAI5 | |||
| { | |||
| PropType type = 1; | |||
| int32 x = 2; | |||
| int32 y = 3; | |||
| double facing_direction = 4; | |||
| int64 mapping_id = 5; | |||
| } | |||
| message MessageOfClassroom | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| int32 progress = 3; | |||
| } | |||
| message MessageOfGate | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| int32 progress = 3; | |||
| } | |||
| message MessageOfHiddenGate | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| bool opened = 3; | |||
| } | |||
| message MessageOfDoor | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| bool is_open = 3; | |||
| int32 progress = 4; | |||
| } | |||
| message MessageOfChest | |||
| { | |||
| int32 x = 1; | |||
| int32 y = 2; | |||
| int32 progress = 3; | |||
| } | |||
| message MessageOfMap | |||
| { | |||
| message Row | |||
| { | |||
| repeated PlaceType col = 1; | |||
| } | |||
| repeated Row row = 2; | |||
| } | |||
| message MessageOfNews | |||
| { | |||
| string news = 1; | |||
| int64 from_id = 2; | |||
| int64 to_id = 3; | |||
| } | |||
| message MessageOfObj | |||
| { | |||
| oneof message_of_obj | |||
| { | |||
| MessageOfStudent student_message = 1; | |||
| MessageOfTricker tricker_message = 2; | |||
| MessageOfProp prop_message = 3; | |||
| MessageOfBullet bullet_message = 4; | |||
| MessageOfBombedBullet bombed_bullet_message = 5; | |||
| MessageOfClassroom classroom_message = 6; | |||
| MessageOfDoor door_message = 7; | |||
| MessageOfGate gate_message = 8; | |||
| MessageOfChest chest_message = 9; | |||
| MessageOfHiddenGate hidden_gate_message = 10; | |||
| MessageOfNews news_message = 11; | |||
| MessageOfMap map_message = 12; | |||
| } | |||
| } | |||
| message MessageOfAll | |||
| { | |||
| int32 game_time = 1; | |||
| int32 subject_finished = 2; // 完成的科目数 | |||
| int32 student_graduated = 3; // 已经毕业的学生数 | |||
| int32 student_quited = 4; // 已经退学的学生数 | |||
| int32 student_score = 5; | |||
| int32 tricker_score = 6; | |||
| } | |||
| message MessageToClient | |||
| { | |||
| repeated MessageOfObj obj_message = 1; | |||
| GameState game_state = 2; | |||
| MessageOfAll all_message = 3; | |||
| } | |||
| message MoveRes // 如果打算设计撞墙保留平行速度分量,且需要返回值则可用这个(大概没啥用) | |||
| { | |||
| int64 actual_speed = 1; | |||
| double actual_angle = 2; | |||
| bool act_success = 3; | |||
| } | |||
| message BoolRes // 用于只需要判断执行操作是否成功的行为,如捡起道具、使用道具 | |||
| { | |||
| bool act_success = 1; | |||
| } | |||
| // message MsgRes // 用于获取队友发来的消息 | |||
| // { | |||
| // bool have_message = 1; // 是否有待接收的消息 | |||
| // int64 from_player_id = 2; | |||
| // string message_received = 3; | |||
| // } | |||
| @@ -0,0 +1,74 @@ | |||
| // Message2Server | |||
| syntax = "proto3"; | |||
| package protobuf; | |||
| import "MessageType.proto"; | |||
| message PlayerMsg | |||
| { | |||
| int64 player_id = 1; | |||
| oneof job_type | |||
| { | |||
| StudentType student_type = 2; | |||
| TrickerType tricker_type = 3; | |||
| } | |||
| PlayerType player_type = 4; | |||
| } | |||
| message MoveMsg | |||
| { | |||
| int64 player_id = 1; | |||
| double angle = 2; | |||
| int64 time_in_milliseconds = 3; | |||
| } | |||
| message PropMsg | |||
| { | |||
| int64 player_id = 1; | |||
| PropType prop_type = 2; | |||
| } | |||
| message SendMsg | |||
| { | |||
| int64 player_id = 1; | |||
| int64 to_player_id = 2; | |||
| string message = 3; | |||
| } | |||
| message AttackMsg // 相当于攻击 | |||
| { | |||
| int64 player_id = 1; | |||
| double angle = 2; | |||
| } | |||
| message IDMsg | |||
| { | |||
| int64 player_id = 1; | |||
| } | |||
| message TreatAndRescueMsg | |||
| { | |||
| int64 player_id = 1; | |||
| int64 to_player_id = 2; | |||
| } | |||
| message SkillMsg | |||
| { | |||
| int64 player_id = 1; | |||
| int32 skill_id = 2; | |||
| } | |||
| // 基本继承于THUAI5,为了使发送的信息尽可能不被浪费,暂定不发这类大包。 | |||
| // message MessageToServer | |||
| // { | |||
| // MessageType messageType = 1; | |||
| // int64 playerID = 2; // 消息发送者的playerID | |||
| // PlayerType playerType = 3; | |||
| // HumanType humanType= 4; | |||
| // TrickerType trickerType = 5; | |||
| // double angle = 6; // messageType为Move, Attack时的角度 | |||
| // PropType propType = 7; // messageType为PickProp时要捡起的道具类型,防止多个道具堆叠时出现问题 | |||
| // int64 timeInMilliseconds = 8;//时间参数 | |||
| // int64 ToPlayerID = 9; // messageType为Send时要发送的对象的ID | |||
| // string message = 10; // messageType为Send时发送的消息内容 | |||
| // } | |||
| @@ -0,0 +1,132 @@ | |||
| // MessageType | |||
| syntax = "proto3"; | |||
| package protobuf; | |||
| enum BulletType | |||
| { | |||
| NULL_BULLET_TYPE = 0; | |||
| FLYING_KNIFE = 1; | |||
| COMMON_ATTACK_OF_TRICKER = 2; | |||
| BOMB_BOMB = 3; | |||
| JUMPY_DUMPTY = 4; | |||
| ATOM_BOMB = 5; | |||
| } | |||
| enum PlaceType // 地图中的所有物件类型 | |||
| { | |||
| NULL_PLACE_TYPE = 0; | |||
| // 地图情况,其中Gate是总体的大门,HiddenGate是地窖 | |||
| LAND = 1; | |||
| WALL = 2; | |||
| GRASS = 3; | |||
| CLASSROOM = 4; | |||
| GATE = 5; | |||
| HIDDEN_GATE = 6; | |||
| WINDOW = 7; | |||
| DOOR3 = 8; | |||
| DOOR5 = 9; | |||
| DOOR6 = 10; | |||
| CHEST = 11; | |||
| // 待补充有特殊效果的地形 | |||
| } | |||
| enum ShapeType // 形状类型 | |||
| { | |||
| NULL_SHAPE_TYPE = 0; | |||
| CIRCLE = 1; // 人类、屠夫、可拾取道具等为圆形 | |||
| SQUARE = 2; // 地形均为方形 | |||
| } | |||
| enum PropType // 地图中的可拾取道具类型 | |||
| { | |||
| NULL_PROP_TYPE = 0; | |||
| ADD_SPEED = 1; | |||
| ADD_LIFE_OR_CLAIRAUDIENCE = 2; | |||
| ADD_HP_OR_AP = 3; | |||
| SHIELD_OR_SPEAR = 4; | |||
| KEY3 = 5; | |||
| KEY5 = 6; | |||
| KEY6 = 7; | |||
| RECOVERY_FROM_DIZZINESS = 8; | |||
| } | |||
| enum StudentBuffType // 人类可用的增益效果类型 | |||
| { | |||
| NULL_SBUFF_TYPE = 0; | |||
| STUDENT_ADD_SPEED = 1; | |||
| ADD_LIFE = 2; | |||
| SHIELD = 3; | |||
| STUDENT_INVISIBLE = 4; | |||
| } | |||
| enum PlayerState | |||
| { | |||
| NULL_STATUS = 0; | |||
| IDLE = 1; // 正常状态 | |||
| LEARNING = 2; // 学习状态,相当于在修机器 | |||
| ADDICTED = 3; // 血条归零后原地沉迷游戏 | |||
| QUIT = 4; // 退学状态,相当于寄了 | |||
| GRADUATED = 5; // 毕业状态,相当于逃脱了 | |||
| TREATED = 6; | |||
| RESCUED = 7; | |||
| STUNNED = 8; | |||
| TREATING = 9; | |||
| RESCUING = 10; | |||
| SWINGING = 11; // 后摇 | |||
| ATTACKING = 12; // 前摇 | |||
| LOCKING = 13; | |||
| RUMMAGING = 14; | |||
| CLIMBING = 15; // 翻窗 | |||
| OPENING_A_CHEST =16; | |||
| USING_SPECIAL_SKILL = 17; | |||
| OPENING_A_GATE =18; | |||
| } | |||
| enum TrickerBuffType // 屠夫可用的增益效果类型 | |||
| { | |||
| NULL_TBUFF_TYPE = 0; | |||
| TRICKER_ADD_SPEED = 1; | |||
| SPEAR = 2; | |||
| ADD_AP = 3; | |||
| CLAIRAUDIENCE = 4; | |||
| TRICKER_INVISIBLE = 5; | |||
| } | |||
| // 特别说明:由于Student阵营和Tricker阵营有显著的隔离,且暂定职业、主动技能和被动效果相互绑定,故不按照THUAI5的方式区分ActiveSkillType和CharacterType,而是选择了按照阵营来给不同阵营赋予不同的职业(及技能)。 | |||
| enum PlayerType | |||
| { | |||
| NULL_PLAYER_TYPE = 0; | |||
| STUDENT_PLAYER = 1; | |||
| TRICKER_PLAYER = 2; | |||
| } | |||
| enum StudentType | |||
| { | |||
| NULL_STUDENT_TYPE = 0; | |||
| ATHLETE = 1; | |||
| TEACHER = 2; | |||
| STRAIGHT_A_STUDENT = 3; | |||
| ROBOT = 4; | |||
| TECH_OTAKU =5; | |||
| SUNSHINE = 6; | |||
| } | |||
| enum TrickerType | |||
| { | |||
| NULL_TRICKER_TYPE = 0; | |||
| ASSASSIN = 1; | |||
| KLEE = 2; | |||
| A_NOISY_PERSON = 3; | |||
| IDOL = 4; | |||
| } | |||
| // 游戏进行状态 | |||
| enum GameState | |||
| { | |||
| NULL_GAME_STATE = 0; | |||
| GAME_START = 1; | |||
| GAME_RUNNING = 2; | |||
| GAME_END = 3; | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| syntax = "proto3"; | |||
| package protobuf; | |||
| import "Message2Clients.proto"; | |||
| import "Message2Server.proto"; | |||
| service AvailableService | |||
| { | |||
| rpc TryConnection (IDMsg) returns(BoolRes); | |||
| // 游戏开局调用一次的服务 | |||
| rpc AddPlayer (PlayerMsg) returns(stream MessageToClient); // 连接上后等待游戏开始,server会定时通过该服务向所有client发送消息。 | |||
| // 游戏过程中玩家执行操作的服务 | |||
| rpc Move (MoveMsg) returns (MoveRes); | |||
| rpc PickProp (PropMsg) returns (BoolRes); | |||
| rpc UseProp (PropMsg) returns (BoolRes); | |||
| rpc ThrowProp (PropMsg) returns (BoolRes); | |||
| rpc UseSkill (SkillMsg) returns (BoolRes); | |||
| rpc SendMessage (SendMsg) returns (BoolRes); | |||
| // rpc GetMessage (IDMsg) returns (stream MsgRes); | |||
| rpc StartLearning (IDMsg) returns (BoolRes); // 开始修理机器 | |||
| rpc StartRescueMate (TreatAndRescueMsg) returns (BoolRes); // 开始救人 | |||
| rpc StartTreatMate (TreatAndRescueMsg) returns (BoolRes); // 开始治疗 | |||
| rpc Attack (AttackMsg) returns (BoolRes); // 攻击 | |||
| rpc Graduate (IDMsg) returns (BoolRes); // 相当于逃跑 | |||
| rpc OpenDoor (IDMsg) returns (BoolRes); // 开门 | |||
| rpc CloseDoor (IDMsg) returns (BoolRes); // 关门 | |||
| rpc SkipWindow (IDMsg) returns (BoolRes); // 窗户 | |||
| rpc StartOpenGate (IDMsg) returns (BoolRes); // 开闸门 | |||
| rpc StartOpenChest (IDMsg) returns (BoolRes); | |||
| rpc EndAllAction (IDMsg) returns (BoolRes); // 结束所有动作 | |||
| } | |||
| @@ -0,0 +1,10 @@ | |||
| version: v1 | |||
| managed: | |||
| enabled: true | |||
| go_package_prefix: | |||
| default: proto | |||
| plugins: | |||
| - name: gotag | |||
| out: . | |||
| opt: | |||
| - auto=bson | |||
| @@ -0,0 +1,14 @@ | |||
| version: v1 | |||
| managed: | |||
| enabled: true | |||
| go_package_prefix: | |||
| default: job-API/proto | |||
| plugins: | |||
| - name: go | |||
| out: proto | |||
| opt: paths=source_relative | |||
| - name: go-grpc | |||
| out: proto | |||
| opt: | |||
| - paths=source_relative | |||
| - require_unimplemented_servers=false | |||
| @@ -0,0 +1,10 @@ | |||
| #!/usr/bin/env bash | |||
| set -e | |||
| PROTO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)" | |||
| pushd "${PROTO_DIR}" | |||
| buf generate | |||
| rm -rf ../../CAPI/go/API/proto | |||
| mv -f proto ../../CAPI/go/API | |||
| popd | |||