Browse Source

feat: 🌱 promote AddPlayer function and add to the framework of server

tags/0.1.0
gsy1519 2 years ago
parent
commit
e325ec52f7
9 changed files with 615 additions and 177 deletions
  1. +2
    -2
      logic/GameClass/GameObj/Character/Character.SkillManager.cs
  2. +0
    -1
      logic/GameClass/GameObj/Map/MapInfo.cs
  3. +0
    -2
      logic/GameEngine/CollisionChecker.cs
  4. +60
    -0
      logic/Server/ArgumentOption.cs
  5. +358
    -0
      logic/Server/CopyInfo.cs
  6. +0
    -109
      logic/Server/Game.cs
  7. +165
    -55
      logic/Server/GameServer.cs
  8. +22
    -5
      logic/Server/Program.cs
  9. +8
    -3
      logic/Server/Server.csproj

+ 2
- 2
logic/GameClass/GameObj/Character/Character.SkillManager.cs View File

@@ -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();


+ 0
- 1
logic/GameClass/GameObj/Map/MapInfo.cs View File

@@ -1,5 +1,4 @@
using Preparation.Utility;
using Preparation.GameData;

namespace GameClass.GameObj
{


+ 0
- 2
logic/GameEngine/CollisionChecker.cs View File

@@ -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


+ 60
- 0
logic/Server/ArgumentOption.cs View File

@@ -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;
}
}

+ 358
- 0
logic/Server/CopyInfo.cs View File

@@ -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完成再写

+ 0
- 109
logic/Server/Game.cs View File

@@ -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;
}
}
}

+ 165
- 55
logic/Server/GameServer.cs View File

@@ -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<long, (SemaphoreSlim, SemaphoreSlim)> 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<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)
{
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<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;
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<BoolRes> 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<MsgRes> 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<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");
}*/
}
}
}

+ 22
- 5
logic/Server/Program.cs View File

@@ -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<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
{
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;
}
}
}

+ 8
- 3
logic/Server/Server.csproj View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
@@ -8,10 +8,11 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
<PackageReference Include="FrameRateTask" Version="1.2.0" />
<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">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -20,6 +21,10 @@

<ItemGroup>
<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>

</Project>

Loading…
Cancel
Save