This commit is contained in:
yaoyanwei
2025-08-04 16:45:48 +08:00
parent 565aa16389
commit 2f2a601227
2296 changed files with 522745 additions and 93 deletions

View File

@@ -0,0 +1,955 @@
using System.Collections.Generic;
using TcgEngine.AI;
using TcgEngine.Gameplay;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
namespace TcgEngine.Server
{
/// <summary>
/// Represent one game on the server, when playing solo this will be created locally,
/// or if online multiple GameServer, one for each match, will be created by the dedicated server
/// Manage receiving actions, sending refresh, and running AI
/// </summary>
public class GameServer
{
public string game_uid; //Game unique ID
public int nb_players = 2;
public static float game_expire_time = 30f; //How long for the game to be deleted when no one is connected
public static float win_expire_time = 60f; //How long for a player to be declared winnner if hes the only one connected
private Game game_data;
private GameLogic gameplay;
private float expiration = 0f;
private float win_expiration = 0f;
private bool is_dedicated_server = false;
private List<ClientData> players = new List<ClientData>(); //Exclude observers, stays in array when disconnected, only players can send commands
private List<ClientData> connected_clients = new List<ClientData>(); //Include obervers, removed from array when disconnected, all clients receive refreshes
private List<AIPlayer> ai_list = new List<AIPlayer>(); //List of all AI players
private Queue<QueuedGameAction> queued_actions = new Queue<QueuedGameAction>(); //List of action waiting to be processed
private Dictionary<ushort, CommandEvent> registered_commands = new Dictionary<ushort, CommandEvent>();
public GameServer(string uid, int players, bool online)
{
Init(uid, players, online);
}
~GameServer()
{
Clear();
}
protected virtual void Init(string uid, int players, bool online)
{
game_uid = uid;
nb_players = Mathf.Max(players, 2);
is_dedicated_server = online;
game_data = new Game(uid, nb_players);
gameplay = new GameLogic(game_data);
//Commands
RegisterAction(GameAction.PlayerSettings, ReceivePlayerSettings);
RegisterAction(GameAction.PlayerSettingsAI, ReceivePlayerSettingsAI);
RegisterAction(GameAction.GameSettings, ReceiveGameplaySettings);
RegisterAction(GameAction.PlayCard, ReceivePlayCard);
RegisterAction(GameAction.Attack, ReceiveAttackTarget);
RegisterAction(GameAction.AttackPlayer, ReceiveAttackPlayer);
RegisterAction(GameAction.Move, ReceiveMove);
RegisterAction(GameAction.CastAbility, ReceiveCastCardAbility);
RegisterAction(GameAction.SelectCard, ReceiveSelectCard);
RegisterAction(GameAction.SelectPlayer, ReceiveSelectPlayer);
RegisterAction(GameAction.SelectSlot, ReceiveSelectSlot);
RegisterAction(GameAction.SelectChoice, ReceiveSelectChoice);
RegisterAction(GameAction.SelectCost, ReceiveSelectCost);
RegisterAction(GameAction.SelectMulligan, ReceiveSelectMulligan);
RegisterAction(GameAction.CancelSelect, ReceiveCancelSelection);
RegisterAction(GameAction.EndTurn, ReceiveEndTurn);
RegisterAction(GameAction.Resign, ReceiveResign);
RegisterAction(GameAction.ChatMessage, ReceiveChat);
//Events
gameplay.onGameStart += OnGameStart;
gameplay.onGameEnd += OnGameEnd;
gameplay.onTurnStart += OnTurnStart;
gameplay.onRefresh += RefreshAll;
gameplay.onCardPlayed += OnCardPlayed;
gameplay.onCardSummoned += OnCardSummoned;
gameplay.onCardMoved += OnCardMoved;
gameplay.onCardTransformed += OnCardTransformed;
gameplay.onCardDiscarded += OnCardDiscarded;
gameplay.onCardDrawn += OnCardDraw;
gameplay.onRollValue += OnValueRolled;
gameplay.onAbilityStart += OnAbilityStart;
gameplay.onAbilityTargetCard += OnAbilityTargetCard;
gameplay.onAbilityTargetPlayer += OnAbilityTargetPlayer;
gameplay.onAbilityTargetSlot += OnAbilityTargetSlot;
gameplay.onAbilityEnd += OnAbilityEnd;
gameplay.onAttackStart += OnAttackStart;
gameplay.onAttackEnd += OnAttackEnd;
gameplay.onAttackPlayerStart += OnAttackPlayerStart;
gameplay.onAttackPlayerEnd += OnAttackPlayerEnd;
gameplay.onCardDamaged += OnCardDamaged;
gameplay.onPlayerDamaged += OnPlayerDamaged;
gameplay.onCardHealed += OnCardHealed;
gameplay.onPlayerHealed += OnPlayerHealed ;
gameplay.onSecretTrigger += OnSecretTriggered;
gameplay.onSecretResolve += OnSecretResolved;
}
protected virtual void Clear()
{
gameplay.onGameStart -= OnGameStart;
gameplay.onGameEnd -= OnGameEnd;
gameplay.onTurnStart -= OnTurnStart;
gameplay.onRefresh -= RefreshAll;
gameplay.onCardPlayed -= OnCardPlayed;
gameplay.onCardSummoned -= OnCardSummoned;
gameplay.onCardMoved -= OnCardMoved;
gameplay.onCardTransformed -= OnCardTransformed;
gameplay.onCardDiscarded -= OnCardDiscarded;
gameplay.onCardDrawn -= OnCardDraw;
gameplay.onRollValue -= OnValueRolled;
gameplay.onAbilityStart -= OnAbilityStart;
gameplay.onAbilityTargetCard -= OnAbilityTargetCard;
gameplay.onAbilityTargetPlayer -= OnAbilityTargetPlayer;
gameplay.onAbilityTargetSlot -= OnAbilityTargetSlot;
gameplay.onAbilityEnd -= OnAbilityEnd;
gameplay.onAttackStart -= OnAttackStart;
gameplay.onAttackEnd -= OnAttackEnd;
gameplay.onAttackPlayerStart -= OnAttackPlayerStart;
gameplay.onAttackPlayerEnd -= OnAttackPlayerEnd;
gameplay.onCardDamaged -= OnCardDamaged;
gameplay.onPlayerDamaged -= OnPlayerDamaged;
gameplay.onSecretTrigger -= OnSecretTriggered;
gameplay.onSecretResolve -= OnSecretResolved;
}
public virtual void Update()
{
//Game Expiration if no one is connected or game ended
int connected_players = CountConnectedClients();
if (HasGameEnded() || connected_players == 0)
expiration += Time.deltaTime;
//Win expiration if all other players left
if (connected_players == 1 && HasGameStarted() && !HasGameEnded())
win_expiration += Time.deltaTime;
if (is_dedicated_server && !HasGameEnded() && IsWinExpired())
EndExpiredGame();
//Timer during game
if (game_data.state == GameState.Play && !gameplay.IsResolving())
{
game_data.turn_timer -= Time.deltaTime;
if (game_data.turn_timer <= 0f)
{
//Time expired during turn
gameplay.NextStep();
}
}
//Start Game when ready
if (game_data.state == GameState.Connecting)
{
bool all_connected = game_data.AreAllPlayersConnected();
bool all_ready = game_data.AreAllPlayersReady();
if (all_connected && all_ready)
{
StartGame();
}
}
//Process queued actions
if (queued_actions.Count > 0 && !gameplay.IsResolving())
{
QueuedGameAction action = queued_actions.Dequeue();
ExecuteAction(action.type, action.client, action.sdata);
}
//Update game logic
gameplay.Update(Time.deltaTime);
//Update AI
foreach (AIPlayer ai in ai_list)
{
ai.Update();
}
}
protected virtual void StartGame()
{
//Setup AI
bool ai_vs_ai = !is_dedicated_server && GameplayData.Get().ai_vs_ai;
foreach (Player player in game_data.players)
{
if (player.is_ai || ai_vs_ai)
{
AIPlayer ai_gameplay = AIPlayer.Create(GameplayData.Get().ai_type, gameplay, player.player_id, player.ai_level);
ai_list.Add(ai_gameplay);
}
}
//Start Game
gameplay.StartGame();
}
//End game when it has expired (only one player is still connected)
protected virtual void EndExpiredGame()
{
Game gdata = gameplay.GetGameData();
foreach (Player player in gdata.players)
{
if (player.IsConnected())
{
gameplay.EndGame(player.player_id);
return;
}
}
}
//------ Receive Actions -------
private void RegisterAction(ushort tag, UnityAction<ClientData, SerializedData> callback)
{
CommandEvent cmdevt = new CommandEvent();
cmdevt.tag = tag;
cmdevt.callback = callback;
registered_commands.Add(tag, cmdevt);
}
public void ReceiveAction(ulong client_id, FastBufferReader reader)
{
ClientData client = GetClient(client_id);
if (client != null)
{
reader.ReadValueSafe(out ushort type);
SerializedData sdata = new SerializedData(reader);
if (!gameplay.IsResolving())
{
//Not resolving, execute now
ExecuteAction(type, client, sdata);
}
else
{
//Resolving, wait before executing
QueuedGameAction action = new QueuedGameAction();
action.type = type;
action.client = client;
action.sdata = sdata;
sdata.PreRead();
queued_actions.Enqueue(action);
}
}
}
public void ExecuteAction(ushort type, ClientData client, SerializedData sdata)
{
bool found = registered_commands.TryGetValue(type, out CommandEvent command);
if(found)
command.callback.Invoke(client, sdata);
}
//-------
public void ReceivePlayerSettings(ClientData iclient, SerializedData sdata)
{
PlayerSettings msg = sdata.Get<PlayerSettings>();
Player player = GetPlayer(iclient);
if (player != null && msg != null)
{
SetPlayerSettings(player.player_id, msg);
}
}
public void ReceivePlayerSettingsAI(ClientData iclient, SerializedData sdata)
{
PlayerSettings msg = sdata.Get<PlayerSettings>();
Player player = GetPlayer(iclient);
if (player != null && msg != null)
{
SetPlayerSettingsAI(player.player_id, msg);
}
}
public void ReceiveGameplaySettings(ClientData iclient, SerializedData sdata)
{
GameSettings settings = sdata.Get<GameSettings>();
if (settings != null)
{
SetGameSettings(settings);
}
}
public void ReceivePlayCard(ClientData iclient, SerializedData sdata)
{
MsgPlayCard msg = sdata.Get<MsgPlayCard>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerActionTurn(player) && !gameplay.IsResolving())
{
Card card = player.GetCard(msg.card_uid);
if (card != null && card.player_id == player.player_id)
gameplay.PlayCard(card, msg.slot);
}
}
public void ReceiveAttackTarget(ClientData iclient, SerializedData sdata)
{
MsgAttack msg = sdata.Get<MsgAttack>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerActionTurn(player) && !gameplay.IsResolving())
{
Card attacker = player.GetCard(msg.attacker_uid);
Card target = game_data.GetCard(msg.target_uid);
if (attacker != null && target != null && attacker.player_id == player.player_id)
{
gameplay.AttackTarget(attacker, target);
}
}
}
public void ReceiveAttackPlayer(ClientData iclient, SerializedData sdata)
{
MsgAttackPlayer msg = sdata.Get<MsgAttackPlayer>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerActionTurn(player) && !gameplay.IsResolving())
{
Card attacker = player.GetCard(msg.attacker_uid);
Player target = game_data.GetPlayer(msg.target_id);
if (attacker != null && target != null && attacker.player_id == player.player_id)
{
gameplay.AttackPlayer(attacker, target);
}
}
}
public void ReceiveMove(ClientData iclient, SerializedData sdata)
{
MsgPlayCard msg = sdata.Get<MsgPlayCard>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerActionTurn(player) && !gameplay.IsResolving())
{
Card card = player.GetCard(msg.card_uid);
if (card != null && card.player_id == player.player_id)
gameplay.MoveCard(card, msg.slot);
}
}
public void ReceiveCastCardAbility(ClientData iclient, SerializedData sdata)
{
MsgCastAbility msg = sdata.Get<MsgCastAbility>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerActionTurn(player) && !gameplay.IsResolving())
{
Card card = player.GetCard(msg.caster_uid);
AbilityData iability = AbilityData.Get(msg.ability_id);
if (card != null && card.player_id == player.player_id)
gameplay.CastAbility(card, iability);
}
}
public void ReceiveSelectCard(ClientData iclient, SerializedData sdata)
{
MsgCard msg = sdata.Get<MsgCard>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
Card target = game_data.GetCard(msg.card_uid);
gameplay.SelectCard(target);
}
}
public void ReceiveSelectPlayer(ClientData iclient, SerializedData sdata)
{
MsgPlayer msg = sdata.Get<MsgPlayer>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
Player target = game_data.GetPlayer(msg.player_id);
gameplay.SelectPlayer(target);
}
}
public void ReceiveSelectSlot(ClientData iclient, SerializedData sdata)
{
Slot slot = sdata.Get<Slot>();
Player player = GetPlayer(iclient);
if (player != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
if(slot != null && slot.IsValid())
gameplay.SelectSlot(slot);
}
}
public void ReceiveSelectChoice(ClientData iclient, SerializedData sdata)
{
MsgInt msg = sdata.Get<MsgInt>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
gameplay.SelectChoice(msg.value);
}
}
public void ReceiveSelectCost(ClientData iclient, SerializedData sdata)
{
MsgInt msg = sdata.Get<MsgInt>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
gameplay.SelectCost(msg.value);
}
}
public void ReceiveCancelSelection(ClientData iclient, SerializedData sdata)
{
Player player = GetPlayer(iclient);
if (player != null && game_data.IsPlayerSelectorTurn(player) && !gameplay.IsResolving())
{
gameplay.CancelSelection();
}
}
public void ReceiveSelectMulligan(ClientData iclient, SerializedData sdata)
{
MsgMulligan msg = sdata.Get<MsgMulligan>();
Player player = GetPlayer(iclient);
if (player != null && msg != null && game_data.IsPlayerMulliganTurn(player) && !gameplay.IsResolving())
{
gameplay.Mulligan(player, msg.cards);
}
}
public void ReceiveEndTurn(ClientData iclient, SerializedData sdata)
{
Player player = GetPlayer(iclient);
if (player != null && game_data.IsPlayerTurn(player))
{
gameplay.NextStep();
}
}
public void ReceiveResign(ClientData iclient, SerializedData sdata)
{
Player player = GetPlayer(iclient);
if (player != null && game_data.state != GameState.Connecting && game_data.state != GameState.GameEnded)
{
int winner = player.player_id == 0 ? 1 : 0;
gameplay.EndGame(winner);
}
}
public void ReceiveChat(ClientData iclient, SerializedData sdata)
{
MsgChat msg = sdata.Get<MsgChat>();
Player player = GetPlayer(iclient);
if (player != null && msg != null)
{
msg.player_id = player.player_id; //Force player id to sending client to avoid spoofing
SendToAll(GameAction.ChatMessage, msg, NetworkDelivery.Reliable);
}
}
//--- Setup Commands ------
public virtual async void SetPlayerDeck(int player_id, string username, UserDeckData deck)
{
Player player = game_data.GetPlayer(player_id);
if (player != null && game_data.state == GameState.Connecting)
{
UserData user = Authenticator.Get().UserData; //Offline game, get local user
if(Authenticator.Get().IsApi())
user = await ApiClient.Get().LoadUserData(username); //Online game, validate from api
//Use user API deck
UserDeckData udeck = user?.GetDeck(deck.tid);
if (user != null && udeck != null)
{
if (user.IsDeckValid(udeck))
{
gameplay.SetPlayerDeck(player, udeck);
SendPlayerReady(player);
return;
}
else
{
Debug.Log(user.username + " deck is invalid: " + udeck.title);
return;
}
}
//Use premade deck
DeckData cdeck = DeckData.Get(deck.tid);
if (cdeck != null)
gameplay.SetPlayerDeck(player, cdeck);
//Trust client in test mode
else if (Authenticator.Get().IsTest())
gameplay.SetPlayerDeck(player, deck);
//Deck not found
else
Debug.Log("Player " + player_id + " deck not found: " + deck.tid);
SendPlayerReady(player);
}
}
public virtual void SetPlayerSettings(int player_id, PlayerSettings psettings)
{
if (game_data.state != GameState.Connecting)
return; //Cant send setting if game already started
Player player = game_data.GetPlayer(player_id);
if (player != null && !player.ready)
{
player.avatar = psettings.avatar;
player.cardback = psettings.cardback;
player.is_ai = false;
player.ready = true;
SetPlayerDeck(player_id, player.username, psettings.deck);
RefreshAll();
}
}
public virtual void SetPlayerSettingsAI(int player_id, PlayerSettings psettings)
{
if (game_data.state != GameState.Connecting)
return; //Cant send setting if game already started
if (is_dedicated_server)
return; //No AI allowed online server
Player player = game_data.GetOpponentPlayer(player_id);
if (player != null && !player.ready)
{
player.username = psettings.username;
player.avatar = psettings.avatar;
player.cardback = psettings.cardback;
player.is_ai = true;
player.ready = true;
player.ai_level = psettings.ai_level;
SetPlayerDeck(player.player_id, player.username, psettings.deck);
RefreshAll();
}
}
public virtual void SetGameSettings(GameSettings settings)
{
if (game_data.state == GameState.Connecting)
{
game_data.settings = settings;
RefreshAll();
}
}
//-------------
public void AddClient(ClientData client)
{
if (!connected_clients.Contains(client))
connected_clients.Add(client);
}
public void RemoveClient(ClientData client)
{
connected_clients.Remove(client);
Player player = GetPlayer(client);
if (player != null && player.connected)
{
player.connected = false;
RefreshAll();
}
}
public ClientData GetClient(ulong client_id)
{
foreach (ClientData client in connected_clients)
{
if (client.client_id == client_id)
return client;
}
return null;
}
public int AddPlayer(ClientData client)
{
if (!players.Contains(client))
players.Add(client);
int player_id = FindPlayerID(client.user_id);
Player player = game_data.GetPlayer(player_id);
if (player != null)
{
player.username = client.username;
player.connected = true;
}
return player_id;
}
public int FindPlayerID(string user_id)
{
int index = 0;
foreach (ClientData player in players)
{
if (player.user_id == user_id)
return index;
index++;
}
return -1;
}
public Player GetPlayer(ClientData client)
{
return GetPlayer(client.user_id);
}
public Player GetPlayer(string user_id)
{
int player_id = FindPlayerID(user_id);
return game_data?.GetPlayer(player_id);
}
public bool IsPlayer(string user_id)
{
Player player = GetPlayer(user_id);
return player != null;
}
public bool IsConnectedPlayer(string user_id)
{
Player player = GetPlayer(user_id);
return player != null && player.connected;
}
public int CountPlayers()
{
return players.Count;
}
public int CountConnectedClients()
{
int nb = 0;
Game game = GetGameData();
foreach (Player player in game.players)
{
if (player.IsConnected())
{
nb++;
}
}
return nb;
}
public Game GetGameData()
{
return gameplay.GetGameData();
}
public virtual bool HasGameStarted()
{
return gameplay.IsGameStarted();
}
public virtual bool HasGameEnded()
{
return gameplay.IsGameEnded();
}
public virtual bool IsGameExpired()
{
return expiration > game_expire_time; //Means that the game expired (everyone left or game ended)
}
public virtual bool IsWinExpired()
{
return win_expiration > win_expire_time; //Means that only one player is left, and he should win
}
protected virtual void OnGameStart()
{
SendToAll(GameAction.GameStart);
if (is_dedicated_server && Authenticator.Get().IsApi())
{
//Create Match
ApiClient.Get().CreateMatch(game_data);
}
}
protected virtual void OnGameEnd(Player winner)
{
MsgPlayer msg = new MsgPlayer();
msg.player_id = winner != null ? winner.player_id : -1;
SendToAll(GameAction.GameEnd, msg, NetworkDelivery.Reliable);
if (is_dedicated_server && Authenticator.Get().IsApi())
{
//End Match and give rewards
ApiClient.Get().EndMatch(game_data, winner.player_id);
}
}
protected virtual void OnTurnStart()
{
MsgPlayer msg = new MsgPlayer();
msg.player_id = game_data.current_player;
SendToAll(GameAction.NewTurn, msg, NetworkDelivery.Reliable);
}
protected virtual void OnCardPlayed(Card card, Slot slot)
{
MsgPlayCard mdata = new MsgPlayCard();
mdata.card_uid = card.uid;
mdata.slot = slot;
SendToAll(GameAction.CardPlayed, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardMoved(Card card, Slot slot)
{
MsgPlayCard mdata = new MsgPlayCard();
mdata.card_uid = card.uid;
mdata.slot = slot;
SendToAll(GameAction.CardMoved, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardSummoned(Card card, Slot slot)
{
MsgPlayCard mdata = new MsgPlayCard();
mdata.card_uid = card.uid;
mdata.slot = slot;
SendToAll(GameAction.CardSummoned, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardTransformed(Card card)
{
MsgCard mdata = new MsgCard();
mdata.card_uid = card.uid;
SendToAll(GameAction.CardTransformed, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardDiscarded(Card card)
{
MsgCard mdata = new MsgCard();
mdata.card_uid = card.uid;
SendToAll(GameAction.CardDiscarded, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardDraw(int nb)
{
MsgInt mdata = new MsgInt();
mdata.value = nb;
SendToAll(GameAction.CardDrawn, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnValueRolled(int nb)
{
MsgInt mdata = new MsgInt();
mdata.value = nb;
SendToAll(GameAction.ValueRolled, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAttackStart(Card attacker, Card target)
{
MsgAttack mdata = new MsgAttack();
mdata.attacker_uid = attacker.uid;
mdata.target_uid = target.uid;
mdata.damage = 0;
SendToAll(GameAction.AttackStart, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAttackEnd(Card attacker, Card target)
{
MsgAttack mdata = new MsgAttack();
mdata.attacker_uid = attacker.uid;
mdata.target_uid = target.uid;
mdata.damage = 0;
SendToAll(GameAction.AttackEnd, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAttackPlayerStart(Card attacker, Player target)
{
MsgAttackPlayer mdata = new MsgAttackPlayer();
mdata.attacker_uid = attacker.uid;
mdata.target_id = target.player_id;
mdata.damage = 0;
SendToAll(GameAction.AttackPlayerStart, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAttackPlayerEnd(Card attacker, Player target)
{
MsgAttackPlayer mdata = new MsgAttackPlayer();
mdata.attacker_uid = attacker.uid;
mdata.target_id = target.player_id;
mdata.damage = 0;
SendToAll(GameAction.AttackPlayerEnd, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardDamaged(Card card, int damage)
{
MsgCardValue mdata = new MsgCardValue();
mdata.card_uid = card.uid;
mdata.value = damage;
SendToAll(GameAction.CardDamaged, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnPlayerDamaged(Player player, int damage)
{
MsgPlayerValue mdata = new MsgPlayerValue();
mdata.player_id = player.player_id;
mdata.value = damage;
SendToAll(GameAction.PlayerDamaged, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnCardHealed(Card card, int hp)
{
MsgCardValue mdata = new MsgCardValue();
mdata.card_uid = card.uid;
mdata.value = hp;
SendToAll(GameAction.CardHealed, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnPlayerHealed(Player player, int hp)
{
MsgPlayerValue mdata = new MsgPlayerValue();
mdata.player_id = player.player_id;
mdata.value = hp;
SendToAll(GameAction.PlayerHealed, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAbilityStart(AbilityData ability, Card caster)
{
MsgCastAbility mdata = new MsgCastAbility();
mdata.ability_id = ability.id;
mdata.caster_uid = caster.uid;
mdata.target_uid = "";
SendToAll(GameAction.AbilityTrigger, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAbilityTargetCard(AbilityData ability, Card caster, Card target)
{
MsgCastAbility mdata = new MsgCastAbility();
mdata.ability_id = ability.id;
mdata.caster_uid = caster.uid;
mdata.target_uid = target != null ? target.uid : "";
SendToAll(GameAction.AbilityTargetCard, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAbilityTargetPlayer(AbilityData ability, Card caster, Player target)
{
MsgCastAbilityPlayer mdata = new MsgCastAbilityPlayer();
mdata.ability_id = ability.id;
mdata.caster_uid = caster.uid;
mdata.target_id = target != null ? target.player_id : -1;
SendToAll(GameAction.AbilityTargetPlayer, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAbilityTargetSlot(AbilityData ability, Card caster, Slot target)
{
MsgCastAbilitySlot mdata = new MsgCastAbilitySlot();
mdata.ability_id = ability.id;
mdata.caster_uid = caster.uid;
mdata.slot = target;
SendToAll(GameAction.AbilityTargetSlot, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnAbilityEnd(AbilityData ability, Card caster)
{
MsgCastAbility mdata = new MsgCastAbility();
mdata.ability_id = ability.id;
mdata.caster_uid = caster.uid;
mdata.target_uid = "";
SendToAll(GameAction.AbilityEnd, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnSecretTriggered(Card secret, Card trigger)
{
MsgSecret mdata = new MsgSecret();
mdata.secret_uid = secret.uid;
mdata.triggerer_uid = trigger != null ? trigger.uid : "";
SendToAll(GameAction.SecretTriggered, mdata, NetworkDelivery.Reliable);
}
protected virtual void OnSecretResolved(Card secret, Card trigger)
{
MsgSecret mdata = new MsgSecret();
mdata.secret_uid = secret.uid;
mdata.triggerer_uid = trigger != null ? trigger.uid : "";
SendToAll(GameAction.SecretResolved, mdata, NetworkDelivery.Reliable);
}
protected virtual void SendPlayerReady(Player player)
{
if (player != null && player.IsReady())
{
MsgInt mdata = new MsgInt();
mdata.value = player.player_id;
SendToAll(GameAction.PlayerReady, mdata, NetworkDelivery.Reliable);
}
}
public virtual void RefreshAll()
{
MsgRefreshAll mdata = new MsgRefreshAll();
mdata.game_data = GetGameData();
SendToAll(GameAction.RefreshAll, mdata, NetworkDelivery.ReliableFragmentedSequenced);
}
public void SendToAll(ushort tag)
{
FastBufferWriter writer = new FastBufferWriter(128, Unity.Collections.Allocator.Temp, TcgNetwork.MsgSizeMax);
writer.WriteValueSafe(tag);
foreach (ClientData iclient in connected_clients)
{
if (iclient != null)
{
Messaging.Send("refresh", iclient.client_id, writer, NetworkDelivery.Reliable);
}
}
writer.Dispose();
}
public void SendToAll(ushort tag, INetworkSerializable data, NetworkDelivery delivery)
{
FastBufferWriter writer = new FastBufferWriter(128, Unity.Collections.Allocator.Temp, TcgNetwork.MsgSizeMax);
writer.WriteValueSafe(tag);
writer.WriteNetworkSerializable(data);
foreach (ClientData iclient in connected_clients)
{
if (iclient != null)
{
Messaging.Send("refresh", iclient.client_id, writer, delivery);
}
}
writer.Dispose();
}
public ulong ServerID { get { return TcgNetwork.Get().ServerID; } }
public NetworkMessaging Messaging { get { return TcgNetwork.Get().Messaging; } }
}
public struct QueuedGameAction
{
public ushort type;
public ClientData client;
public SerializedData sdata;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d49bc33cf0fcdd041b6cb7b05d0688de
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,291 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.Events;
using Unity.Netcode;
namespace TcgEngine.Server
{
/// <summary>
/// Top-level server script, that manages new connection, and assign players to the right match
/// Will also receive game actions and send them to the appropirate game
/// Can contain multiple games at once (GameServer)
/// </summary>
public class ServerManager : MonoBehaviour
{
[Header("API")]
public string api_username;
public string api_password;
private Dictionary<ulong, ClientData> client_list = new Dictionary<ulong, ClientData>(); //List of clients
private Dictionary<string, GameServer> game_list = new Dictionary<string, GameServer>(); //List of games
private List<string> game_remove_list = new List<string>();
private float login_timer = 0f;
protected virtual void Awake()
{
Application.runInBackground = true;
Application.targetFrameRate = 200; //Limit server frame rate to prevent using 100% cpu
}
protected virtual void Start()
{
TcgNetwork network = TcgNetwork.Get();
network.onClientJoin += OnClientConnected;
network.onClientQuit += OnClientDisconnected;
Messaging.ListenMsg("connect", ReceiveConnectPlayer);
Messaging.ListenMsg("action", ReceiveGameAction);
if (!network.IsActive())
{
network.StartServer(NetworkData.Get().port);
}
Login();
}
protected virtual void Update()
{
//Update games and Destroy games with no players
foreach (KeyValuePair<string, GameServer> pair in game_list)
{
GameServer gserver = pair.Value;
gserver.Update();
if (gserver.IsGameExpired())
game_remove_list.Add(pair.Key);
}
foreach (string key in game_remove_list)
{
game_list.Remove(key);
if (ServerMatchmaker.Get())
ServerMatchmaker.Get().EndMatch(key);
}
game_remove_list.Clear();
//Re login
login_timer += Time.deltaTime;
if (login_timer > 15f && !Authenticator.Get().IsConnected())
{
login_timer = 0f;
Login();
}
}
protected virtual async void Login()
{
await Authenticator.Get().Login(api_username, api_password);
bool success = Authenticator.Get().IsConnected();
int permission = Authenticator.Get().GetPermission();
string api = Authenticator.Get().IsApi() ? "API" : "Local";
Debug.Log(api + " authentication: " + success + " (" + permission + ")");
//If login fail, login again
if (!success)
{
TimeTool.WaitFor(5f, () =>
{
if (!Authenticator.Get().IsConnected())
{
Login();
}
});
}
}
protected virtual void OnClientConnected(ulong client_id)
{
ClientData iclient = new ClientData(client_id);
client_list[client_id] = iclient;
}
protected virtual void OnClientDisconnected(ulong client_id)
{
ClientData iclient = GetClient(client_id);
client_list.Remove(client_id);
ReceiveDisconnectPlayer(iclient);
}
protected virtual void ReceiveConnectPlayer(ulong client_id, FastBufferReader reader)
{
ClientData iclient = GetClient(client_id);
reader.ReadNetworkSerializable(out MsgPlayerConnect msg);
if (iclient != null && msg != null)
{
if (string.IsNullOrWhiteSpace(msg.username))
return;
if (string.IsNullOrWhiteSpace(msg.game_uid))
return;
Debug.Log("Client " + client_id + " connecting to game: " + msg.game_uid);
//Connect to game as player or observer
if (msg.observer)
ConnectObserverToGame(iclient, msg.user_id, msg.username, msg.game_uid);
else
ConnectPlayerToGame(iclient, msg.user_id, msg.username, msg.game_uid, msg.nb_players);
GameServer gserver = GetGame(msg.game_uid);
if(gserver != null)
gserver.RefreshAll();
}
}
protected virtual void ReceiveDisconnectPlayer(ClientData iclient)
{
if (iclient == null)
return;
GameServer gserver = GetGame(iclient.game_uid);
if (gserver != null)
{
gserver.RemoveClient(iclient);
}
}
protected virtual void ReceiveGameAction(ulong client_id, FastBufferReader reader)
{
ClientData client = GetClient(client_id);
if (client != null)
{
GameServer gserver = GetGame(client.game_uid);
if (gserver != null && gserver.IsConnectedPlayer(client.user_id))
gserver.ReceiveAction(client_id, reader);
}
}
//Player wants to connect to game_uid
protected virtual void ConnectPlayerToGame(ClientData client, string user_id, string username, string game_uid, int nb_players)
{
//Create game
GameServer gserver = GetGame(game_uid);
if (gserver == null)
gserver = CreateGame(game_uid, nb_players);
bool can_connect = gserver.IsPlayer(user_id) || gserver.CountPlayers() < gserver.nb_players;
if (gserver != null && can_connect)
{
//Add player to game
client.game_uid = game_uid;
client.user_id = user_id;
client.username = username;
gserver.AddClient(client);
int player_id = gserver.AddPlayer(client);
//Send back result
MsgAfterConnected msg_data = new MsgAfterConnected();
msg_data.success = true;
msg_data.player_id = player_id;
msg_data.game_data = gserver.GetGameData();
SendToClient(client.client_id, GameAction.Connected, msg_data, NetworkDelivery.ReliableFragmentedSequenced);
}
}
//Player wants to connect to game_uid as observer
protected virtual void ConnectObserverToGame(ClientData client, string user_id, string username, string game_uid)
{
GameServer gserver = GetGame(game_uid);
if (gserver != null && client != null)
{
//Set client data
client.game_uid = game_uid;
client.user_id = user_id;
client.username = username;
gserver.AddClient(client);
//Return request
MsgAfterConnected msg_data = new MsgAfterConnected();
msg_data.success = true;
msg_data.player_id = -1;
msg_data.game_data = gserver.GetGameData();
SendToClient(client.client_id, GameAction.Connected, msg_data, NetworkDelivery.ReliableFragmentedSequenced);
}
}
public void SendToClient(ulong client_id, ushort tag, INetworkSerializable data, NetworkDelivery delivery)
{
FastBufferWriter writer = new FastBufferWriter(128, Unity.Collections.Allocator.Temp, TcgNetwork.MsgSizeMax);
writer.WriteValueSafe(tag);
writer.WriteNetworkSerializable(data);
Messaging.Send("refresh", client_id, writer, delivery);
writer.Dispose();
}
public void SendMsgToClient(ushort client_id, string msg)
{
FastBufferWriter writer = new FastBufferWriter(128, Unity.Collections.Allocator.Temp, TcgNetwork.MsgSizeMax);
writer.WriteValueSafe(GameAction.ServerMessage);
writer.WriteValueSafe(msg);
Messaging.Send("refresh", client_id, writer, NetworkDelivery.Reliable);
writer.Dispose();
}
public GameServer CreateGame(string uid, int nb_players)
{
GameServer game = new GameServer(uid, nb_players, true);
game_list[game.game_uid] = game;
return game;
}
public void RemoveGame(string game_id)
{
game_list.Remove(game_id);
}
public GameServer GetGame(string game_uid)
{
if (string.IsNullOrEmpty(game_uid))
return null;
if (game_list.ContainsKey(game_uid))
return game_list[game_uid];
return null;
}
public ClientData GetClient(ulong client_id)
{
if (client_list.ContainsKey(client_id))
return client_list[client_id];
return null;
}
public ClientData GetClientByUser(string username)
{
foreach (KeyValuePair<ulong, ClientData> pair in client_list)
{
if (pair.Value.username == username)
return pair.Value;
}
return null;
}
public ulong ServerID { get { return TcgNetwork.Get().ServerID; } }
public NetworkMessaging Messaging { get { return TcgNetwork.Get().Messaging; } }
}
public class ClientData
{
public ulong client_id; //index of the connection
public string user_id; //Player user_id, in auth system
public string username; //Player username
public string game_uid; //Unique id for the game
public ClientData(ulong id) { client_id = id; }
}
public class CommandEvent
{
public ushort tag;
public UnityAction<ClientData, SerializedData> callback;
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3e451cc077529a6449060b1c4e92825f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,136 @@
using System.Collections;
using System.Collections.Generic;
using Unity.Netcode;
using UnityEngine;
using TcgEngine.Client;
namespace TcgEngine.Server
{
/// <summary>
/// Local server running on the client to play in solo mode against AI
/// Contains only one GameServer
/// </summary>
public class ServerManagerLocal : MonoBehaviour
{
private GameServer server;
private Dictionary<ulong, ClientData> client_list = new Dictionary<ulong, ClientData>(); //List of clients
protected virtual void Start()
{
if (GameClient.game_settings.IsHost())
{
StartServer(); //Start local server if not playing online
}
}
protected virtual void StartServer()
{
TcgNetwork network = TcgNetwork.Get();
network.onClientJoin += OnClientJoin;
network.onClientQuit += OnClientQuit;
network.Messaging.ListenMsg("connect", ReceiveConnectPlayer);
network.Messaging.ListenMsg("action", ReceiveGameAction);
client_list[network.ServerID] = new ClientData(network.ServerID); //Add yourself
server = new GameServer(GameClient.game_settings.game_uid, GameClient.game_settings.nb_players, false);
}
protected virtual void OnDestroy()
{
TcgNetwork network = TcgNetwork.Get();
if (network != null)
{
network.onClientJoin -= OnClientJoin;
network.onClientQuit -= OnClientQuit;
network.Messaging.UnListenMsg("connect");
network.Messaging.UnListenMsg("action");
}
}
protected virtual void OnClientJoin(ulong client_id)
{
client_list[client_id] = new ClientData(client_id);
}
protected virtual void OnClientQuit(ulong client_id)
{
ClientData client = GetClient(client_id);
server?.RemoveClient(client);
client_list.Remove(client_id);
}
protected virtual void Update()
{
if (server != null)
server.Update();
}
protected virtual void ReceiveConnectPlayer(ulong client_id, FastBufferReader reader)
{
reader.ReadNetworkSerializable(out MsgPlayerConnect msg);
if (msg != null)
{
if (string.IsNullOrWhiteSpace(msg.username))
return;
if (string.IsNullOrWhiteSpace(msg.game_uid))
return;
ClientData client = GetClient(client_id);
if (client == null)
return;
bool can_connect = server.IsPlayer(msg.user_id) || server.CountPlayers() < server.nb_players;
if (can_connect)
{
client.game_uid = msg.game_uid;
client.user_id = msg.user_id;
client.username = msg.username;
server.AddClient(client);
int player_id = server.AddPlayer(client);
//Send back result
MsgAfterConnected msg_data = new MsgAfterConnected();
msg_data.success = true;
msg_data.player_id = player_id;
msg_data.game_data = server.GetGameData();
SendToClient(client_id, GameAction.Connected, msg_data, NetworkDelivery.ReliableFragmentedSequenced);
}
}
}
protected virtual void ReceiveGameAction(ulong client_id, FastBufferReader reader)
{
ClientData client = GetClient(client_id);
if (client != null)
{
if (server.IsConnectedPlayer(client.user_id))
server.ReceiveAction(client_id, reader);
}
}
public void SendToClient(ulong client_id, ushort tag, INetworkSerializable data, NetworkDelivery delivery)
{
FastBufferWriter writer = new FastBufferWriter(128, Unity.Collections.Allocator.Temp, TcgNetwork.MsgSizeMax);
writer.WriteValueSafe(tag);
writer.WriteNetworkSerializable(data);
Messaging.Send("refresh", client_id, writer, delivery);
writer.Dispose();
}
public ClientData GetClient(ulong client_id)
{
if (client_list.ContainsKey(client_id))
return client_list[client_id];
return null;
}
public ulong ServerID { get { return TcgNetwork.Get().ServerID; } }
public NetworkMessaging Messaging { get { return TcgNetwork.Get().Messaging; } }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d649bee306cfd694f8e16d7bf02f1b97
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,307 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Netcode;
namespace TcgEngine.Server
{
/// <summary>
/// Main server script for the matchmaker
/// will receive player request and then match players together and send them the game uid and game url to connect to
/// </summary>
public class ServerMatchmaker : MonoBehaviour
{
[Header("Matchmaker")]
public string[] servers;
private Dictionary<ulong, ClientData> client_list = new Dictionary<ulong, ClientData>(); //List of clients
private Dictionary<string, MatchPlayerData> matchmaking_players = new Dictionary<string, MatchPlayerData>(); //Get deleted every 20 sec
private Dictionary<string, MatchData> matched_players = new Dictionary<string, MatchData>(); //user_id -> match
private List<MatchPlayerData> valid_users = new List<MatchPlayerData>(); //temporary array
private float matchmake_timer = 0f;
private static ServerMatchmaker _instance;
protected virtual void Awake()
{
_instance = this;
Application.runInBackground = true;
}
protected virtual void Start()
{
TcgNetwork network = TcgNetwork.Get();
network.onClientJoin += OnClientConnected;
network.onClientQuit += OnClientDisconnected;
Messaging.ListenMsg("matchmaking", ReceiveMatchmaking);
Messaging.ListenMsg("matchmaking_list", ReceiveMatchmakingList);
Messaging.ListenMsg("match_list", ReceiveMatchList);
if (!network.IsActive())
{
network.StartServer(NetworkData.Get().port);
}
}
protected virtual void Update()
{
//Matchmaking
matchmake_timer += Time.deltaTime;
if (matchmake_timer > 20f)
{
matchmake_timer = 0f;
matchmaking_players.Clear(); //Delete and restart, to make sure you only keep recent players
}
}
protected virtual void OnClientConnected(ulong client_id)
{
ClientData iclient = new ClientData(client_id);
client_list[client_id] = iclient;
}
protected virtual void OnClientDisconnected(ulong client_id)
{
if (client_list.ContainsKey(client_id))
{
ClientData iclient = client_list[client_id];
if(iclient.username != null)
matchmaking_players.Remove(iclient.user_id);
client_list.Remove(client_id);
}
}
protected virtual void ReceiveMatchmaking(ulong client_id, FastBufferReader reader)
{
ClientData iclient = GetClient(client_id);
reader.ReadNetworkSerializable(out MsgMatchmaking msg);
if (iclient == null || string.IsNullOrWhiteSpace(msg.user_id) || string.IsNullOrWhiteSpace(msg.username))
return;
string user_id = msg.user_id;
bool is_refresh = msg.refresh;
iclient.user_id = msg.user_id;
iclient.username = msg.username;
//Restart matching
if (!is_refresh)
matched_players.Remove(user_id);
//Check if already matched
if (matched_players.ContainsKey(user_id))
{
MatchData match = matched_players[user_id];
if (!match.ended)
{
SendMatchmakingResponse(iclient, match, msg.group, match.players.Length); //Was already matched, return saved result!
return;
}
}
//Data
MatchPlayerData pdata = new MatchPlayerData();
pdata.user_id = msg.user_id;
pdata.username = msg.username;
pdata.group = msg.group;
pdata.elo_rank = msg.elo;
pdata.nb_players = msg.players;
//Add to matchking players
if (!matchmaking_players.ContainsKey(user_id))
matchmaking_players.Add(user_id, pdata);
//Start searching for other valid players
float wait_max = 20f;
int variance_max = 2000;
bool friendly = msg.group.StartsWith("u_");
float wait_timer = msg.time;
float wait_value = Mathf.Clamp01(wait_timer / wait_max);
int elo_variance = Mathf.RoundToInt(wait_value * variance_max);
valid_users.Clear();
valid_users.Add(pdata); //Add self
foreach (KeyValuePair<string, MatchPlayerData> opair in matchmaking_players)
{
string auser_id = opair.Key;
MatchPlayerData adata = opair.Value;
int diff = Mathf.Abs(adata.elo_rank - msg.elo);
bool same_group = adata.group == msg.group;
bool same_players = adata.nb_players == msg.players;
bool valid_elo = friendly || diff < elo_variance;
if (auser_id != user_id && valid_elo && same_group && same_players)
{
valid_users.Add(adata);
}
}
//Not enough players found, send current count
if (valid_users.Count < msg.players)
{
SendMatchmakingResponse(iclient, null, msg.group, valid_users.Count);
return; //Not enough valid users
}
//Match success, send result
string prefix = msg.group.Length >= 2 ? msg.group.Substring(0, 2) : "";
string game_code = prefix + GameTool.GenerateRandomID(12, 15);
string game_url = ""; //Empty url means it will use the default NetworkData url set on the client
if (servers.Length > 0)
game_url = servers[Random.Range(0, servers.Length)];
int pindex = 0;
MatchData nmatch = new MatchData(msg.group, game_code, game_url, msg.players);
foreach (MatchPlayerData vuser in valid_users)
{
if (pindex < nmatch.players.Length)
{
matchmaking_players.Remove(vuser.user_id);
matched_players[vuser.user_id] = nmatch;
nmatch.players[pindex] = vuser.username;
pindex++;
}
}
//Send response to current request
if (matched_players.ContainsKey(user_id))
{
SendMatchmakingResponse(iclient, nmatch, nmatch.group, nmatch.players.Length); //Just matched to new player!
}
}
protected virtual void SendMatchmakingResponse(ClientData iclient, MatchData match, string group, int players)
{
MatchmakingResult msg_match = new MatchmakingResult();
msg_match.success = match != null;
msg_match.players = players;
msg_match.group = group;
msg_match.game_uid = match != null ? match.game_uid : "";
msg_match.server_url = match != null ? match.server_url : "";
Messaging.SendObject("matchmaking", iclient.client_id, msg_match, NetworkDelivery.Reliable);
}
protected virtual void ReceiveMatchmakingList(ulong client_id, FastBufferReader reader)
{
reader.ReadNetworkSerializable(out MsgMatchmakingList msg);
List<MatchmakingListItem> items = new List<MatchmakingListItem>();
foreach (KeyValuePair<string, MatchPlayerData> pair in matchmaking_players)
{
if (string.IsNullOrEmpty(msg.username) || pair.Key == msg.username)
{
MatchPlayerData pdata = pair.Value;
MatchmakingListItem item = new MatchmakingListItem();
item.group = pdata.group;
item.user_id = pdata.user_id;
item.username = pdata.username;
items.Add(item);
}
}
MatchmakingList msg_list = new MatchmakingList();
msg_list.items = items.ToArray();
Messaging.SendObject("matchmaking_list", client_id, msg_list, NetworkDelivery.Reliable);
}
protected virtual void ReceiveMatchList(ulong client_id, FastBufferReader reader)
{
reader.ReadNetworkSerializable(out MsgMatchmakingList msg);
List<MatchListItem> items = new List<MatchListItem>();
foreach (KeyValuePair<string, MatchData> pair in matched_players)
{
if (!pair.Value.ended)
{
if (string.IsNullOrEmpty(msg.username) || Contains(pair.Value.players, msg.username))
{
MatchData pdata = pair.Value;
MatchListItem item = new MatchListItem();
item.group = pair.Value.group;
item.username = msg.username;
item.game_uid = pdata.game_uid;
item.game_url = pdata.server_url;
items.Add(item);
}
}
}
MatchList msg_list = new MatchList();
msg_list.items = items.ToArray();
Messaging.SendObject("match_list", client_id, msg_list, NetworkDelivery.Reliable);
}
private bool Contains(string[] users, string user)
{
foreach (string auser in users)
{
if (auser == user)
return true;
}
return false;
}
public void EndMatch(string uid)
{
foreach (KeyValuePair<string, MatchData> pair in matched_players)
{
if (pair.Value.game_uid == uid)
pair.Value.ended = true;
}
}
public ClientData GetClient(ulong client_id)
{
if (client_list.ContainsKey(client_id))
return client_list[client_id];
return null;
}
public ClientData GetClientByUser(string username)
{
foreach (KeyValuePair<ulong, ClientData> pair in client_list)
{
if (pair.Value.username == username)
return pair.Value;
}
return null;
}
public ulong ServerID { get { return TcgNetwork.Get().ServerID; } }
public NetworkMessaging Messaging { get { return TcgNetwork.Get().Messaging; } }
public static ServerMatchmaker Get()
{
return _instance;
}
}
public class MatchPlayerData
{
public string user_id;
public string username;
public string group;
public int elo_rank;
public int nb_players;
}
public class MatchData
{
public string group;
public string game_uid;
public string server_url;
public bool ended = false;
public string[] players;
public MatchData(string grp, string uid, string url, int players) { group = grp; game_uid = uid; server_url = url; this.players = new string[players]; }
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 189ef7d70f3b50648986d731b64303ce
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: