| @@ -60,12 +60,12 @@ namespace GameClass.GameObj | |||||
| this.score = 0; | this.score = 0; | ||||
| this.propInventory = null; | this.propInventory = null; | ||||
| this.buffManeger = new BuffManeger(); | this.buffManeger = new BuffManeger(); | ||||
| IPassiveSkill pSkill; | |||||
| IPassiveSkill? pSkill; | |||||
| ICommonSkill cSkill; | ICommonSkill cSkill; | ||||
| switch (characterType) | switch (characterType) | ||||
| { | { | ||||
| case CharacterType.Assassin: | case CharacterType.Assassin: | ||||
| pSkill = new(); | |||||
| pSkill = null; | |||||
| break; | break; | ||||
| case CharacterType.RecoverAfterBattle: | case CharacterType.RecoverAfterBattle: | ||||
| pSkill = new RecoverAfterBattle(); | pSkill = new RecoverAfterBattle(); | ||||
| @@ -1,5 +1,4 @@ | |||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using Preparation.GameData; | |||||
| namespace GameClass.GameObj | namespace GameClass.GameObj | ||||
| { | { | ||||
| @@ -3,8 +3,6 @@ using System.Collections.Generic; | |||||
| using System.Threading; | using System.Threading; | ||||
| using Preparation.Interface; | using Preparation.Interface; | ||||
| using Preparation.Utility; | using Preparation.Utility; | ||||
| using Preparation.GameData; | |||||
| namespace GameEngine | namespace GameEngine | ||||
| { | { | ||||
| internal class CollisionChecker | internal class CollisionChecker | ||||
| @@ -0,0 +1,60 @@ | |||||
| using CommandLine; | |||||
| namespace Server | |||||
| { | |||||
| static class DefaultArgumentOptions | |||||
| { | |||||
| public static string FileName = "xxxx不用记啊xxxx"; // An impossible name of the playback file to indicate -f is not sepcified. | |||||
| public static string Token = "xxxx随手推啊xxxx"; // An impossible name of the token to indicate -f is not sepcified. | |||||
| public static string Url = "xxxx闭眼写啊xxxx"; | |||||
| public static string MapResource = "xxxx多简单啊xxxx"; | |||||
| } | |||||
| public class ArgumentOptions | |||||
| { | |||||
| [Option('p', "port", Required = true, HelpText = "Server listening port")] | |||||
| public ushort ServerPort { get; set; } = 10086; | |||||
| [Option('t', "teamCount", Required = false, HelpText = "The number of teams, 1 by defualt")] | |||||
| public ushort TeamCount { get; set; } = 1; | |||||
| [Option('c', "playerCount", Required = false, HelpText = "The number of players per team, 1 by default")] | |||||
| public ushort PlayerCountPerTeam { get; set; } = 1; | |||||
| [Option('g', "gameTimeInSecond", Required = false, HelpText = "The time of the game in second, 10 minutes by default")] | |||||
| public uint GameTimeInSecond { get; set; } = 10 * 60; | |||||
| [Option('f', "fileName", Required = false, HelpText = "The file to store playback file or to read file.")] | |||||
| public string FileName { get; set; } = DefaultArgumentOptions.FileName; | |||||
| [Option('b', "playback", Required = false, HelpText = "Whether open the server in a playback mode.")] | |||||
| public bool Playback { get; set; } = false; | |||||
| [Option("playbackSpeed", Required = false, HelpText = "The speed of the playback, between 0.25 and 4.0")] | |||||
| public double PlaybackSpeed { get; set; } = 1.0; | |||||
| [Option("resultOnly", Required = false, HelpText = "In playback mode to get the result directly")] | |||||
| public bool ResultOnly { get; set; } = false; | |||||
| [Option('k', "token", Required = false, HelpText = "Web API Token")] | |||||
| public string Token { get; set; } = DefaultArgumentOptions.Token; | |||||
| [Option('u', "url", Required = false, HelpText = "Web Url")] | |||||
| public string Url { get; set; } = DefaultArgumentOptions.Url; | |||||
| [Option('m', "mapResource", Required = false, HelpText = "Map Resource Path")] | |||||
| public string mapResource { get; set; } = DefaultArgumentOptions.MapResource; | |||||
| [Option("requestOnly", Required = false, HelpText = "Only send web requests")] | |||||
| public bool RequestOnly { get; set; } = false; | |||||
| [Option("finalGame", Required = false, HelpText = "Whether it is the final game")] | |||||
| public bool FinalGame { get; set; } = false; | |||||
| [Option("cheatMode", Required = false, HelpText = "Whether to open the cheat code")] | |||||
| public bool CheatMode { get; set; } = false; | |||||
| [Option("resultFileName", Required = false, HelpText = "Result file name, saved as .json")] | |||||
| public string ResultFileName { get; set; } = DefaultArgumentOptions.FileName; | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,358 @@ | |||||
| using Protobuf; | |||||
| using System.Collections.Generic; | |||||
| using GameClass.GameObj; | |||||
| /* | |||||
| namespace Server | |||||
| { | |||||
| public static class CopyInfo | |||||
| { | |||||
| public static MessageToClient.Types.GameObjMessage? Auto(GameObj gameObj) | |||||
| { | |||||
| 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; //先写着防报错 | |||||
| } | |||||
| private static MessageToClient.Types.GameObjMessage Player(Character player) | |||||
| { | |||||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||||
| msg.MessageOfCharacter = new MessageOfCharacter(); | |||||
| 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; | |||||
| //应该要发队伍分数,这里先发个人分数 | |||||
| msg.MessageOfCharacter.Score = player.Score; | |||||
| //这条暂时没啥用 | |||||
| msg.MessageOfCharacter.TimeUntilUltimateSkillAvailable = 0; | |||||
| msg.MessageOfCharacter.Vampire = player.Vampire; | |||||
| foreach (KeyValuePair<Preparation.Utility.BuffType, bool> kvp in player.Buff) | |||||
| { | |||||
| if (kvp.Value) | |||||
| { | |||||
| switch(kvp.Key) | |||||
| { | |||||
| case Preparation.Utility.BuffType.Spear: | |||||
| msg.MessageOfCharacter.Buff.Add(BuffType.SpearBuff); | |||||
| break; | |||||
| case Preparation.Utility.BuffType.AddLIFE: | |||||
| msg.MessageOfCharacter.Buff.Add(BuffType.AddLife); | |||||
| break; | |||||
| case Preparation.Utility.BuffType.Shield: | |||||
| msg.MessageOfCharacter.Buff.Add(BuffType.ShieldBuff); | |||||
| break; | |||||
| case Preparation.Utility.BuffType.AddSpeed: | |||||
| msg.MessageOfCharacter.Buff.Add(BuffType.MoveSpeed); | |||||
| break; | |||||
| default: | |||||
| break; | |||||
| } | |||||
| } | |||||
| } | |||||
| switch (player.Place) | |||||
| { | |||||
| case Preparation.Utility.PlaceType.Land: | |||||
| msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Land; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass1: | |||||
| msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass1; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass2: | |||||
| msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass2; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass3: | |||||
| msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Grass3; | |||||
| break; | |||||
| // case Preparation.Utility.PlaceType.Invisible: | |||||
| // msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.Invisible; | |||||
| // break; | |||||
| default: | |||||
| msg.MessageOfCharacter.Place = Communication.Proto.PlaceType.NullPlaceType; | |||||
| break; | |||||
| } | |||||
| //Character的储存方式可能得改,用enum type存道具和子弹,不应该用对象 | |||||
| //现在懒得改了,有时间再重整一波 | |||||
| if (player.PropInventory == null) | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.NullPropType; | |||||
| else | |||||
| { | |||||
| switch (player.PropInventory.GetPropType()) | |||||
| { | |||||
| case Preparation.Utility.PropType.Gem: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Gem; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addLIFE: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.AddLife; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addSpeed: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.AddSpeed; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Shield: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Shield; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Spear: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.Spear; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfCharacter.Prop = Communication.Proto.PropType.NullPropType; | |||||
| break; | |||||
| } | |||||
| } | |||||
| switch (player.PassiveSkillType) | |||||
| { | |||||
| case Preparation.Utility.PassiveSkillType.RecoverAfterBattle: | |||||
| msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.RecoverAfterBattle; | |||||
| break; | |||||
| case Preparation.Utility.PassiveSkillType.SpeedUpWhenLeavingGrass: | |||||
| msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.SpeedUpWhenLeavingGrass; | |||||
| break; | |||||
| case Preparation.Utility.PassiveSkillType.Vampire: | |||||
| msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.Vampire; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfCharacter.PassiveSkillType = Communication.Proto.PassiveSkillType.NullPassiveSkillType; | |||||
| break; | |||||
| } | |||||
| switch (player.CommonSkillType) | |||||
| { | |||||
| case Preparation.Utility.ActiveSkillType.BecomeAssassin: | |||||
| msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeAssassin; | |||||
| break; | |||||
| case Preparation.Utility.ActiveSkillType.BecomeVampire: | |||||
| msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.BecomeVampire; | |||||
| break; | |||||
| case Preparation.Utility.ActiveSkillType.NuclearWeapon: | |||||
| msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.NuclearWeapon; | |||||
| break; | |||||
| case Preparation.Utility.ActiveSkillType.SuperFast: | |||||
| msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.SuperFast; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfCharacter.ActiveSkillType = Communication.Proto.ActiveSkillType.NullActiveSkillType; | |||||
| break; | |||||
| } | |||||
| switch (player.BulletOfPlayer) | |||||
| { | |||||
| case Preparation.Utility.BulletType.AtomBomb: | |||||
| msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.AtomBomb; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||||
| msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.OrdinaryBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.FastBullet: | |||||
| msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.FastBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.LineBullet: | |||||
| msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.LineBullet; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfCharacter.BulletType = Communication.Proto.BulletType.NullBulletType; | |||||
| break; | |||||
| } | |||||
| return msg; | |||||
| } | |||||
| private static MessageToClient.Types.GameObjMessage Bullet(Bullet bullet) | |||||
| { | |||||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||||
| msg.MessageOfBullet = new MessageOfBullet(); | |||||
| msg.MessageOfBullet.FacingDirection = bullet.FacingDirection; | |||||
| msg.MessageOfBullet.Guid = bullet.ID; | |||||
| msg.MessageOfBullet.BombRange = BulletFactory.BulletBombRange(bullet.TypeOfBullet); | |||||
| switch (bullet.TypeOfBullet) | |||||
| { | |||||
| case Preparation.Utility.BulletType.AtomBomb: | |||||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.AtomBomb; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.OrdinaryBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.FastBullet: | |||||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.FastBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.LineBullet: | |||||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.LineBullet; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfBullet.Type = Communication.Proto.BulletType.NullBulletType; | |||||
| break; | |||||
| } | |||||
| msg.MessageOfBullet.X = bullet.Position.x; | |||||
| msg.MessageOfBullet.Y = bullet.Position.y; | |||||
| if (bullet.Parent != null) | |||||
| msg.MessageOfBullet.ParentTeamID = bullet.Parent.TeamID; | |||||
| switch (bullet.Place) | |||||
| { | |||||
| case Preparation.Utility.PlaceType.Land: | |||||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Land; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass1: | |||||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass1; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass2: | |||||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass2; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass3: | |||||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.Grass3; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfBullet.Place = Communication.Proto.PlaceType.NullPlaceType; | |||||
| break; | |||||
| } | |||||
| return msg; | |||||
| } | |||||
| private static MessageToClient.Types.GameObjMessage Prop(Prop prop) | |||||
| { | |||||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||||
| msg.MessageOfProp = new MessageOfProp(); | |||||
| msg.MessageOfProp.FacingDirection = prop.FacingDirection; | |||||
| msg.MessageOfProp.Guid = prop.ID; | |||||
| msg.MessageOfProp.IsMoving = prop.IsMoving; | |||||
| switch (prop.GetPropType()) | |||||
| { | |||||
| case Preparation.Utility.PropType.Gem: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.Gem; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addLIFE: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.AddLife; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addSpeed: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.AddSpeed; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Shield: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.Shield; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Spear: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.Spear; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfProp.Type = Communication.Proto.PropType.NullPropType; | |||||
| break; | |||||
| } | |||||
| msg.MessageOfProp.X = prop.Position.x; | |||||
| msg.MessageOfProp.Y = prop.Position.y; | |||||
| if(prop is Gem) | |||||
| { | |||||
| msg.MessageOfProp.Size = ((Gem)prop).Size; | |||||
| } | |||||
| else | |||||
| { | |||||
| msg.MessageOfProp.Size = 1; | |||||
| } | |||||
| switch (prop.Place) | |||||
| { | |||||
| case Preparation.Utility.PlaceType.Land: | |||||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Land; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass1: | |||||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass1; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass2: | |||||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass2; | |||||
| break; | |||||
| case Preparation.Utility.PlaceType.Grass3: | |||||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.Grass3; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfProp.Place = Communication.Proto.PlaceType.NullPlaceType; | |||||
| break; | |||||
| } | |||||
| return msg; | |||||
| } | |||||
| private static MessageToClient.Types.GameObjMessage BombedBullet(BombedBullet bombedBullet) | |||||
| { | |||||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||||
| msg.MessageOfBombedBullet = 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); | |||||
| switch (bombedBullet.bulletHasBombed.TypeOfBullet) | |||||
| { | |||||
| case Preparation.Utility.BulletType.OrdinaryBullet: | |||||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.OrdinaryBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.AtomBomb: | |||||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.AtomBomb; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.FastBullet: | |||||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.FastBullet; | |||||
| break; | |||||
| case Preparation.Utility.BulletType.LineBullet: | |||||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.LineBullet; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfBombedBullet.Type = Communication.Proto.BulletType.NullBulletType; | |||||
| break; | |||||
| } | |||||
| return msg; | |||||
| } | |||||
| private static MessageToClient.Types.GameObjMessage PickedProp(PickedProp pickedProp) | |||||
| { | |||||
| MessageToClient.Types.GameObjMessage msg = new MessageToClient.Types.GameObjMessage(); | |||||
| msg.MessageOfPickedProp = new MessageOfPickedProp(); | |||||
| msg.MessageOfPickedProp.MappingID = pickedProp.MappingID; | |||||
| msg.MessageOfPickedProp.X = pickedProp.PropHasPicked.Position.x; | |||||
| msg.MessageOfPickedProp.Y = pickedProp.PropHasPicked.Position.y; | |||||
| msg.MessageOfPickedProp.FacingDirection = pickedProp.PropHasPicked.FacingDirection; | |||||
| switch (pickedProp.PropHasPicked.GetPropType()) | |||||
| { | |||||
| case Preparation.Utility.PropType.Gem: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.Gem; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addLIFE: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.AddLife; | |||||
| break; | |||||
| case Preparation.Utility.PropType.addSpeed: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.AddSpeed; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Shield: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.Shield; | |||||
| break; | |||||
| case Preparation.Utility.PropType.Spear: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.Spear; | |||||
| break; | |||||
| default: | |||||
| msg.MessageOfPickedProp.Type = Communication.Proto.PropType.NullPropType; | |||||
| break; | |||||
| } | |||||
| return msg; | |||||
| } | |||||
| } | |||||
| }*/ | |||||
| // 等Preparation完成再写 | |||||
| @@ -1,109 +0,0 @@ | |||||
| using Grpc.Core; | |||||
| using Protobuf; | |||||
| using System.Threading; | |||||
| using Timothy.FrameRateTask; | |||||
| using System; | |||||
| using System.Net.Http.Headers; | |||||
| namespace Server | |||||
| { | |||||
| public class Game | |||||
| { | |||||
| private const int gameTime = 3000; | |||||
| public int GameTime => gameTime; | |||||
| private MessageToClient gameInfo = new(); | |||||
| private object gameInfoLock = new(); | |||||
| private int isGaming = 0; | |||||
| public bool IsGaming | |||||
| { | |||||
| get => Interlocked.CompareExchange(ref isGaming, 0, 0) != 0; | |||||
| set => Interlocked.Exchange(ref isGaming, value ? 1 : 0); | |||||
| } | |||||
| public MessageToClient GetCopiedGameInfo() | |||||
| { | |||||
| lock (gameInfoLock) | |||||
| { | |||||
| return gameInfo.Clone(); | |||||
| } | |||||
| } | |||||
| public void AddPlayer(PlayerMsg player) | |||||
| { | |||||
| lock (gameInfoLock) | |||||
| { | |||||
| if (player.PlayerType == PlayerType.NullPlayerType) | |||||
| return; | |||||
| if (player.PlayerType == PlayerType.HumanPlayer) | |||||
| { | |||||
| gameInfo.HumanMessage.Add(new MessageOfHuman() | |||||
| { | |||||
| PlayerId = player.PlayerId | |||||
| }); | |||||
| return; | |||||
| } | |||||
| if (player.PlayerType == PlayerType.ButcherPlayer) | |||||
| { | |||||
| gameInfo.ButcherMessage.Add(new MessageOfButcher() | |||||
| { | |||||
| PlayerId = player.PlayerId | |||||
| }); | |||||
| return; | |||||
| } | |||||
| } | |||||
| } | |||||
| public void Move() | |||||
| { | |||||
| } | |||||
| public SemaphoreSlim StartGame() | |||||
| { | |||||
| IsGaming = true; | |||||
| var waitHandle = new SemaphoreSlim(0); | |||||
| new Thread | |||||
| ( | |||||
| () => | |||||
| { | |||||
| new FrameRateTaskExecutor<int> | |||||
| ( | |||||
| () => IsGaming, | |||||
| () => | |||||
| { | |||||
| lock (gameInfo) | |||||
| { | |||||
| for (int i = 0; i < gameInfo.HumanMessage.Count; i++) | |||||
| { | |||||
| if (gameInfo.HumanMessage[i] != null) | |||||
| { | |||||
| gameInfo.HumanMessage[i].X++; | |||||
| gameInfo.HumanMessage[i].Y--; | |||||
| } | |||||
| } | |||||
| for (int i = 0; i < gameInfo.ButcherMessage.Count; i++) | |||||
| { | |||||
| if (gameInfo.ButcherMessage[i] != null) | |||||
| { | |||||
| gameInfo.ButcherMessage[i].X--; | |||||
| gameInfo.ButcherMessage[i].Y++; | |||||
| } | |||||
| } | |||||
| } | |||||
| }, | |||||
| 100, | |||||
| () => | |||||
| { | |||||
| IsGaming = false; | |||||
| waitHandle.Release(); | |||||
| return 0; | |||||
| }, | |||||
| gameTime | |||||
| ).Start(); | |||||
| } | |||||
| ) | |||||
| { IsBackground = true }.Start(); | |||||
| return waitHandle; | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -4,6 +4,8 @@ using System.Threading; | |||||
| using Timothy.FrameRateTask; | using Timothy.FrameRateTask; | ||||
| using System; | using System; | ||||
| using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||
| using Gaming; | |||||
| using GameClass.GameObj; | |||||
| namespace Server | namespace Server | ||||
| { | { | ||||
| @@ -11,18 +13,81 @@ namespace Server | |||||
| { | { | ||||
| private Dictionary<long, (SemaphoreSlim, SemaphoreSlim)> semaDict = new(); | private Dictionary<long, (SemaphoreSlim, SemaphoreSlim)> semaDict = new(); | ||||
| private object gameLock = new(); | private object gameLock = new(); | ||||
| private const int playerNum = 1; | |||||
| private const int playerNum = 2; // 注意修改 | |||||
| private MessageToClient currentGameInfo = new(); | private MessageToClient currentGameInfo = new(); | ||||
| private Game game = new(); | |||||
| public int GameTime => game.GameTime; | |||||
| private SemaphoreSlim endGameSem = new(0); | private SemaphoreSlim endGameSem = new(0); | ||||
| // 以上是测试时用到的定义 | |||||
| protected readonly ArgumentOptions options; | |||||
| protected readonly Game game; | |||||
| private uint spectatorMinPlayerID = 2022; | |||||
| private List<Tuple<PlayerType, uint>> spectatorList = new List<Tuple<PlayerType, uint>>(); | |||||
| public int TeamCount => options.TeamCount; | |||||
| protected long[,] communicationToGameID; // 通信用的ID映射到游戏内的ID,[i,j]表示team:i,player:j的id。 | |||||
| private readonly object messageToAllClientsLock = new(); | |||||
| public static readonly long SendMessageToClientIntervalInMilliseconds = 50; | |||||
| private readonly Semaphore endGameInfoSema = new(0, 1); | |||||
| // private MessageWriter? mwr = null; | |||||
| public void StartGame() | |||||
| { | |||||
| bool gameState = game.StartGame((int)options.GameTimeInSecond * 1000); | |||||
| var waitHandle = new SemaphoreSlim(gameState == true ? 1 : 0); // 注意修改 | |||||
| new Thread(() => | |||||
| { | |||||
| new FrameRateTaskExecutor<int> | |||||
| ( | |||||
| () => game.GameMap.Timer.IsGaming, | |||||
| ReportGame, | |||||
| 1000, | |||||
| () => | |||||
| { | |||||
| ReportGame(); // 最后发一次消息,唤醒发消息的线程,防止发消息的线程由于有概率处在 Wait 状态而卡住 | |||||
| return 0; | |||||
| } | |||||
| ).Start(); | |||||
| }) | |||||
| { IsBackground = true }.Start(); | |||||
| new Thread(() => | |||||
| { | |||||
| waitHandle.Wait(); | |||||
| this.endGameSem.Release(); | |||||
| }) | |||||
| { IsBackground = true }.Start(); | |||||
| } | |||||
| public void WaitForEnd() | |||||
| { | |||||
| this.endGameSem.Wait(); | |||||
| } | |||||
| public void ReportGame() | |||||
| { | |||||
| // currentGameInfo = game.GetCopiedGameInfo(); | |||||
| currentGameInfo.HumanMessage[0].X = 1; | |||||
| currentGameInfo.ButcherMessage[0].X = 1; | |||||
| foreach (var kvp in semaDict) | |||||
| { | |||||
| kvp.Value.Item1.Release(); | |||||
| } | |||||
| foreach (var kvp in semaDict) | |||||
| { | |||||
| kvp.Value.Item2.Wait(); | |||||
| } | |||||
| } | |||||
| private uint GetBirthPointIdx(long teamID, long playerID) // 获取出生点位置 | |||||
| { | |||||
| return (uint)((teamID * options.PlayerCountPerTeam) + playerID); | |||||
| } | |||||
| public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context) | public override Task<BoolRes> TryConnection(IDMsg request, ServerCallContext context) | ||||
| { | { | ||||
| var onConnection = new BoolRes(); | var onConnection = new BoolRes(); | ||||
| lock (gameLock) | lock (gameLock) | ||||
| { | { | ||||
| // if (0 <= request.PlayerId && request.PlayerId < playerNum) | |||||
| if (0 <= request.PlayerId && request.PlayerId < playerNum) // 注意修改 | |||||
| { | { | ||||
| onConnection.ActSuccess = true; | onConnection.ActSuccess = true; | ||||
| Console.WriteLine(onConnection.ActSuccess); | Console.WriteLine(onConnection.ActSuccess); | ||||
| @@ -33,21 +98,44 @@ namespace Server | |||||
| return Task.FromResult(onConnection); | return Task.FromResult(onConnection); | ||||
| } | } | ||||
| protected readonly object addPlayerLock = new(); | |||||
| public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter<MessageToClient> responseStream, ServerCallContext context) | public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter<MessageToClient> responseStream, ServerCallContext context) | ||||
| { | { | ||||
| lock (gameLock) | |||||
| if (request.PlayerId >= spectatorMinPlayerID && request.PlayerType == PlayerType.NullPlayerType) | |||||
| { | { | ||||
| if (game.IsGaming) | |||||
| return; | |||||
| game.AddPlayer(request); | |||||
| var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); | |||||
| // 观战模式 | |||||
| Tuple<PlayerType, uint> tp = new Tuple<PlayerType, uint>(request.PlayerType, (uint)request.PlayerId); | |||||
| if (!spectatorList.Contains(tp)) | |||||
| { | |||||
| spectatorList.Add(tp); | |||||
| Console.WriteLine("A new spectator comes to watch this game."); | |||||
| } | |||||
| return; | |||||
| } | |||||
| if (game.GameMap.Timer.IsGaming) | |||||
| return; | |||||
| /*if (!ValidTeamIDAndPlayerID(msg.TeamID, msg.PlayerID)) //玩家id是否正确 | |||||
| return; | |||||
| if (communicationToGameID[msg.TeamID, msg.PlayerID] != GameObj.invalidID) //是否已经添加了该玩家 | |||||
| return false; | |||||
| Preparation.Utility.PassiveSkillType passiveSkill; | |||||
| */ | |||||
| lock (addPlayerLock) | |||||
| { | |||||
| /*Game.PlayerInitInfo playerInitInfo = new(GetBirthPointIdx(msg.TeamID, msg.PlayerID), msg.TeamID, msg.PlayerID, passiveSkill, commonSkill); | |||||
| long newPlayerID = game.AddPlayer(playerInitInfo); | |||||
| if (newPlayerID == GameObj.invalidID) | |||||
| return false;*/ | |||||
| // 内容待修改 | |||||
| var temp = (new SemaphoreSlim(0, 1), new SemaphoreSlim(0, 1)); | |||||
| bool start = false; | bool start = false; | ||||
| Console.WriteLine($"Id: {request.PlayerId} joins."); | Console.WriteLine($"Id: {request.PlayerId} joins."); | ||||
| lock (semaDict) | lock (semaDict) | ||||
| { | { | ||||
| semaDict.Add(request.PlayerId, temp); | semaDict.Add(request.PlayerId, temp); | ||||
| start = semaDict.Count == playerNum; | |||||
| start = semaDict.Count == playerNum; // 之后补上CheckStart函数 | |||||
| } | } | ||||
| if (start) | if (start) | ||||
| @@ -66,8 +154,8 @@ namespace Server | |||||
| Console.WriteLine("Send!"); | Console.WriteLine("Send!"); | ||||
| } | } | ||||
| semaDict[request.PlayerId].Item2.Release(); | semaDict[request.PlayerId].Item2.Release(); | ||||
| } while (game.IsGaming); | |||||
| } | |||||
| } while (game.GameMap.Timer.IsGaming); | |||||
| } | |||||
| public override Task<BoolRes> Attack(AttackMsg request, ServerCallContext context) | public override Task<BoolRes> Attack(AttackMsg request, ServerCallContext context) | ||||
| { | { | ||||
| @@ -94,11 +182,6 @@ namespace Server | |||||
| return base.Escape(request, context); | return base.Escape(request, context); | ||||
| } | } | ||||
| public override int GetHashCode() | |||||
| { | |||||
| return base.GetHashCode(); | |||||
| } | |||||
| public override Task GetMessage(IDMsg request, IServerStreamWriter<MsgRes> responseStream, ServerCallContext context) | public override Task GetMessage(IDMsg request, IServerStreamWriter<MsgRes> responseStream, ServerCallContext context) | ||||
| { | { | ||||
| return base.GetMessage(request, responseStream, context); | return base.GetMessage(request, responseStream, context); | ||||
| @@ -148,54 +231,81 @@ namespace Server | |||||
| { | { | ||||
| return base.UseSkill(request, context); | return base.UseSkill(request, context); | ||||
| } | } | ||||
| public void StartGame() | |||||
| public GameServer(ArgumentOptions options) | |||||
| { | { | ||||
| var waitHandle = game.StartGame(); | |||||
| new Thread(() => | |||||
| /*this.options = options; | |||||
| if (options.mapResource == DefaultArgumentOptions.MapResource) | |||||
| this.game = new Game(MapInfo.defaultMap, options.TeamCount); | |||||
| else | |||||
| { | { | ||||
| new FrameRateTaskExecutor<int> | |||||
| ( | |||||
| () => game.IsGaming, | |||||
| ReportGame, | |||||
| 1000, | |||||
| () => | |||||
| uint[,] map = new uint[GameData.rows, GameData.cols]; | |||||
| try | |||||
| { | |||||
| string? line; | |||||
| int i = 0, j = 0; | |||||
| using (StreamReader sr = new StreamReader(options.mapResource)) | |||||
| { | { | ||||
| ReportGame(); // 最后发一次消息,唤醒发消息的线程,防止发消息的线程由于有概率处在 Wait 状态而卡住 | |||||
| return 0; | |||||
| while (!sr.EndOfStream && i < GameData.rows) | |||||
| { | |||||
| if ((line = sr.ReadLine()) != null) | |||||
| { | |||||
| string[] nums = line.Split(' '); | |||||
| foreach (string item in nums) | |||||
| { | |||||
| if (item.Length > 1)//以兼容原方案 | |||||
| { | |||||
| map[i, j] = (uint)int.Parse(item); | |||||
| } | |||||
| else | |||||
| { | |||||
| //2022-04-22 by LHR 十六进制编码地图方案(防止地图编辑员瞎眼x | |||||
| map[i, j] = (uint)Preparation.Utility.MapEncoder.Hex2Dec(char.Parse(item)); | |||||
| } | |||||
| j++; | |||||
| if (j >= GameData.cols) | |||||
| { | |||||
| j = 0; | |||||
| break; | |||||
| } | |||||
| } | |||||
| i++; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| ).Start(); | |||||
| }) | |||||
| { IsBackground = true }.Start(); | |||||
| new Thread(() => | |||||
| { | |||||
| waitHandle.Wait(); | |||||
| this.endGameSem.Release(); | |||||
| }) | |||||
| { IsBackground = true }.Start(); | |||||
| } | |||||
| public void WaitForEnd() | |||||
| { | |||||
| this.endGameSem.Wait(); | |||||
| } | |||||
| public void ReportGame() | |||||
| { | |||||
| currentGameInfo = game.GetCopiedGameInfo(); | |||||
| foreach (var kvp in semaDict) | |||||
| } | |||||
| catch | |||||
| { | |||||
| map = MapInfo.defaultMap; | |||||
| } | |||||
| finally { this.game = new Game(map, options.TeamCount); } | |||||
| } | |||||
| communicationToGameID = new long[options.TeamCount, options.PlayerCountPerTeam]; | |||||
| //创建server时先设定待加入人物都是invalid | |||||
| for (int i = 0; i < communicationToGameID.GetLength(0); i++) | |||||
| { | { | ||||
| kvp.Value.Item1.Release(); | |||||
| for (int j = 0; j < communicationToGameID.GetLength(1); j++) | |||||
| { | |||||
| communicationToGameID[i, j] = GameObj.invalidID; | |||||
| } | |||||
| } | } | ||||
| foreach (var kvp in semaDict) | |||||
| if (options.FileName != DefaultArgumentOptions.FileName) | |||||
| { | { | ||||
| kvp.Value.Item2.Wait(); | |||||
| try | |||||
| { | |||||
| //mwr = new MessageWriter(options.FileName, options.TeamCount, options.PlayerCountPerTeam); | |||||
| } | |||||
| catch | |||||
| { | |||||
| Console.WriteLine($"Error: Cannot create the playback file: {options.FileName}!"); | |||||
| } | |||||
| } | } | ||||
| } | |||||
| public GameServer() | |||||
| { | |||||
| if (options.Url != DefaultArgumentOptions.Url && options.Token != DefaultArgumentOptions.Token) | |||||
| { | |||||
| //this.httpSender = new HttpSender(options.Url, options.Token, "PUT"); | |||||
| }*/ | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -4,20 +4,36 @@ using System.Threading; | |||||
| using Timothy.FrameRateTask; | using Timothy.FrameRateTask; | ||||
| using System; | using System; | ||||
| using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||
| using CommandLine; | |||||
| namespace Server | namespace Server | ||||
| { | { | ||||
| public class Program | public class Program | ||||
| { | { | ||||
| public static void Main() | |||||
| static int Main(string[] args) | |||||
| { | { | ||||
| foreach (var arg in args) | |||||
| { | |||||
| Console.Write($"{arg} "); | |||||
| } | |||||
| Console.WriteLine(); | |||||
| ArgumentOptions? options = null; | |||||
| _ = Parser.Default.ParseArguments<ArgumentOptions>(args).WithParsed(o => { options = o; }); | |||||
| if (options == null) | |||||
| { | |||||
| Console.WriteLine("Argument parsing failed!"); | |||||
| // return 1; | |||||
| } | |||||
| // Console.WriteLine("Server begins to run: " + options.ServerPort.ToString()); | |||||
| try | try | ||||
| { | { | ||||
| GameServer gameServer = new(); | |||||
| Grpc.Core.Server server = new Grpc.Core.Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) | |||||
| { | |||||
| GameServer? gameServer = new(options); | |||||
| Grpc.Core.Server server = new Grpc.Core.Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) }) { | |||||
| Services = { AvailableService.BindService(gameServer) }, | Services = { AvailableService.BindService(gameServer) }, | ||||
| Ports = { new ServerPort("127.0.0.1", 8888, ServerCredentials.Insecure) } | |||||
| Ports = { new ServerPort("0.0.0.0", 8888, ServerCredentials.Insecure) } | |||||
| }; | }; | ||||
| server.Start(); | server.Start(); | ||||
| @@ -30,6 +46,7 @@ namespace Server | |||||
| { | { | ||||
| Console.WriteLine(ex.ToString()); | Console.WriteLine(ex.ToString()); | ||||
| } | } | ||||
| return 0; | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,4 +1,4 @@ | |||||
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <Project Sdk="Microsoft.NET.Sdk"> | |||||
| <PropertyGroup> | <PropertyGroup> | ||||
| <OutputType>Exe</OutputType> | <OutputType>Exe</OutputType> | ||||
| @@ -8,10 +8,11 @@ | |||||
| </PropertyGroup> | </PropertyGroup> | ||||
| <ItemGroup> | <ItemGroup> | ||||
| <PackageReference Include="CommandLineParser" Version="2.9.1" /> | |||||
| <PackageReference Include="FrameRateTask" Version="1.2.0" /> | <PackageReference Include="FrameRateTask" Version="1.2.0" /> | ||||
| <PackageReference Include="Google.Protobuf" Version="3.21.12" /> | <PackageReference Include="Google.Protobuf" Version="3.21.12" /> | ||||
| <PackageReference Include="Grpc" Version="2.46.5" /> | |||||
| <PackageReference Include="Grpc.Core" Version="2.46.5" /> | |||||
| <PackageReference Include="Grpc" Version="2.46.6" /> | |||||
| <PackageReference Include="Grpc.Core" Version="2.46.6" /> | |||||
| <PackageReference Include="Grpc.Tools" Version="2.51.0"> | <PackageReference Include="Grpc.Tools" Version="2.51.0"> | ||||
| <PrivateAssets>all</PrivateAssets> | <PrivateAssets>all</PrivateAssets> | ||||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||||
| @@ -20,6 +21,10 @@ | |||||
| <ItemGroup> | <ItemGroup> | ||||
| <ProjectReference Include="..\..\dependency\proto\Protos.csproj" /> | <ProjectReference Include="..\..\dependency\proto\Protos.csproj" /> | ||||
| <ProjectReference Include="..\GameClass\GameClass.csproj" /> | |||||
| <ProjectReference Include="..\GameEngine\GameEngine.csproj" /> | |||||
| <ProjectReference Include="..\Gaming\Gaming.csproj" /> | |||||
| <ProjectReference Include="..\Preparation\Preparation.csproj" /> | |||||
| </ItemGroup> | </ItemGroup> | ||||
| </Project> | </Project> | ||||