From e325ec52f718b858a5ebd102736da604d6df661a Mon Sep 17 00:00:00 2001 From: gsy1519 <614054460@qq.com> Date: Thu, 9 Feb 2023 13:25:56 +0800 Subject: [PATCH] feat: :seedling: promote AddPlayer function and add to the framework of server --- .../Character/Character.SkillManager.cs | 4 +- logic/GameClass/GameObj/Map/MapInfo.cs | 1 - logic/GameEngine/CollisionChecker.cs | 2 - logic/Server/ArgumentOption.cs | 60 +++ logic/Server/CopyInfo.cs | 358 ++++++++++++++++++ logic/Server/Game.cs | 109 ------ logic/Server/GameServer.cs | 220 ++++++++--- logic/Server/Program.cs | 27 +- logic/Server/Server.csproj | 11 +- 9 files changed, 615 insertions(+), 177 deletions(-) create mode 100644 logic/Server/ArgumentOption.cs create mode 100644 logic/Server/CopyInfo.cs delete mode 100644 logic/Server/Game.cs diff --git a/logic/GameClass/GameObj/Character/Character.SkillManager.cs b/logic/GameClass/GameObj/Character/Character.SkillManager.cs index 36b522f..3712f1c 100644 --- a/logic/GameClass/GameObj/Character/Character.SkillManager.cs +++ b/logic/GameClass/GameObj/Character/Character.SkillManager.cs @@ -60,12 +60,12 @@ namespace GameClass.GameObj this.score = 0; this.propInventory = null; this.buffManeger = new BuffManeger(); - IPassiveSkill pSkill; + IPassiveSkill? pSkill; ICommonSkill cSkill; switch (characterType) { case CharacterType.Assassin: - pSkill = new(); + pSkill = null; break; case CharacterType.RecoverAfterBattle: pSkill = new RecoverAfterBattle(); diff --git a/logic/GameClass/GameObj/Map/MapInfo.cs b/logic/GameClass/GameObj/Map/MapInfo.cs index 436456e..b17175f 100644 --- a/logic/GameClass/GameObj/Map/MapInfo.cs +++ b/logic/GameClass/GameObj/Map/MapInfo.cs @@ -1,5 +1,4 @@ using Preparation.Utility; -using Preparation.GameData; namespace GameClass.GameObj { diff --git a/logic/GameEngine/CollisionChecker.cs b/logic/GameEngine/CollisionChecker.cs index 459f63f..82802c5 100644 --- a/logic/GameEngine/CollisionChecker.cs +++ b/logic/GameEngine/CollisionChecker.cs @@ -3,8 +3,6 @@ using System.Collections.Generic; using System.Threading; using Preparation.Interface; using Preparation.Utility; -using Preparation.GameData; - namespace GameEngine { internal class CollisionChecker diff --git a/logic/Server/ArgumentOption.cs b/logic/Server/ArgumentOption.cs new file mode 100644 index 0000000..2f8752a --- /dev/null +++ b/logic/Server/ArgumentOption.cs @@ -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; + } +} \ No newline at end of file diff --git a/logic/Server/CopyInfo.cs b/logic/Server/CopyInfo.cs new file mode 100644 index 0000000..1455b69 --- /dev/null +++ b/logic/Server/CopyInfo.cs @@ -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 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完成再写 diff --git a/logic/Server/Game.cs b/logic/Server/Game.cs deleted file mode 100644 index 6993d5c..0000000 --- a/logic/Server/Game.cs +++ /dev/null @@ -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 - ( - () => 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; - } - } -} diff --git a/logic/Server/GameServer.cs b/logic/Server/GameServer.cs index b275b50..7da8c7c 100644 --- a/logic/Server/GameServer.cs +++ b/logic/Server/GameServer.cs @@ -4,6 +4,8 @@ using System.Threading; using Timothy.FrameRateTask; using System; using System.Net.Http.Headers; +using Gaming; +using GameClass.GameObj; namespace Server { @@ -11,18 +13,81 @@ namespace Server { private Dictionary semaDict = new(); private object gameLock = new(); - private const int playerNum = 1; + private const int playerNum = 2; // 注意修改 private MessageToClient currentGameInfo = new(); - private Game game = new(); - public int GameTime => game.GameTime; private SemaphoreSlim endGameSem = new(0); + // 以上是测试时用到的定义 + + protected readonly ArgumentOptions options; + protected readonly Game game; + private uint spectatorMinPlayerID = 2022; + private List> spectatorList = new List>(); + 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 + ( + () => 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 TryConnection(IDMsg request, ServerCallContext context) { var onConnection = new BoolRes(); lock (gameLock) { - // if (0 <= request.PlayerId && request.PlayerId < playerNum) + if (0 <= request.PlayerId && request.PlayerId < playerNum) // 注意修改 { onConnection.ActSuccess = true; Console.WriteLine(onConnection.ActSuccess); @@ -33,21 +98,44 @@ namespace Server return Task.FromResult(onConnection); } + protected readonly object addPlayerLock = new(); public override async Task AddPlayer(PlayerMsg request, IServerStreamWriter 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 tp = new Tuple(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; Console.WriteLine($"Id: {request.PlayerId} joins."); lock (semaDict) { semaDict.Add(request.PlayerId, temp); - start = semaDict.Count == playerNum; + start = semaDict.Count == playerNum; // 之后补上CheckStart函数 } if (start) @@ -66,8 +154,8 @@ namespace Server Console.WriteLine("Send!"); } semaDict[request.PlayerId].Item2.Release(); - } while (game.IsGaming); - } + } while (game.GameMap.Timer.IsGaming); +} public override Task Attack(AttackMsg request, ServerCallContext context) { @@ -94,11 +182,6 @@ namespace Server return base.Escape(request, context); } - public override int GetHashCode() - { - return base.GetHashCode(); - } - public override Task GetMessage(IDMsg request, IServerStreamWriter responseStream, ServerCallContext context) { return base.GetMessage(request, responseStream, context); @@ -148,54 +231,81 @@ namespace Server { 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 - ( - () => 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"); + }*/ } } } \ No newline at end of file diff --git a/logic/Server/Program.cs b/logic/Server/Program.cs index f43bc80..c891663 100644 --- a/logic/Server/Program.cs +++ b/logic/Server/Program.cs @@ -4,20 +4,36 @@ using System.Threading; using Timothy.FrameRateTask; using System; using System.Net.Http.Headers; +using CommandLine; namespace Server { 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(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 { - 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) }, - Ports = { new ServerPort("127.0.0.1", 8888, ServerCredentials.Insecure) } + Ports = { new ServerPort("0.0.0.0", 8888, ServerCredentials.Insecure) } }; server.Start(); @@ -30,6 +46,7 @@ namespace Server { Console.WriteLine(ex.ToString()); } + return 0; } } } \ No newline at end of file diff --git a/logic/Server/Server.csproj b/logic/Server/Server.csproj index 03e9bdd..454cea3 100644 --- a/logic/Server/Server.csproj +++ b/logic/Server/Server.csproj @@ -1,4 +1,4 @@ - + Exe @@ -8,10 +8,11 @@ + - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -20,6 +21,10 @@ + + + +