/// <summary>Part of a shuffle process.</summary> /// <param name="group">The group being shuffled.</param> /// <param name="card">An array containing the CardIdentity ids to shuffle.</param> public void Shuffle(Group group, int[] card) { // Array to hold the new aliases (sent to CreateAlias) ulong[] aliases = new ulong[card.Length]; // Intialize the group shuffle group.FilledShuffleSlots = 0; group.HasReceivedFirstShuffledMessage = false; group.MyShufflePos = new short[card.Length]; // Check if we received enough cards if (card.Length < group.Count / (Player.Count - 1)) { Program.Trace.TraceEvent(TraceEventType.Warning, EventIds.Event, "[Shuffle] Too few cards received."); } // Do the shuffling var rnd = new CryptoRandom(); for (int i = card.Length - 1; i >= 0; i--) { int r = rnd.Next(i + 1); int tc = card[r]; card[r] = card[i]; // Create a new alias, if the card is not face up CardIdentity ci = CardIdentity.Find(tc); if (group.FindByCardIdentity(ci) != null) { card[i] = tc; aliases[i] = ulong.MaxValue; ci.Visible = true; } else { ci = new CardIdentity(Program.Game.GenerateCardId()); ci.MySecret = ci.Alias = true; ci.Key = ((ulong)Crypto.PositiveRandom()) << 32 | (uint)tc; card[i] = ci.Id; aliases[i] = Crypto.ModExp(ci.Key); ci.Visible = false; } // Give a random position to the card group.MyShufflePos[i] = (short)Crypto.Random(group.Count); } // Send the results Program.Client.Rpc.CreateAlias(card, aliases); Program.Client.Rpc.Shuffled(group, card, group.MyShufflePos); }
/// <summary>Unalias some Cards, e.g. before a shuffle</summary> /// <param name="card">An array containing the Card ids to unalias.</param> /// <param name="type">An array containing the corresponding revealed CardIdentity ids.</param> public void Unalias(int[] card, ulong[] type) { if (card.Length != type.Length) { Program.TraceWarning("[Unalias] Card and type lengths don't match."); return; } Pile g = null; List <int> cards = new List <int>(card.Length); List <ulong> types = new List <ulong>(card.Length); for (int i = 0; i < card.Length; i++) { Card c = Card.Find(card[i]); if (c == null) { Program.TraceWarning("[Unalias] Card not found."); continue; } if (g == null) { g = c.Group as Pile; } else if (g != c.Group) { Program.TraceWarning("[Unalias] Not all cards belong to the same group!"); continue; } // Check nobody cheated if (!c.Type.MySecret) { if (c.Type.Key != Crypto.ModExp(type[i])) { Program.TraceWarning("[Unalias] Card identity doesn't match."); } } // Substitue the card's identity CardIdentity ci = CardIdentity.Find((int)type[i]); if (ci == null) { Program.TraceWarning("[Unalias] Card identity not found."); continue; } CardIdentity.Delete(c.Type.Id); c.Type = ci; // Propagate unaliasing if (ci.Alias && ci.MySecret) { cards.Add(c.Id); } types.Add(ci.Key); } if (cards.Count > 0) { Program.Client.Rpc.Unalias(cards.ToArray(), types.ToArray()); } if (g == null) { return; } if (!g.PreparingShuffle) { Program.TraceWarning("[Unalias] Cards revealed are not in a group prepared for shuffle."); return; } // If all cards are now revealed, one can proceed to shuffling if (!g.WantToShuffle) { return; } bool done = false; for (int i = 0; !done && i < g.Count; i++) { done = g[i].Type.Alias; } if (!done) { g.DoShuffle(); } }
public void Shuffled(Group group, int[] card, short[] pos) { // Check the args if (card.Length != pos.Length) { Program.TraceWarning("[Shuffled] Cards and positions lengths don't match."); return; } group.FilledShuffleSlots += card.Length; if (group.FilledShuffleSlots > group.Count) { Program.TraceWarning("[Shuffled] Too many card positions received."); return; } // If it's the first packet we receive for this shuffle, clear all Types if (!group.HasReceivedFirstShuffledMessage) { foreach (Card c in group) { c.Type = null; } } group.HasReceivedFirstShuffledMessage = true; // Check that the server didn't change our positions if (card[0] >> 16 == Player.LocalPlayer.Id && group.MyShufflePos != null) { if (pos.Where((t, i) => t != @group.MyShufflePos[i]).Any()) { Program.TraceWarning("[Shuffled] The server has changed the order of the cards."); } group.MyShufflePos = null; } // Insert the cards for (int j = 0; j < card.Length; j++) { // Get the wished position int i = pos[j]; // Get the card CardIdentity ci = CardIdentity.Find(card[j]); if (ci == null) { Program.TraceWarning("[Shuffled] Card not found."); continue; } // Check if the slot is free, otherwise choose the first free one if (i >= group.Count || group[i].Type != null) { i = group.FindNextFreeSlot(i); } if (i >= group.Count) { continue; } // Set the type group[i].Type = ci; group[i].SetVisibility(ci.Visible ? DataNew.Entities.GroupVisibility.Everybody : DataNew.Entities.GroupVisibility.Nobody, null); } if (group.FilledShuffleSlots == group.Count) { group.OnShuffled(); } }
/// <summary>Reveal a card's identity to one player only.</summary> /// <param name="players"> </param> /// <param name="card">The card, whose identity is revealed.</param> /// <param name="encrypted">Either a ulong[2] containing an encrypted aliased CardIdentity id. Or a ulong[5] containing an encrypted CardModel guid.</param> public void RevealTo(Player[] players, Card card, ulong[] encrypted) { var oldType = card.Type; ulong alias = 0; Guid id = Guid.Empty; players = players.Where(x => x != null).ToArray(); switch (encrypted.Length) { case 2: alias = Crypto.Decrypt(encrypted); break; case 5: id = Crypto.DecryptGuid(encrypted); break; default: Program.TraceWarning("[RevealTo] Invalid data received."); return; } if (!players.All(p => (card.Group.Visibility == DataNew.Entities.GroupVisibility.Custom && card.Group.Viewers.Contains(p)) || card.PlayersLooking.Contains(p) || card.PeekingPlayers.Contains(p))) { Program.TraceWarning("[RevealTo] Revealing a card to a player, who isn't allowed to see it. This indicates a bug or cheating."); } // If it's an alias, we must revealed it to the final recipient bool sendToMyself = true; if (alias != 0) { sendToMyself = false; CardIdentity ci = CardIdentity.Find((int)alias); if (ci == null) { Program.TraceWarning("[RevealTo] Identity not found."); return; } // If the revealed type is an alias, pass it to the one who owns it to continue the RevealTo chain. if (ci.Alias) { Player p = Player.Find((byte)(ci.Key >> 16)); Program.Client.Rpc.RevealToReq(p, players, card, Crypto.Encrypt(ci.Key, p.PublicKey)); } // Else revealed the card model to the ones, who must see it else { Player[] pArray = new Player[1]; foreach (Player p in players) { if (p != Player.LocalPlayer) { pArray[0] = p; Program.Client.Rpc.RevealToReq(p, pArray, card, Crypto.Encrypt(ci.Model.Id, p.PublicKey)); } else { sendToMyself = true; id = ci.Model.Id; } } } } // Else it's a type and we are the final recipients if (!sendToMyself) { return; } if (card.Type.Model == null) { card.SetModel(Program.GameEngine.Definition.GetCardById(id)); } // Raise a notification oldType.OnRevealed(card.Type); }
/// <summary>Reveal one card's identity</summary> /// <param name="card">The card, whose identity is revealed</param> /// <param name="revealed">Either the salted CardIdentity id (in the case of an alias), or the salted, condensed Card GUID.</param> /// <param name="guid"> </param> public void Reveal(Card card, ulong revealed, Guid guid) { // Save old id CardIdentity oldType = card.Type; // Check if the card is rightfully revealed if (!card.Type.Revealing) { Program.Trace.TraceEvent(TraceEventType.Warning, EventIds.Event, "Someone tries to reveal a card which is not visible to everybody."); } // Check if we can trust other clients if (!card.Type.MySecret) { if (guid != Guid.Empty && (uint)revealed != guid.Condense()) { Program.Trace.TraceEvent(TraceEventType.Warning, EventIds.Event, "[Reveal] Alias and id aren't the same. One client is buggy or tries to cheat."); } if (Crypto.ModExp(revealed) != card.Type.Key) { Program.Trace.TraceEvent(TraceEventType.Warning, EventIds.Event, "[Reveal] Card identity doesn't match. One client is buggy or tries to cheat."); } } else { card.Type.MySecret = false; } // Reveal an alias if (guid == Guid.Empty) { // Find the new type CardIdentity newId = CardIdentity.Find((int)revealed); // HACK: it is unclear to me how the CardIdentity could not be found and newId ends up null // see this bug report: https://octgn.16bugs.com/projects/3602/bugs/192070 // for now I'm just doing nothing (supposing that it means the type was already revealed). if (newId == null) { card.Reveal(); return; } // Possibly copy the model, if it was known and isn't anymore // (possible when the alias has beeen locally revealed) if (newId.Model == null) { newId.Model = card.Type.Model; } // Set the new type card.Type = newId; // Delete the old identity CardIdentity.Delete(oldType.Id); // Possibly reveal the alias further card.Reveal(); // Raise a notification oldType.OnRevealed(newId); } // Reveal a card's type else if (card.Type.Model == null) { card.SetModel(Program.GameEngine.Definition.GetCardById(guid)); // Raise a notification oldType.OnRevealed(oldType); } }