/// <summary> /// Obtains the corresponding matchmaker entity from database /// /// Handles entity creation and migration /// </summary> private BasicMatchmakerEntity GetEntity() { // try to load the entity var e = DB.TakeAll <BasicMatchmakerEntity>() .Filter(entity => entity.MatchmakerName == GetMatchmakerName()) .First(); // delete deprecated entity if (e != null && e.Version != BasicMatchmakerEntity.CurrentVersion) { e.Delete(); e = null; } // create new entity if (e == null) { e = new BasicMatchmakerEntity { MatchmakerName = GetMatchmakerName() }; e.Save(); } return(e); }
/// <summary> /// Player wants to join this matchmaker and start waiting /// </summary> /// <param name="ticket">Ticket of the player</param> /// <exception cref="ArgumentException"> /// Ticket owner and facet caller differ /// </exception> public void JoinMatchmaker(TMatchmakerTicket ticket) { // null ticket owner gets set to the caller if (ticket.PlayerId == null) { ticket.PlayerId = Caller.EntityId; } // ticket owner has to match the caller if (ticket.PlayerId != Caller.EntityId) { throw new ArgumentException( "Ticket belongs to a different player " + "than the one registering it.", nameof(ticket) ); } PrepareNewTicket(ticket); entity = GetEntity(); DB.RetryOnConflict(() => { entity.Refresh(); var tickets = entity .DeserializeTickets <TMatchmakerTicket>(); // if already waiting, perform a re-insert tickets.RemoveAll(t => t.PlayerId == ticket.PlayerId); // if to be notified, remove from notifications entity.Notifications.RemoveAll( n => n.playerId == Caller.EntityId ); // add ticket into the queue ticket.InsertedNow(); tickets.Add(ticket); entity.SerializeTickets( tickets ); entity.SaveCarefully(); }); }
/// <summary> /// Player polls for new status on his/her matching /// </summary> /// <param name="leave">Player wants to leave the matchmaker</param> /// <returns>Null if not matched yet, match entity otherwise</returns> /// <exception cref="UnknownPlayerPollingException"> /// When the matchmaker has no clue why is this player polling /// </exception> public TMatchEntity PollMatchmaker(bool leave) { entity = GetEntity(); TMatchEntity returnedValue = null; // first perform cleanup DB.RetryOnConflict(() => { entity.Refresh(); CleanUpExpiredItems(); entity.SaveCarefully(); }); DB.RetryOnConflict(() => { entity.Refresh(); // player not waiting -> throw // unless there's a notification for this player if (entity.Notifications.All( n => n.playerId != Caller.EntityId )) { var tickets = entity .DeserializeTickets <TMatchmakerTicket>(); if (tickets.All(t => t.PlayerId != Caller.EntityId)) { throw new UnknownPlayerPollingException( "Polling, but not waiting in ticket queue, " + "nor having a notification prepared." ); } } // update poll time for this ticket { var tickets = entity .DeserializeTickets <TMatchmakerTicket>(); var ticket = tickets.FirstOrDefault( t => t.PlayerId == Caller.EntityId ); ticket?.PolledNow(); entity.SerializeTickets( tickets ); } // attempt to create matches CallCreateMatches(); // find notification to return var notification = entity.Notifications .FirstOrDefault(n => n.playerId == Caller.EntityId); if (notification != null) { returnedValue = DB.Find <TMatchEntity>(notification.matchId); entity.Notifications.RemoveAll( n => n.playerId == Caller.EntityId ); } // stop waiting no match, but leaving requested if (leave && returnedValue == null) { var tickets = entity .DeserializeTickets <TMatchmakerTicket>(); tickets.RemoveAll(t => t.PlayerId == Caller.EntityId); entity.SerializeTickets( tickets ); } entity.SaveCarefully(); }); return(returnedValue); }