diff --git a/dependency/proto/Message2Clients.proto b/dependency/proto/Message2Clients.proto index 5f13f80..59adc12 100755 --- a/dependency/proto/Message2Clients.proto +++ b/dependency/proto/Message2Clients.proto @@ -13,7 +13,7 @@ message MessageOfStudent int32 fail_num = 5; // 挂科的科目数 double time_until_skill_available = 6; PlaceType place = 7; - PropType prop = 8; + repeated PropType prop = 8; StudentType student_type = 9; int64 guid = 10; StudentState state = 11; @@ -34,7 +34,7 @@ message MessageOfTricker int32 damage = 4; // 对学生造成的心理伤害 double time_until_skill_available = 5; PlaceType place = 6; - PropType prop = 7; + repeated PropType prop = 7; TrickerType tricker_type = 8; int64 guid = 9; bool movable = 10; // 是否进入了攻击后摇 @@ -78,6 +78,7 @@ message MessageOfProp // 可拾取道具的信息 bool is_moving = 8; } + message MessageOfPickedProp //for Unity,直接继承自THUAI5 { PropType type = 1; @@ -87,6 +88,37 @@ message MessageOfPickedProp //for Unity,直接继承自THUAI5 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 MessageOfDoor +{ + int32 x = 1; + int32 y = 2; + bool is_open = 3; +} + +message MessageOfMapObj +{ + oneof message_of_map_obj + { + MessageOfClassroom classroom_message = 1; + MessageOfDoor door_message = 2; + MessageOfGate gate_message = 3; + } +} + message MessageOfMap { message Row @@ -94,17 +126,27 @@ message MessageOfMap repeated PlaceType col = 1; } repeated Row row = 2; + repeated MessageOfMapObj map_obj_message = 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; + } } message MessageToClient { - repeated MessageOfStudent student_message = 1; - repeated MessageOfTricker tricker_message = 2; // 是否真正repeated待定 - repeated MessageOfProp prop_message = 3; - repeated MessageOfBullet bullet_message = 4; - repeated MessageOfBombedBullet bombed_bullet_message = 5; - MessageOfMap map_message = 6; - GameState game_state = 7; + repeated MessageOfObj obj_message = 1; + MessageOfMap map_message = 2; + GameState game_state = 3; + int32 finished_num = 4; // 完成的科目数 } message MoveRes // 如果打算设计撞墙保留平行速度分量,且需要返回值则可用这个(大概没啥用) diff --git a/dependency/proto/Message2Server.proto b/dependency/proto/Message2Server.proto index c3fcda3..eb748ea 100755 --- a/dependency/proto/Message2Server.proto +++ b/dependency/proto/Message2Server.proto @@ -22,7 +22,7 @@ message MoveMsg int64 time_in_milliseconds = 3; } -message PickMsg +message PropMsg { int64 player_id = 1; PropType prop_type = 2; diff --git a/dependency/proto/MessageType.proto b/dependency/proto/MessageType.proto index d289639..b2ac017 100755 --- a/dependency/proto/MessageType.proto +++ b/dependency/proto/MessageType.proto @@ -23,6 +23,8 @@ enum PlaceType // 地图中的所有物件类型 CLASSROOM = 4; GATE = 5; HIDDEN_GATE = 6; + WINDOW = 7; + DOOR = 8; // 待补充有特殊效果的地形 } @@ -109,6 +111,7 @@ enum GameState { NULL_GAME_STATE = 0; GAME_START = 1; - GAME_RUNNING = 2; - GAME_END = 3; + STAGE_1 = 2; // 第一阶段:大门没有开 + STAGE_2 = 3; // 第二阶段:大门已经开了 + GAME_END = 4; } diff --git a/dependency/proto/Services.proto b/dependency/proto/Services.proto index 3b00e01..798f698 100755 --- a/dependency/proto/Services.proto +++ b/dependency/proto/Services.proto @@ -13,7 +13,7 @@ service AvailableService // 游戏过程中玩家执行操作的服务 rpc Move (MoveMsg) returns (MoveRes); - rpc PickProp (PickMsg) returns (BoolRes); + rpc PickProp (PropMsg) returns (BoolRes); rpc UseProp (IDMsg) returns (BoolRes); rpc UseSkill (IDMsg) returns (BoolRes); rpc SendMessage (SendMsg) returns (BoolRes); @@ -23,5 +23,9 @@ service AvailableService rpc StartHealMate (IDMsg) returns (BoolRes); rpc Trick (TrickMsg) 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 EndAllAction (IDMsg) returns (BoolRes); // 结束所有动作 } \ No newline at end of file diff --git a/logic/ClientTest/Program.cs b/logic/ClientTest/Program.cs index ef30dc9..3d4cf80 100644 --- a/logic/ClientTest/Program.cs +++ b/logic/ClientTest/Program.cs @@ -7,27 +7,29 @@ namespace ClientTest { public static async Task Main(string[] args) { - Channel channel = new Channel("0.0.0.0:8888", ChannelCredentials.Insecure); + Thread.Sleep(3000); + Channel channel = new Channel("127.0.0.1:8888", ChannelCredentials.Insecure); var client = new AvailableService.AvailableServiceClient(channel); PlayerMsg playerInfo = new(); - playerInfo.PlayerId = Convert.ToInt32(args[0]); - playerInfo.PlayerType = (PlayerType)Convert.ToInt32(args[1]); + playerInfo.PlayerId = 0; + playerInfo.PlayerType = PlayerType.StudentPlayer; + playerInfo.StudentType = StudentType.NullStudentType; var call = client.AddPlayer(playerInfo); while (await call.ResponseStream.MoveNext()) { var currentGameInfo = call.ResponseStream.Current; - if (playerInfo.PlayerType == PlayerType.HumanPlayer) + if (playerInfo.PlayerType == PlayerType.StudentPlayer) { - for (int i = 0; i < currentGameInfo.HumanMessage.Count; i++) + for (int i = 0; i < currentGameInfo.StudentMessage.Count; i++) { - Console.WriteLine($"Human is at ({currentGameInfo.HumanMessage[i].X}, {currentGameInfo.HumanMessage[i].Y})"); + Console.WriteLine($"Human is at ({currentGameInfo.StudentMessage[i].X}, {currentGameInfo.StudentMessage[i].Y})"); } } - if (playerInfo.PlayerType == PlayerType.ButcherPlayer) + if (playerInfo.PlayerType == PlayerType.TrickerPlayer) { - for (int i = 0; i < currentGameInfo.ButcherMessage.Count; i++) + for (int i = 0; i < currentGameInfo.TrickerMessage.Count; i++) { - Console.WriteLine($"Butcher is at ({currentGameInfo.ButcherMessage[i].X}, {currentGameInfo.ButcherMessage[i].Y})"); + Console.WriteLine($"Butcher is at ({currentGameInfo.TrickerMessage[i].X}, {currentGameInfo.TrickerMessage[i].Y})"); } } } diff --git a/logic/GameClass/GameObj/Character/Character.SkillManager.cs b/logic/GameClass/GameObj/Character/Character.SkillManager.cs index 2393340..bc6e7ad 100644 --- a/logic/GameClass/GameObj/Character/Character.SkillManager.cs +++ b/logic/GameClass/GameObj/Character/Character.SkillManager.cs @@ -100,4 +100,4 @@ namespace GameClass.GameObj Debugger.Output(this, "constructed!"); } } -} +} \ No newline at end of file diff --git a/logic/GameClass/GameObj/Character/Character.Student.cs b/logic/GameClass/GameObj/Character/Character.Student.cs index 7b6b075..601ec5b 100644 --- a/logic/GameClass/GameObj/Character/Character.Student.cs +++ b/logic/GameClass/GameObj/Character/Character.Student.cs @@ -100,4 +100,4 @@ namespace GameClass.GameObj this.fixSpeed = ((IStudent)Occupation).FixSpeed; } } -} +} \ No newline at end of file diff --git a/logic/Gaming/Game.cs b/logic/Gaming/Game.cs index 2b578ec..c9f5f4d 100644 --- a/logic/Gaming/Game.cs +++ b/logic/Gaming/Game.cs @@ -39,6 +39,7 @@ namespace Gaming XY pos = gameMap.BirthPointList[playerInitInfo.birthPointIndex]; // Console.WriteLine($"x,y: {pos.x},{pos.y}"); + Character newPlayer = (GameData.IsGhost(playerInitInfo.characterType)) ? new Ghost(pos, GameData.characterRadius, gameMap.GetPlaceType(pos), playerInitInfo.characterType) : new Student(pos, GameData.characterRadius, gameMap.GetPlaceType(pos), playerInitInfo.characterType); gameMap.GameObjLockDict[GameObjType.Character].EnterWriteLock(); try diff --git a/logic/Server/CopyInfo.cs b/logic/Server/CopyInfo.cs index f9facfa..e9e4003 100644 --- a/logic/Server/CopyInfo.cs +++ b/logic/Server/CopyInfo.cs @@ -1,187 +1,366 @@ using Protobuf; using System.Collections.Generic; using GameClass.GameObj; -/* + namespace Server { + public static class CopyInfo { - public static MessageToClient.Types.GameObjMessage? Auto(GameObj gameObj) + // 下面赋值为0的大概率是还没写完 2023-03-03 + private static MessageOfStudent Human(Character player) { - if (gameObj.Type == Preparation.Utility.GameObjType.Character) - return Player((Character)gameObj); - else if (gameObj.Type == Preparation.Utility.GameObjType.Bullet) - return Bullet((Bullet)gameObj); - else if (gameObj.Type == Preparation.Utility.GameObjType.Prop) - return Prop((Prop)gameObj); - else if (gameObj.Type == Preparation.Utility.GameObjType.BombedBullet) - return BombedBullet((BombedBullet)gameObj); - else if (gameObj.Type == Preparation.Utility.GameObjType.PickedProp) - return PickedProp((PickedProp)gameObj); - else return null; //先写着防报错 + MessageOfStudent msg = new MessageOfStudent(); + if (player.IsGhost()) return null; + + msg.X = player.Position.x; + msg.Y = player.Position.y; + msg.Speed = player.MoveSpeed; + msg.Determination = player.HP; + msg.FailNum = 0; + msg.TimeUntilSkillAvailable = 0; + //msg.Place = 0; 下面写了 + msg.StudentType = StudentType.NullStudentType; // 下面写 + msg.Guid = 0; + msg.State = StudentState.NullStatus; + msg.FailTime = 0; + msg.EmoTime = 0; + msg.PlayerId = 0; + msg.ViewRange = 0; + msg.Radius = 0; + //msg.Buff[0] = StudentBuffType.NullSbuffType; 下面写了 + + /* THUAI5中的内容 + msg.BulletNum = player.BulletNum; + msg.CanMove = player.CanMove; + msg.CD = player.CD; + msg.GemNum = player.GemNum; + msg.Guid = player.ID; + msg.IsResetting = player.IsResetting; + + msg.LifeNum = player.DeathCount + 1; + msg.Radius = player.Radius; + + msg.TimeUntilCommonSkillAvailable = player.TimeUntilCommonSkillAvailable; + msg.TeamID = player.TeamID; + msg.PlayerID = player.PlayerID; + msg.IsInvisible = player.IsInvisible; + msg.FacingDirection = player.FacingDirection; + + //应该要发队伍分数,这里先发个人分数 + msg.MessageOfHuman.Score = player.Score; + + //这条暂时没啥用 + msg.MessageOfHuman.TimeUntilUltimateSkillAvailable = 0; + + msg.MessageOfHuman.Vampire = player.Vampire;*/ + + foreach (KeyValuePair kvp in player.Buff) + { + if (kvp.Value) + { + switch (kvp.Key) // StudentBuffType具体内容待定 + { + case Preparation.Utility.BuffType.Spear: + msg.Buff.Add(StudentBuffType.NullSbuffType); + break; + case Preparation.Utility.BuffType.AddLIFE: + msg.Buff.Add(StudentBuffType.NullSbuffType); + break; + case Preparation.Utility.BuffType.Shield: + msg.Buff.Add(StudentBuffType.NullSbuffType); + break; + case Preparation.Utility.BuffType.AddSpeed: + msg.Buff.Add(StudentBuffType.NullSbuffType); + break; + default: + break; + } + } + } + /*switch (player.Place) + { + case Preparation.Utility.PlaceType.Land: + msg.Place = PlaceType.Land; + break; + case Preparation.Utility.PlaceType.Grass1: + msg.Place = PlaceType.Grass; + break; + case Preparation.Utility.PlaceType.Grass2: + msg.Place = PlaceType.Grass; + break; + case Preparation.Utility.PlaceType.Grass3: + msg.Place = PlaceType.Grass; + break; + // case Preparation.Utility.PlaceType.Invisible: + // msg.MessageOfHuman.Place = Communication.Proto.PlaceType.Invisible; + // break; + default: + msg.Place = PlaceType.NullPlaceType; + break; + }*/ + + //Character的储存方式可能得改,用enum type存道具和子弹,不应该用对象 + //现在懒得改了,有时间再重整一波 + /*if (player.PropInventory == null) + msg.Prop = PropType.NullPropType; + else + { + switch (player.PropInventory.GetPropType()) + { + case Preparation.Utility.PropType.Gem: + msg.Prop = PropType.NullPropType; + break; + case Preparation.Utility.PropType.addLIFE: + msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; + break; + case Preparation.Utility.PropType.addSpeed: + msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; + break; + case Preparation.Utility.PropType.Shield: + msg.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; + break; + case Preparation.Utility.PropType.Spear: + msg.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; + break; + default: + msg.Prop = PropType.NullPropType; + break; + } + }*/ + /*switch (player.PassiveSkillType) 需要对接一下,proto里似乎没有这个 + { + case Preparation.Utility.PassiveSkillType.RecoverAfterBattle: + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; + break; + case Preparation.Utility.PassiveSkillType.SpeedUpWhenLeavingGrass: + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; + break; + case Preparation.Utility.PassiveSkillType.Vampire: + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; + break; + default: + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; + break; + } + + switch (player.CommonSkillType) + { + case Preparation.Utility.ActiveSkillType.BecomeAssassin: + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; + break; + case Preparation.Utility.ActiveSkillType.BecomeVampire: + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; + break; + case Preparation.Utility.ActiveSkillType.NuclearWeapon: + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; + break; + case Preparation.Utility.ActiveSkillType.SuperFast: + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; + break; + default: + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; + break; + } + + switch (player.BulletOfPlayer) + { + case Preparation.Utility.BulletType.AtomBomb: + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.AtomBomb; + break; + case Preparation.Utility.BulletType.OrdinaryBullet: + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.OrdinaryBullet; + break; + case Preparation.Utility.BulletType.FastBullet: + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.FastBullet; + break; + case Preparation.Utility.BulletType.LineBullet: + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.LineBullet; + break; + default: + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.NullBulletType; + break; + }*/ + + return msg; } - private static MessageToClient.Types.GameObjMessage Player(Character player) + private static MessageOfTricker Butcher(Character player) { - MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); - msg.MessageOfCharacter = new MessageOfCharacter(); + MessageOfTricker msg = new MessageOfTricker(); + if (!player.IsGhost()) return null; + + msg.X = player.Position.x; + msg.Y = player.Position.y; + msg.Speed = player.MoveSpeed; + msg.Damage = 0; + msg.TimeUntilSkillAvailable = 0; + //msg.Place = 0; 下面写了 + //msg.Prop = PropType.NullPropType; // 下面写 + msg.TrickerType = TrickerType.NullTrickerType; // 下面写 + msg.Guid = 0; + msg.Movable = false; + msg.PlayerId = 0; + msg.ViewRange = 0; + msg.Radius = 0; + //msg.Buff[0] = ButcherBuffType.NullSbuffType; 下面写了 - msg.MessageOfCharacter.X = player.Position.x; - msg.MessageOfCharacter.Y = player.Position.y; - msg.MessageOfCharacter.AttackRange = player.AttackRange; - msg.MessageOfCharacter.BulletNum = player.BulletNum; - msg.MessageOfCharacter.CanMove = player.CanMove; - msg.MessageOfCharacter.CD = player.CD; - msg.MessageOfCharacter.GemNum = player.GemNum; - msg.MessageOfCharacter.Guid = player.ID; - msg.MessageOfCharacter.IsResetting = player.IsResetting; - msg.MessageOfCharacter.Life = player.HP; - msg.MessageOfCharacter.LifeNum = player.DeathCount + 1; - msg.MessageOfCharacter.Radius = player.Radius; - msg.MessageOfCharacter.Speed = player.MoveSpeed; - msg.MessageOfCharacter.TimeUntilCommonSkillAvailable = player.TimeUntilCommonSkillAvailable; - msg.MessageOfCharacter.TeamID = player.TeamID; - msg.MessageOfCharacter.PlayerID = player.PlayerID; - msg.MessageOfCharacter.IsInvisible = player.IsInvisible; - msg.MessageOfCharacter.FacingDirection = player.FacingDirection; + /* THUAI5中的内容 + msg.BulletNum = player.BulletNum; + msg.CanMove = player.CanMove; + msg.CD = player.CD; + msg.GemNum = player.GemNum; + msg.Guid = player.ID; + msg.IsResetting = player.IsResetting; + + msg.LifeNum = player.DeathCount + 1; + msg.Radius = player.Radius; + + msg.TimeUntilCommonSkillAvailable = player.TimeUntilCommonSkillAvailable; + msg.TeamID = player.TeamID; + msg.PlayerID = player.PlayerID; + msg.IsInvisible = player.IsInvisible; + msg.FacingDirection = player.FacingDirection; //应该要发队伍分数,这里先发个人分数 - msg.MessageOfCharacter.Score = player.Score; + msg.MessageOfHuman.Score = player.Score; //这条暂时没啥用 - msg.MessageOfCharacter.TimeUntilUltimateSkillAvailable = 0; + msg.MessageOfHuman.TimeUntilUltimateSkillAvailable = 0; + + msg.MessageOfHuman.Vampire = player.Vampire;*/ - msg.MessageOfCharacter.Vampire = player.Vampire; foreach (KeyValuePair kvp in player.Buff) { if (kvp.Value) { - switch(kvp.Key) + switch (kvp.Key) // ButcherBuffType具体内容待定 { case Preparation.Utility.BuffType.Spear: - msg.MessageOfCharacter.Buff.Add(BuffType.SpearBuff); + msg.Buff.Add(TrickerBuffType.NullTbuffType); break; case Preparation.Utility.BuffType.AddLIFE: - msg.MessageOfCharacter.Buff.Add(BuffType.AddLife); + msg.Buff.Add(TrickerBuffType.NullTbuffType); break; case Preparation.Utility.BuffType.Shield: - msg.MessageOfCharacter.Buff.Add(BuffType.ShieldBuff); + msg.Buff.Add(TrickerBuffType.NullTbuffType); break; case Preparation.Utility.BuffType.AddSpeed: - msg.MessageOfCharacter.Buff.Add(BuffType.MoveSpeed); + msg.Buff.Add(TrickerBuffType.NullTbuffType); break; default: break; } } } - switch (player.Place) + /*switch (player.Place) { - case Preparation.Utility.PlaceType.Null: - msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Null; + case Preparation.Utility.PlaceType.Land: + msg.Place = PlaceType.Land; break; - case Preparation.Utility.PlaceType.Grass: - msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass; + case Preparation.Utility.PlaceType.Grass1: + msg.Place = PlaceType.Grass; break; - case Preparation.Utility.PlaceType.Grass: - msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass; + case Preparation.Utility.PlaceType.Grass2: + msg.Place = PlaceType.Grass; break; - case Preparation.Utility.PlaceType.Grass: - msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass; + case Preparation.Utility.PlaceType.Grass3: + msg.Place = PlaceType.Grass; break; - // case Preparation.Utility.PlacccceType.Invisible: - // msg.MessageOfCharacter.Place = Communication.Proto.PlacccceType.Invisible; - // break; + // case Preparation.Utility.PlaceType.Invisible: + // msg.MessageOfHuman.Place = Communication.Proto.PlaceType.Invisible; + // break; default: - msg.MessageOfCharacter.Place = Communication.Proto.PlacccceType.NullPlaceType; + msg.Place = PlaceType.NullPlaceType; break; - } + }*/ //Character的储存方式可能得改,用enum type存道具和子弹,不应该用对象 //现在懒得改了,有时间再重整一波 - if (player.PropInventory == null) - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.NullPropType; + /*if (player.PropInventory == null) + msg.Prop = PropType.NullPropType; else { switch (player.PropInventory.GetPropType()) { case Preparation.Utility.PropType.Gem: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Gem; + msg.Prop = PropType.NullPropType; break; case Preparation.Utility.PropType.addLIFE: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.AddLife; + msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddLife; break; case Preparation.Utility.PropType.addSpeed: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.AddSpeed; + msg.MessageOfHuman.Prop = Communication.Proto.PropType.AddSpeed; break; case Preparation.Utility.PropType.Shield: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Shield; + msg.MessageOfHuman.Prop = Communication.Proto.PropType.Shield; break; case Preparation.Utility.PropType.Spear: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Spear; + msg.MessageOfHuman.Prop = Communication.Proto.PropType.Spear; break; default: - msg.MessageOfCharacter.Prop = Communication.Proto.PropType.NullPropType; + msg.Prop = PropType.NullPropType; break; } - } - switch (player.PassiveSkillType) + }*/ + /*switch (player.PassiveSkillType) 需要对接一下,proto里似乎没有这个 { case Preparation.Utility.PassiveSkillType.RecoverAfterBattle: - msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; break; case Preparation.Utility.PassiveSkillType.SpeedUpWhenLeavingGrass: - msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; break; case Preparation.Utility.PassiveSkillType.Vampire: - msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; break; default: - msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; + msg.MessageOfHuman.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; break; } + switch (player.CommonSkillType) { case Preparation.Utility.ActiveSkillType.BecomeAssassin: - msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; break; case Preparation.Utility.ActiveSkillType.BecomeVampire: - msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; break; case Preparation.Utility.ActiveSkillType.NuclearWeapon: - msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; break; case Preparation.Utility.ActiveSkillType.SuperFast: - msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; break; default: - msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; + msg.MessageOfHuman.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; break; } switch (player.BulletOfPlayer) { case Preparation.Utility.BulletType.AtomBomb: - msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.AtomBomb; + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.AtomBomb; break; case Preparation.Utility.BulletType.OrdinaryBullet: - msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.OrdinaryBullet; + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.OrdinaryBullet; break; case Preparation.Utility.BulletType.FastBullet: - msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.FastBullet; + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.FastBullet; break; case Preparation.Utility.BulletType.LineBullet: - msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.LineBullet; + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.LineBullet; break; default: - msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.NullBulletType; + msg.MessageOfHuman.BulletType = Communication.Proto.BulletType.NullBulletType; break; - } + }*/ return msg; } - private static MessageToClient.Types.GameObjMessage Bullet(Bullet bullet) + /*private static MessageToClient.Types.GameObjMessage Bullet(Bullet bullet) { MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); msg.MessageOfBullet = new MessageOfBullet(); @@ -229,39 +408,47 @@ namespace Server break; } return msg; - } + }*/ - private static MessageToClient.Types.GameObjMessage Prop(Prop prop) + private static MessageOfProp Prop(Prop prop) { - MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); - msg.MessageOfProp = new MessageOfProp(); + MessageOfProp msg = new MessageOfProp(); + //msg.Type = PropType.NullPropType; 下面写 + msg.X = prop.Position.x; + msg.Y = prop.Position.y; + msg.FacingDirection = 0; + msg.Guid = 0; + msg.Place = PlaceType.NullPlaceType; + msg.Size = 0; + msg.IsMoving = false; + /* THUAI5中的内容 msg.MessageOfProp.FacingDirection = prop.FacingDirection; msg.MessageOfProp.Guid = prop.ID; - msg.MessageOfProp.IsMoving = prop.IsMoving; + msg.MessageOfProp.IsMoving = prop.IsMoving;*/ + switch (prop.GetPropType()) { - case Preparation.Utility.PropType.Gem: - msg.MessageOfProp.Type = Communication.Proto.PropType.Gem; + /*case Preparation.Utility.PropType.Gem: + msg.Type = PropType.Gem; break; case Preparation.Utility.PropType.addLIFE: - msg.MessageOfProp.Type = Communication.Proto.PropType.AddLife; + msg.Type = PropType.AddLife; break; case Preparation.Utility.PropType.addSpeed: - msg.MessageOfProp.Type = Communication.Proto.PropType.AddSpeed; + msg.Type = PropType.AddSpeed; break; case Preparation.Utility.PropType.Shield: - msg.MessageOfProp.Type = Communication.Proto.PropType.Shield; + msg.Type = PropType.Shield; break; case Preparation.Utility.PropType.Spear: - msg.MessageOfProp.Type = Communication.Proto.PropType.Spear; - break; + msg.Type = PropType.Spear; + break;*/ default: - msg.MessageOfProp.Type = Communication.Proto.PropType.NullPropType; + msg.Type = PropType.NullPropType; break; } - msg.MessageOfProp.X = prop.Position.x; - msg.MessageOfProp.Y = prop.Position.y; - if(prop is Gem) + + /*if(prop is Gem) { msg.MessageOfProp.Size = ((Gem)prop).Size; } @@ -286,20 +473,19 @@ namespace Server default: msg.MessageOfProp.Place = Communication.Proto.PlacccceType.NullPlaceType; break; - } + }*/ return msg; } - private static MessageToClient.Types.GameObjMessage BombedBullet(BombedBullet bombedBullet) + /*private static MessageOfBombedBullet BombedBullet(BombedBullet bombedBullet) { - MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); - msg.MessageOfBombedBullet = new MessageOfBombedBullet(); + MessageOfBombedBullet msg = new MessageOfBombedBullet; - msg.MessageOfBombedBullet.FacingDirection = bombedBullet.bulletHasBombed.FacingDirection; - msg.MessageOfBombedBullet.X = bombedBullet.bulletHasBombed.Position.x; - msg.MessageOfBombedBullet.Y = bombedBullet.bulletHasBombed.Position.y; - msg.MessageOfBombedBullet.MappingID = bombedBullet.MappingID; - msg.MessageOfBombedBullet.BombRange = BulletFactory.BulletBombRange(bombedBullet.bulletHasBombed.TypeOfBullet); + msg.FacingDirection = bombedBullet.FacingDirection; + msg.X = bombedBullet.bulletHasBombed.Position.x; + msg.Y = bombedBullet.bulletHasBombed.Position.y; + msg.MappingID = bombedBullet.MappingID; + msg.BombRange = BulletFactory.BulletBombRange(bombedBullet.bulletHasBombed.TypeOfBullet); switch (bombedBullet.bulletHasBombed.TypeOfBullet) { case Preparation.Utility.BulletType.OrdinaryBullet: @@ -319,9 +505,9 @@ namespace Server break; } return msg; - } + }*/ - private static MessageToClient.Types.GameObjMessage PickedProp(PickedProp pickedProp) + /*private static MessageToClient.Types.GameObjMessage PickedProp(PickedProp pickedProp) { MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); msg.MessageOfPickedProp = new MessageOfPickedProp(); @@ -352,7 +538,6 @@ namespace Server break; } return msg; - } + }*/ } -}*/ -// 等Preparation完成再写 +} diff --git a/logic/Server/GameServer.cs b/logic/Server/GameServer.cs index 77e08de..83869d6 100644 --- a/logic/Server/GameServer.cs +++ b/logic/Server/GameServer.cs @@ -6,6 +6,8 @@ using System; using System.Net.Http.Headers; using Gaming; using GameClass.GameObj; +using Preparation.Utility; + namespace Server { @@ -13,9 +15,17 @@ namespace Server { private Dictionary semaDict = new(); private object gameLock = new(); - private const int playerNum = 2; // 注意修改 + private const int playerNum = 1; // 注意修改 private MessageToClient currentGameInfo = new(); private SemaphoreSlim endGameSem = new(0); + + object gameInfo = new(); + private int isGaming = 0; + public bool IsGaming + { + get => Interlocked.CompareExchange(ref isGaming, 0, 0) != 0; + set => Interlocked.Exchange(ref isGaming, value ? 1 : 0); + } // 以上是测试时用到的定义 protected readonly ArgumentOptions options; @@ -29,6 +39,46 @@ namespace Server private readonly Semaphore endGameInfoSema = new(0, 1); // private MessageWriter? mwr = null; + public SemaphoreSlim StartGameTest() + { + IsGaming = true; + var waitHandle = new SemaphoreSlim(0); + + new Thread + ( + () => + { + new FrameRateTaskExecutor + ( + () => IsGaming, + () => + { + lock (gameInfo) + { + /*for (int i = 0; i < gameInfo.GameObjs.Count; i++) + { + if (gameInfo.GameObjs[i].Character != null) + { + gameInfo.GameObjs[i].Character.X++; + gameInfo.GameObjs[i].Character.Y--; + } + }*/ + } + }, + 100, + () => + { + IsGaming = false; + waitHandle.Release(); + return 0; + }, + 3000//gameTime + ).Start(); + } + ) + { IsBackground = true }.Start(); + return waitHandle; + } public void StartGame() { bool gameState = game.StartGame((int)options.GameTimeInSecond * 1000); @@ -63,9 +113,7 @@ namespace Server public void ReportGame() { - // currentGameInfo = game.GetCopiedGameInfo(); - currentGameInfo.HumanMessage[0].X = 1; - currentGameInfo.ButcherMessage[0].X = 1; + //currentGameInfo = null; foreach (var kvp in semaDict) { @@ -77,13 +125,29 @@ namespace Server kvp.Value.Item2.Wait(); } } - private uint GetBirthPointIdx(long teamID, long playerID) // 获取出生点位置 + + private int PlayerTypeToTeamID(PlayerType playerType) { - return (uint)((teamID * options.PlayerCountPerTeam) + playerID); + if (playerType == PlayerType.StudentPlayer) return 0; + if (playerType == PlayerType.TrickerPlayer) return 1; + return -1; + } + private uint GetBirthPointIdx(PlayerType playerType, long playerID) // 获取出生点位置 + { + return (uint)((PlayerTypeToTeamID(playerType) * options.PlayerCountPerTeam) + playerID + 1); + } + private bool ValidPlayerTypeAndPlayerID(PlayerType playerType, long playerID) + { + if (playerType == PlayerType.StudentPlayer && 0 <= playerID && playerID < options.PlayerCountPerTeam) + return true; // 人数待修改 + if (playerType == PlayerType.TrickerPlayer && 0 <= playerID && playerID < options.PlayerCountPerTeam) + return true; + return false; } public override Task TryConnection(IDMsg request, ServerCallContext context) { + Console.WriteLine($"TryConnection ID: {request.PlayerId}"); var onConnection = new BoolRes(); lock (gameLock) { @@ -101,6 +165,7 @@ namespace Server protected readonly object addPlayerLock = new(); public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter responseStream, ServerCallContext context) { + Console.WriteLine($"AddPlayer: {request.PlayerId}"); if (request.PlayerId >= spectatorMinPlayerID && request.PlayerType == PlayerType.NullPlayerType) { // 观战模式 @@ -115,23 +180,23 @@ namespace Server if (game.GameMap.Timer.IsGaming) return; - /*if (!ValidTeamIDAndPlayerID(msg.TeamID, msg.PlayerID)) //玩家id是否正确 - return; - if (communicationToGameID[msg.TeamID, msg.PlayerID] != GameObj.invalidID) //是否已经添加了该玩家 - return false; + if (!ValidPlayerTypeAndPlayerID(request.PlayerType, request.PlayerId)) //玩家id是否正确 + return; + //if (communicationToGameID[PlayerTypeToTeamID(request.PlayerType), request.PlayerId] != GameObj.invalidID) //是否已经添加了该玩家 + //return; + + Preparation.Utility.CharacterType characterType = Preparation.Utility.CharacterType.Athlete; // 待修改 - Preparation.Utility.PassiveSkillType passiveSkill; - */ lock (addPlayerLock) { - /*Game.PlayerInitInfo playerInitInfo = new(GetBirthPointIdx(msg.TeamID, msg.PlayerID), msg.TeamID, msg.PlayerID, passiveSkill, commonSkill); + Game.PlayerInitInfo playerInitInfo = new(GetBirthPointIdx(request.PlayerType, request.PlayerId), PlayerTypeToTeamID(request.PlayerType), request.PlayerId, characterType); long newPlayerID = game.AddPlayer(playerInitInfo); - if (newPlayerID == GameObj.invalidID) - return false;*/ + //if (newPlayerID == GameObj.invalidID) + //return; // 内容待修改 var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); bool start = false; - Console.WriteLine($"Id: {request.PlayerId} joins."); + Console.WriteLine($"PlayerType: {request.PlayerType} Id: {request.PlayerId} joins."); lock (semaDict) { semaDict.Add(request.PlayerId, temp); @@ -157,29 +222,12 @@ namespace Server } while (game.GameMap.Timer.IsGaming); } - public override Task Attack(AttackMsg request, ServerCallContext context) - { - return base.Attack(request, context); - } - - public override Task CarryHuman(IDMsg request, ServerCallContext context) + public override Task Trick(TrickMsg request, ServerCallContext context) { - return base.CarryHuman(request, context); - } - - public override Task EndFixMachine(IDMsg request, ServerCallContext context) - { - return base.EndFixMachine(request, context); - } - - public override Task EndSaveHuman(IDMsg request, ServerCallContext context) - { - return base.EndSaveHuman(request, context); - } - - public override Task Escape(IDMsg request, ServerCallContext context) - { - return base.Escape(request, context); + game.Attack(request.PlayerId, request.Angle); + BoolRes boolRes = new(); + boolRes.ActSuccess = true; + return Task.FromResult(boolRes); } public override Task GetMessage(IDMsg request, IServerStreamWriter responseStream, ServerCallContext context) @@ -187,57 +235,62 @@ namespace Server return base.GetMessage(request, responseStream, context); } - public override Task HangHuman(IDMsg request, ServerCallContext context) + public override Task Move(MoveMsg request, ServerCallContext context) { - return base.HangHuman(request, context); + Console.WriteLine($"Move ID: {request.PlayerId}, TimeInMilliseconds: {request.TimeInMilliseconds}"); + game.MovePlayer(request.PlayerId, (int)request.TimeInMilliseconds, request.Angle); + // 之后game.MovePlayer可能改为bool类型 + MoveRes moveRes = new(); + moveRes.ActSuccess = true; + return Task.FromResult(moveRes); } - public override Task Move(MoveMsg request, ServerCallContext context) + public override Task PickProp(PropMsg request, ServerCallContext context) { - return base.Move(request, context); + BoolRes boolRes = new(); + if (request.PropType == Protobuf.PropType.NullPropType) + boolRes.ActSuccess = game.PickProp(request.PlayerId, Preparation.Utility.PropType.Null); + return Task.FromResult(boolRes); } - public override Task PickProp(PickMsg request, ServerCallContext context) + public override Task SendMessage(SendMsg request, ServerCallContext context) { - return base.PickProp(request, context); + return base.SendMessage(request, context); } - public override Task ReleaseHuman(IDMsg request, ServerCallContext context) + public override Task UseProp(IDMsg request, ServerCallContext context) { - return base.ReleaseHuman(request, context); + return base.UseProp(request, context); } - public override Task SendMessage(SendMsg request, ServerCallContext context) + public override Task UseSkill(IDMsg request, ServerCallContext context) { - return base.SendMessage(request, context); + return base.UseSkill(request, context); } - public override Task StartFixMachine(IDMsg request, ServerCallContext context) + public override Task Graduate(IDMsg request, ServerCallContext context) { - return base.StartFixMachine(request, context); + return base.Graduate(request, context); } - - public override Task StartSaveHuman(IDMsg request, ServerCallContext context) + public override Task StartHealMate(IDMsg request, ServerCallContext context) { - return base.StartSaveHuman(request, context); + return base.StartHealMate(request, context); } - - public override Task UseProp(IDMsg request, ServerCallContext context) + public override Task StartHelpMate(IDMsg request, ServerCallContext context) { - return base.UseProp(request, context); + return base.StartHelpMate(request, context); } - - public override Task UseSkill(IDMsg request, ServerCallContext context) + public override Task StartLearning(IDMsg request, ServerCallContext context) { - return base.UseSkill(request, context); + return base.StartLearning(request, context); } public GameServer(ArgumentOptions options) { - /*this.options = options; - if (options.mapResource == DefaultArgumentOptions.MapResource) - this.game = new Game(MapInfo.defaultMap, options.TeamCount); - else + this.options = options; + //if (options.mapResource == DefaultArgumentOptions.MapResource) + // this.game = new Game(MapInfo.defaultMap, options.TeamCount); + //else { uint[,] map = new uint[GameData.rows, GameData.cols]; try @@ -305,7 +358,7 @@ namespace Server if (options.Url != DefaultArgumentOptions.Url && options.Token != DefaultArgumentOptions.Token) { //this.httpSender = new HttpSender(options.Url, options.Token, "PUT"); - }*/ + } } } -} \ No newline at end of file +} diff --git a/logic/Server/Properties/launchSettings.json b/logic/Server/Properties/launchSettings.json new file mode 100644 index 0000000..62a6702 --- /dev/null +++ b/logic/Server/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Server": { + "commandName": "Project", + "commandLineArgs": "-p 8888" + } + } +} diff --git a/playback/Playback/MessageReader.cs b/playback/Playback/MessageReader.cs new file mode 100644 index 0000000..f4badfe --- /dev/null +++ b/playback/Playback/MessageReader.cs @@ -0,0 +1,134 @@ +using Communication.Proto; +using Google.Protobuf; +using System; +using System.IO; +using System.IO.Compression; + +namespace Playback +{ + public class FileFormatNotLegalException : Exception + { + private readonly string fileName; + public FileFormatNotLegalException(string fileName) + { + this.fileName = fileName; + } + public override string Message => $"The file: " + this.fileName + " is not a legal playback file for THUAI6."; + } + + public class MessageReader : IDisposable + { + private FileStream? fs; + private CodedInputStream cos; + private GZipStream gzs; + private byte[] buffer; + public bool Finished { get; private set; } = false; + + public readonly uint teamCount; + public readonly uint playerCount; + + const int bufferMaxSize = 1024 * 1024; // 1M + + public MessageReader(string fileName) + { + if (!fileName.EndsWith(PlayBackConstant.ExtendedName)) + { + fileName += PlayBackConstant.ExtendedName; + } + + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read); + + try + { + var prefixLen = PlayBackConstant.Prefix.Length; + byte[] bt = new byte[prefixLen + sizeof(UInt32) * 2]; + fs.Read(bt, 0, bt.Length); + for (int i = 0; i < prefixLen; ++i) + { + if (bt[i] != PlayBackConstant.Prefix[i]) throw new FileFormatNotLegalException(fileName); + } + + teamCount = BitConverter.ToUInt32(bt, prefixLen); + playerCount = BitConverter.ToUInt32(bt, prefixLen + sizeof(UInt32)); + } + catch + { + throw new FileFormatNotLegalException(fileName); + } + + gzs = new GZipStream(fs, CompressionMode.Decompress); + var tmpBuffer = new byte[bufferMaxSize]; + var bufferSize = gzs.Read(tmpBuffer); + if (bufferSize == 0) + { + buffer = tmpBuffer; + Finished = true; + } + else if (bufferSize != bufferMaxSize) // 不留空位,防止 CodedInputStream 获取信息错误 + { + if (bufferSize == 0) + { + Finished = true; + } + buffer = new byte[bufferSize]; + Array.Copy(tmpBuffer, buffer, bufferSize); + } + else + { + buffer = tmpBuffer; + } + cos = new CodedInputStream(buffer); + } + + public MessageToClient? ReadOne() + { + beginRead: + if (Finished) return null; + var pos = cos.Position; + try + { + MessageToClient? msg = new MessageToClient(); + cos.ReadMessage(msg); + return msg; + } + catch (InvalidProtocolBufferException) + { + var leftByte = buffer.Length - pos; // 上次读取剩余的字节 + for (int i = 0; i < leftByte; ++i) + { + buffer[i] = buffer[pos + i]; + } + var bufferSize = gzs.Read(buffer, (int)leftByte, (int)(buffer.Length - leftByte)) + leftByte; + if (bufferSize == leftByte) + { + Finished = true; + return null; + } + if (bufferSize != buffer.Length) // 不留空位,防止 CodedInputStream 获取信息错误 + { + var tmpBuffer = new byte[bufferSize]; + Array.Copy(buffer, tmpBuffer, bufferSize); + buffer = tmpBuffer; + } + cos = new CodedInputStream(buffer); + goto beginRead; + } + } + + public void Dispose() + { + Finished = true; + if (fs == null) + return; + if (fs.CanRead) + { + fs.Close(); + } + } + + ~MessageReader() + { + Dispose(); + } + } +} diff --git a/playback/Playback/MessageWriter.cs b/playback/Playback/MessageWriter.cs new file mode 100644 index 0000000..923a4ab --- /dev/null +++ b/playback/Playback/MessageWriter.cs @@ -0,0 +1,74 @@ +using Communication.Proto; +using Google.Protobuf; +using System; +using System.IO; +using System.IO.Compression; + +namespace Playback +{ + public class MessageWriter : IDisposable + { + private FileStream fs; + private CodedOutputStream cos; + private MemoryStream ms; + private GZipStream gzs; + private const int memoryCapacity = 1024 * 1024; // 1M + + private static void ClearMemoryStream(MemoryStream msToClear) + { + msToClear.Position = 0; + msToClear.SetLength(0); + } + + public MessageWriter(string fileName, uint teamCount, uint playerCount) + { + if (!fileName.EndsWith(PlayBackConstant.ExtendedName)) + { + fileName += PlayBackConstant.ExtendedName; + } + + fs = new FileStream(fileName, FileMode.Create, FileAccess.Write); + fs.Write(PlayBackConstant.Prefix); // 写入前缀 + + fs.Write(BitConverter.GetBytes((UInt32)teamCount)); // 写入队伍数 + fs.Write(BitConverter.GetBytes((UInt32)playerCount)); // 写入每队的玩家人数 + ms = new MemoryStream(memoryCapacity); + cos = new CodedOutputStream(ms); + gzs = new GZipStream(fs, CompressionMode.Compress); + } + + public void WriteOne(MessageToClient msg) + { + cos.WriteMessage(msg); + if (ms.Length > memoryCapacity) + Flush(); + } + + public void Flush() + { + if (fs.CanWrite) + { + cos.Flush(); + gzs.Write(ms.GetBuffer(), 0, (int)ms.Length); + ClearMemoryStream(ms); + fs.Flush(); + } + } + + public void Dispose() + { + if (fs.CanWrite) + { + Flush(); + cos.Dispose(); + gzs.Dispose(); + fs.Dispose(); + } + } + + ~MessageWriter() + { + Dispose(); + } + } +} diff --git a/playback/Playback/Playback.cs b/playback/Playback/Playback.cs deleted file mode 100644 index 658e5d6..0000000 --- a/playback/Playback/Playback.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Playback -{ - public class Playback - { - } -} \ No newline at end of file diff --git a/playback/Playback/Playback.csproj b/playback/Playback/Playback.csproj index 132c02c..70e6d49 100644 --- a/playback/Playback/Playback.csproj +++ b/playback/Playback/Playback.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;netstandard2.1 enable enable diff --git a/playback/Playback/PlaybackConstant.cs b/playback/Playback/PlaybackConstant.cs new file mode 100644 index 0000000..8c34a43 --- /dev/null +++ b/playback/Playback/PlaybackConstant.cs @@ -0,0 +1,8 @@ +namespace Playback +{ + public static class PlayBackConstant + { + public static string ExtendedName = ".thuaipb"; + public static byte[] Prefix = { (byte)'P', (byte)'B', 6, 0 }; // 文件前缀,用于标识文件类型,版本号为6 + } +}