You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
448 lines
16 KiB
448 lines
16 KiB
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Concurrent;
|
|
using System.Threading.Tasks;
|
|
using System.Net.Sockets;
|
|
using UnityEngine;
|
|
using Utilities;
|
|
using System.Net;
|
|
using System;
|
|
using Empty = Google.Protobuf.WellKnownTypes.Empty;
|
|
using System.IO;
|
|
|
|
public class Client : MonoBehaviour {
|
|
public static DateTime UnixEpoch() => new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
|
private static ConnectionImpl reference;
|
|
|
|
public static void Connect(string name, string address = "127.0.0.1:50052") {
|
|
reference = new ConnectionImpl(name, address);
|
|
reference.eventManager.AddHandler("general_lobby_status_handler", (Lobby.LobbyStatus status) => {
|
|
reference.lobby_status.Set(status);
|
|
});
|
|
reference.eventManager.AddHandler("general_game_status_handler", (Game.GameStatus status) => {
|
|
reference.game_status.Set(status);
|
|
GameLoader.ReloadPiles();
|
|
});
|
|
}
|
|
|
|
public static ref ConnectionImpl GetConnection() {
|
|
return ref reference;
|
|
}
|
|
|
|
public static void CloseConnection() {
|
|
if (reference != null) {
|
|
reference.Close();
|
|
reference = null;
|
|
}
|
|
}
|
|
|
|
void Update() {
|
|
if (reference != null) {
|
|
reference.eventManager.Update();
|
|
}
|
|
}
|
|
|
|
public class Card {
|
|
public DateTime timestamp;
|
|
public string card;
|
|
|
|
public Card(string card, DateTime lastUpdated) {
|
|
this.card = card;
|
|
this.timestamp = lastUpdated > UnixEpoch() ? lastUpdated : UnixEpoch();
|
|
}
|
|
|
|
public Card(string card) {
|
|
this.card = card;
|
|
this.timestamp = UnixEpoch();
|
|
}
|
|
}
|
|
|
|
public class NetEventManager {
|
|
private Dictionary<string, Action<Common.Name>> returnNameHandlers = new Dictionary<string, Action<Common.Name>>();
|
|
private Dictionary<string, Action<Connection.UserID>> returnConnectHandlers = new Dictionary<string, Action<Connection.UserID>>();
|
|
private Dictionary<string, Action<Connection.LobbyCode>> returnCreateLobbyHandlers = new Dictionary<string, Action<Connection.LobbyCode>>();
|
|
private Dictionary<string, Action<Protocol.Games>> returnGamesHandlers = new Dictionary<string, Action<Protocol.Games>>();
|
|
private Dictionary<string, Action<Protocol.LobbyCodes>> returnPublicLobbiesHandlers = new Dictionary<string, Action<Protocol.LobbyCodes>>();
|
|
private Dictionary<string, Action<Protocol.Names>> returnUsersHandlers = new Dictionary<string, Action<Protocol.Names>>();
|
|
private Dictionary<string, Action<Lobby.LobbyStatus>> lobbyStatusHandlers = new Dictionary<string, Action<Lobby.LobbyStatus>>();
|
|
private Dictionary<string, Action<Game.Image>> returnCardImageHandlers = new Dictionary<string, Action<Game.Image>>();
|
|
private Dictionary<string, Action<Images>> returnCardImagesHandlers = new Dictionary<string, Action<Images>>();
|
|
private Dictionary<string, Action<Game.GameStatus>> gameStatusHandlers = new Dictionary<string, Action<Game.GameStatus>>();
|
|
|
|
private int currentImagesPacketLength = 0;
|
|
private int currentImagesPacketIndex = 0;
|
|
|
|
private MemoryStream currentImages;
|
|
|
|
|
|
public ConcurrentQueue<Protocol.ServerClientPacket> queue;
|
|
|
|
public void Update() {
|
|
Protocol.ServerClientPacket p = null;
|
|
while (queue.TryDequeue(out p)) {
|
|
switch (p.DataCase) {
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnName: {
|
|
var v = new Action<Common.Name>[this.returnNameHandlers.Count];
|
|
this.returnNameHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnName);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnConnect: {
|
|
var v = new Action<Connection.UserID>[this.returnConnectHandlers.Count];
|
|
this.returnConnectHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnConnect);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCreateLobby: {
|
|
var v = new Action<Connection.LobbyCode>[this.returnCreateLobbyHandlers.Count];
|
|
this.returnCreateLobbyHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnCreateLobby);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnGames: {
|
|
var v = new Action<Protocol.Games>[this.returnGamesHandlers.Count];
|
|
this.returnGamesHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnGames);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnPublicLobbies: {
|
|
var v = new Action<Protocol.LobbyCodes>[this.returnPublicLobbiesHandlers.Count];
|
|
this.returnPublicLobbiesHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnPublicLobbies);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnUsers: {
|
|
var v = new Action<Protocol.Names>[this.returnUsersHandlers.Count];
|
|
this.returnUsersHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnUsers);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.LobbyStatus: {
|
|
var v = new Action<Lobby.LobbyStatus>[this.lobbyStatusHandlers.Count];
|
|
this.lobbyStatusHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.LobbyStatus);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCardImage: {
|
|
var v = new Action<Game.Image>[this.returnCardImageHandlers.Count];
|
|
this.returnCardImageHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.ReturnCardImage);
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCardsImages: {
|
|
if (p.ReturnCardsImages.Setup != null) {
|
|
this.currentImagesPacketLength = (int)p.ReturnCardsImages.Setup.Number;
|
|
this.currentImagesPacketIndex = 0;
|
|
this.currentImages = new MemoryStream();
|
|
// Debug.Log("Setup received");
|
|
} else {
|
|
if (p.ReturnCardsImages.DataPacket.Id != this.currentImagesPacketIndex)
|
|
throw new Exception("images packet id doesn't match expected");
|
|
this.currentImages.Write(p.ReturnCardsImages.DataPacket.Data.Span.ToArray(), 0, p.ReturnCardsImages.DataPacket.Data.Span.Length);
|
|
Debug.Log(this.currentImages.ToArray()[0] + ":" + p.ReturnCardsImages.DataPacket.Data.Span.ToArray()[0]);
|
|
this.currentImagesPacketIndex++;
|
|
if (this.currentImagesPacketIndex == this.currentImagesPacketLength) {
|
|
// Debug.Log("Last packet received");
|
|
// Debug.Log("Images tar length: " + this.currentImages.Length);
|
|
this.currentImages.Seek(0, SeekOrigin.Begin);
|
|
// Debug.Log("Starting decoding");
|
|
Images i = new Images(this.currentImages);
|
|
// Debug.Log("Finished decoding");
|
|
this.currentImagesPacketIndex = 0;
|
|
var v = new Action<Images>[this.returnCardImagesHandlers.Count];
|
|
this.returnCardImagesHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.GameStatus: {
|
|
var v = new Action<Game.GameStatus>[this.gameStatusHandlers.Count];
|
|
this.gameStatusHandlers.Values.CopyTo(v, 0);
|
|
foreach (var n in v) {
|
|
n(p.GameStatus);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Common.Name> handler) {
|
|
returnNameHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Connection.UserID> handler) {
|
|
returnConnectHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Connection.LobbyCode> handler) {
|
|
returnCreateLobbyHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Protocol.Games> handler) {
|
|
returnGamesHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Protocol.LobbyCodes> handler) {
|
|
returnPublicLobbiesHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Protocol.Names> handler) {
|
|
returnUsersHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Lobby.LobbyStatus> handler) {
|
|
lobbyStatusHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Game.Image> handler) {
|
|
returnCardImageHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Images> handler) {
|
|
returnCardImagesHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void AddHandler(string name, Action<Game.GameStatus> handler) {
|
|
gameStatusHandlers.Add(name, handler);
|
|
}
|
|
|
|
public void RemoveHandler(Protocol.ServerClientPacket.DataOneofCase kind, string name) {
|
|
switch (kind) {
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnName:
|
|
returnNameHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnConnect:
|
|
returnConnectHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCreateLobby:
|
|
returnCreateLobbyHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnGames:
|
|
returnGamesHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnPublicLobbies:
|
|
returnPublicLobbiesHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnUsers:
|
|
returnUsersHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.LobbyStatus:
|
|
lobbyStatusHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCardImage:
|
|
returnCardImageHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.ReturnCardsImages:
|
|
returnCardImagesHandlers.Remove(name);
|
|
break;
|
|
case Protocol.ServerClientPacket.DataOneofCase.GameStatus:
|
|
gameStatusHandlers.Remove(name);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class ConnectionImpl // : MonoBehaviour
|
|
{
|
|
public TcpConnection conn;
|
|
|
|
private string connId;
|
|
private uint? lobby = null;
|
|
private string address;
|
|
|
|
public NetEventManager eventManager;
|
|
|
|
public Modifiable<Lobby.LobbyStatus> lobby_status = new Modifiable<Lobby.LobbyStatus>(null);
|
|
public Modifiable<Game.GameStatus> game_status = new Modifiable<Game.GameStatus>(null);
|
|
public ConnectionImpl(string user, string address) {
|
|
Debug.Log("Connecting with the tcp channel");
|
|
conn = new TcpConnection(address);
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { Connect = new Common.Name { Name_ = user } });
|
|
Protocol.ServerClientPacket p = null;
|
|
Debug.Log("Waiting for response");
|
|
while (p == null) {
|
|
conn.q.TryDequeue(out p);
|
|
}
|
|
if (p.ReturnConnect == null) {
|
|
throw new Exception("Unexpected non ReturnConnect packet");
|
|
}
|
|
connId = p.ReturnConnect.Id;
|
|
this.address = address;
|
|
this.eventManager = new NetEventManager() { queue = conn.q };
|
|
}
|
|
|
|
public void QueryName() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryName = new Google.Protobuf.WellKnownTypes.Empty() });
|
|
// return connection.name(new Google.Protobuf.WellKnownTypes.Empty()).Name_;
|
|
}
|
|
public string GetServerAddress() {
|
|
return address;
|
|
}
|
|
|
|
public void JoinLobby(string code) {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { JoinLobby = new Connection.LobbyCode { Code = Base32.FromString(code) } });
|
|
// connection.joinLobbyWithCode(new ConnectionService.LobbyCode { Code = (uint)lobby }, new Metadata { new Metadata.Entry("client_id", connId) });
|
|
}
|
|
|
|
public void CreateLobby(bool isPublic) {
|
|
eventManager.AddHandler("client_create_lobby_auto_join", (Connection.LobbyCode lobby1) => {
|
|
lobby = lobby1.Code;
|
|
eventManager.RemoveHandler(Protocol.ServerClientPacket.DataOneofCase.ReturnCreateLobby, "client_create_lobby_auto_join");
|
|
});
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { CreateLobby = new Connection.LobbyConfig { Public = isPublic } });
|
|
// lobby = connection.createLobby(new ConnectionService.LobbyConfig {Public = isPublic}, new Metadata { new Metadata.Entry("client_id", connId) }).Code;
|
|
}
|
|
|
|
public string GetLobby() {
|
|
if (lobby != null) {
|
|
return Base32.ToString((uint)lobby);
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public void GetUsersInSameLobby() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryUsers = new Empty() });
|
|
}
|
|
|
|
public void GetGameStatus() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryGameStatus = new Empty() });
|
|
}
|
|
|
|
public List<Lobby.Vote> GetLobbyVotes(uint game) {
|
|
var gameVotes = new List<Lobby.Vote>();
|
|
if (lobby_status.Get() != null) {
|
|
Google.Protobuf.Collections.RepeatedField<Lobby.Vote> votes = lobby_status.Get()?.Votes;
|
|
foreach (Lobby.Vote vote in votes) {
|
|
if (vote.Game == game) {
|
|
gameVotes.Add(vote);
|
|
}
|
|
}
|
|
}
|
|
return gameVotes;
|
|
}
|
|
|
|
public int GetLobbyVoteCount(uint game) {
|
|
var gameVotes = new List<Lobby.Vote>();
|
|
if (lobby_status.Get() != null) {
|
|
Google.Protobuf.Collections.RepeatedField<Lobby.Vote> votes = lobby_status.Get()?.Votes;
|
|
foreach (Lobby.Vote vote in votes) {
|
|
if (vote.Game == game) {
|
|
gameVotes.Add(vote);
|
|
}
|
|
}
|
|
}
|
|
return gameVotes.Count;
|
|
}
|
|
public Lobby.Vote GetSelfVote(string player) {
|
|
if (lobby_status.Get() != null) {
|
|
Google.Protobuf.Collections.RepeatedField<Lobby.Vote> votes = lobby_status.Get().Votes;
|
|
foreach (Lobby.Vote vote in votes) {
|
|
Debug.Log(vote);
|
|
if (vote.Player == player) {
|
|
return vote;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
public bool IsStarting() {
|
|
return lobby_status.Get() != null && lobby_status.Get().IsStarting;
|
|
}
|
|
|
|
public void Vote(uint game) {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { Vote = new Lobby.SingleVote { Game = game } });
|
|
// lobby_client.vote(new Lobby.SingleVote() { Game = game }, new Metadata { new Metadata.Entry("client_id", connId) });
|
|
}
|
|
public void SetReady() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { Ready = new Empty() });
|
|
// lobby_client.ready(new Google.Protobuf.WellKnownTypes.Empty(), new Metadata { new Metadata.Entry("client_id", connId) });
|
|
}
|
|
|
|
public void LeaveLobby() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { Leave = new Empty() });
|
|
lobby = null;
|
|
}
|
|
|
|
public void GetPublicLobbies() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryPublicLobbies = new Empty() });
|
|
}
|
|
|
|
public void GetCardImage(string cardKind) {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryCardImage = new Game.CardKind { Kind = cardKind } });
|
|
}
|
|
|
|
public void GetCardImages(Card[] cards) {
|
|
Game.Cards c = new Game.Cards();
|
|
c.Cards_.Add(cards.Select(c => new Game.Cards.Types.Card() { Kind = c.card, Time = Google.Protobuf.WellKnownTypes.Timestamp.FromDateTime(c.timestamp.ToUniversalTime()) }));
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryCardImages = c });
|
|
}
|
|
|
|
public int GetUserIndex(string user) {
|
|
if (game_status.Get() != null) return game_status.Get().Names.IndexOf(new Common.Name { Name_ = user });
|
|
else return -1;
|
|
}
|
|
public Google.Protobuf.Collections.MapField<string, Game.GameStatus.Types.Pile> GetPlayerPiles(string user) {
|
|
int idx = this.GetUserIndex(user);
|
|
if (idx != -1) {
|
|
return game_status.Get().PlayerPiles[idx].Piles_;
|
|
}
|
|
return null;
|
|
}
|
|
public Google.Protobuf.Collections.MapField<string, Game.GameStatus.Types.Pile> GetCommonPiles() {
|
|
if (game_status.Get() != null) return game_status.Get().CommonPiles.Piles_;
|
|
else return null;
|
|
}
|
|
public void OnClickCard(string pileName, bool isCommonPile, int cardIdx, string user) {
|
|
Game.PileKind pileKind = new Game.PileKind() { Owned = (uint)GetUserIndex(user) };
|
|
if (isCommonPile) pileKind = new Game.PileKind { Common = new Empty() };
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { CallOnClick = new Game.CardId { PileKind = pileKind, CardIndex = new Game.CardIndex { Index = (uint)cardIdx }, PileName = pileName } });
|
|
}
|
|
|
|
// public Dictionary<string, Dictionary<string, Game.MessageStatus.Types.Pile>> OnClickCard(string pileName, bool isCommonPile, int cardIdx, string user) {
|
|
// Game.PileKind pileKind = new Game.PileKind() { Owned = (uint)GetUserIndex(user) };
|
|
// game_client.onClick(new Game.CardId { PileKind = pileKind, CardIndex = new Game.CardIndex { Index = (uint)cardIdx }, PileName = pileName }, new Metadata { new Metadata.Entry("client_id", connId) });
|
|
// return GetPiles(user);
|
|
// }
|
|
|
|
public void GetGames() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { QueryGames = new Empty() });
|
|
}
|
|
|
|
static T runSync<T>(Task<T> task) {
|
|
task.Wait();
|
|
return task.Result;
|
|
}
|
|
|
|
public void Close() {
|
|
conn.SendMessage(new Protocol.ClientServerPacket() { Disconnect = new Empty() });
|
|
conn.Close();
|
|
}
|
|
}
|
|
|
|
|
|
}
|