Пример #1
0
        /// <summary>
        /// Tasks we handle are:
        ///   1) Asking a server to host the lot
        ///   2) Telling users which server is hosting the lot
        ///
        /// It is then up to the client and server to connect to each other. This is a bit crappy because it means you
        /// must open a connection to the lot server before you can find out if there is room for you to join. But this removes
        /// a lot of complexity.
        ///
        /// In the future we may move the person claiming logic to city if it causes problems. One problem I can see is there is a possible
        /// race condition in which the lot could fill up before the owner gets in. The lot would then automatically shut down
        ///
        /// </summary>
        /// <param name="lotId"></param>
        /// <param name="avatarId">The id of the avatar opening this lot. If 0, we're opening for a scheduled cleanup. (lot start fresh)</param>
        /// <param name="openIfClosed"></param>
        /// <returns></returns>

        Task <TryFindLotResult> TryFind(uint lotId, uint avatarId, bool openIfClosed, ISecurityContext security)
        {
            bool jobLot     = false;
            var  originalId = lotId;

            if (lotId > 0x200 && lotId < 0x300)
            {
                //special: join available job lot instance
                var result = Matchmaker.TryGetJobLot(lotId, avatarId);
                lotId      = result.Item1 ?? 0;
                lotId     |= 0x40000000;
                originalId = result.Item2;
                jobLot     = true;
                if (lotId == 0)
                {
                    return(Immediate(new TryFindLotResult
                    {
                        Status = FindLotResponseStatus.NO_CAPACITY
                    }));
                }
            }
            else if (lotId > 0x200 && lotId < 0x10000)
            { //job lot range
                if (!Matchmaker.TryJoinExisting(lotId, avatarId))
                {
                    return(Immediate(new TryFindLotResult
                    {
                        Status = FindLotResponseStatus.NO_ADMIT
                    }));
                }
                lotId |= 0x40000000;
                jobLot = true;
            }

            var allocation = Get(lotId);

            lock (allocation)
            {
                switch (allocation.State)
                {
                case LotAllocationState.NOT_ALLOCATED:
                    //We need to pick a server to run this lot
                    if (!openIfClosed)
                    {
                        //Sorry, cant do this
                        Remove(lotId);
                        return(Immediate(new TryFindLotResult
                        {
                            Status = FindLotResponseStatus.NOT_OPEN
                        }));
                    }

                    if (!jobLot)
                    {
                        DbLot lot = null;
                        using (var db = DAFactory.Get)
                        {
                            //Convert the lot location into a lot db id
                            lot = db.Lots.GetByLocation(Context.ShardId, lotId);
                            if (lot == null)
                            {
                                Remove(lotId);
                                return(Immediate(new TryFindLotResult
                                {
                                    Status = FindLotResponseStatus.NO_SUCH_LOT
                                }));
                            }

                            if (avatarId != 0)
                            {
                                var roomies  = db.Roommates.GetLotRoommates(lot.lot_id);
                                var modState = db.Avatars.GetModerationLevel(avatarId);
                                var avatars  = new List <uint>();
                                foreach (var roomie in roomies)
                                {
                                    if (roomie.is_pending == 0)
                                    {
                                        avatars.Add(roomie.avatar_id);
                                    }
                                }

                                try
                                {
                                    if (lot.admit_mode < 4 && modState == 0)
                                    {
                                        security.DemandAvatars(avatars, AvatarPermissions.WRITE);
                                    }
                                }
                                catch (Exception ex)
                                {
                                    Remove(lotId);
                                    return(Immediate(new TryFindLotResult
                                    {
                                        Status = FindLotResponseStatus.NOT_PERMITTED_TO_OPEN
                                    }));
                                }
                            }
                        }

                        if (!allocation.TryClaim(lot))
                        {
                            Remove(lotId);
                            return(Immediate(new TryFindLotResult
                            {
                                Status = FindLotResponseStatus.CLAIM_FAILED
                            }));
                        }
                        allocation.SetLot(lot, 0,
                                          (avatarId == 0) ? ClaimAction.LOT_CLEANUP : ClaimAction.LOT_HOST);
                    }
                    else
                    {
                        allocation.SetLot(new DbLot()
                        {
                            lot_id = (int)lotId
                        }, originalId,
                                          (avatarId == 0)? ClaimAction.LOT_CLEANUP : ClaimAction.LOT_HOST);
                    }

                    var pick = PickingEngine.PickServer();
                    if (pick.Success == false)
                    {
                        //Release claim
                        allocation.TryUnclaim();
                        Remove(lotId);
                        return(Immediate(new TryFindLotResult
                        {
                            Status = FindLotResponseStatus.NO_CAPACITY
                        }));
                    }
                    else
                    {
                        return(allocation.BeginPick(pick).ContinueWith(x => {
                            if (x.IsFaulted || x.IsCanceled || x.Result.Accepted == false)
                            {
                                Remove(lotId);
                                return new TryFindLotResult
                                {
                                    Status = FindLotResponseStatus.NO_CAPACITY
                                };
                            }

                            return new TryFindLotResult {
                                Status = FindLotResponseStatus.FOUND,
                                Server = allocation.Server,
                                LotDbId = allocation.LotDbId,
                                LotId = lotId,
                            };
                        }));
                    }
                    break;

                case LotAllocationState.ALLOCATING:
                    break;

                case LotAllocationState.ALLOCATED:
                    if (!jobLot && avatarId != 0)
                    {
                        //check admit type (might be expensive?)
                        using (var db = DAFactory.Get)
                        {
                            var lot = db.Lots.GetByLocation(Context.ShardId, lotId);
                            if (lot != null)
                            {
                                if (lot.admit_mode > 0 && lot.admit_mode < 4)
                                {
                                    //special admit mode

                                    var roomies  = db.Roommates.GetLotRoommates(lot.lot_id);
                                    var modState = db.Avatars.GetModerationLevel(avatarId);
                                    var avatars  = new List <uint>();
                                    foreach (var roomie in roomies)
                                    {
                                        avatars.Add(roomie.avatar_id);
                                    }

                                    try
                                    {
                                        if (modState == 0)
                                        {
                                            security.DemandAvatars(avatars, AvatarPermissions.WRITE);
                                        }
                                    }
                                    catch (Exception ex)
                                    {
                                        //if we're not a roommate, check admit rules
                                        if ((lot.admit_mode == 1 && !db.LotAdmit.GetLotAdmitDeny(lot.lot_id, 0).Contains(avatarId)) ||  //admit list
                                            (lot.admit_mode == 2 && db.LotAdmit.GetLotAdmitDeny(lot.lot_id, 1).Contains(avatarId)) ||     //ban list
                                            (lot.admit_mode == 3))        //ban all
                                        {
                                            return(Immediate(new TryFindLotResult
                                            {
                                                Status = FindLotResponseStatus.NO_ADMIT
                                            }));
                                        }
                                    }
                                }
                            }
                        }
                    }

                    return(Immediate(new TryFindLotResult
                    {
                        Status = FindLotResponseStatus.FOUND,
                        Server = allocation.Server,
                        LotDbId = allocation.LotDbId,
                        LotId = lotId
                    }));

                //Should never get here..
                case LotAllocationState.FAILED:
                    Remove(lotId);
                    return(Immediate(new TryFindLotResult {
                        Status = FindLotResponseStatus.UNKNOWN_ERROR
                    }));
                }

                return(Immediate(new TryFindLotResult
                {
                    Status = FindLotResponseStatus.UNKNOWN_ERROR
                }));
            }
        }
Пример #2
0
        public override void DemandMutation(object entity, MutationType type, string path, object value, ISecurityContext context)
        {
            var lot = entity as Lot;

            if (lot.DbId == 0)
            {
                throw new SecurityException("Unclaimed lots cannot be mutated");
            }

            var roomies = lot.Lot_RoommateVec;

            switch (path)
            {
            //Owner only
            case "Lot_Description":
                context.DemandAvatar(lot.Lot_LeaderID, AvatarPermissions.WRITE);
                var desc = value as string;
                if (desc != null && desc.Length > 500)
                {
                    throw new Exception("Description too long!");
                }
                break;

            case "Lot_Name":
                context.DemandAvatar(lot.Lot_LeaderID, AvatarPermissions.WRITE);
                if (!GlobalRealestate.ValidateLotName((string)value))
                {
                    throw new Exception("Invalid lot name");
                }
                //Lot_Name is a special case, it has to be unique so we have to hit the db in the security check
                //for this mutation.
                TryChangeLotName(lot, (string)value);
                break;

            case "Lot_Category":
                context.DemandAvatar(lot.Lot_LeaderID, AvatarPermissions.WRITE);

                if (lot.Lot_IsOnline)
                {
                    throw new SecurityException("Lot must be offline to change category!");
                }

                //7 days
                if (((Epoch.Now - lot.Lot_LastCatChange) / (60 * 60)) < 168)
                {
                    throw new SecurityException("You must wait 7 days to change your lot category again");
                }
                break;

            case "Lot_SkillGamemode":
                context.DemandAvatar(lot.Lot_LeaderID, AvatarPermissions.WRITE);

                var  svalue = (uint)value;
                uint minSkill;
                if (!SkillGameplayCategory.TryGetValue((LotCategory)lot.Lot_Category, out minSkill))
                {
                    minSkill = 0;
                }
                if (Math.Min(2, Math.Max(minSkill, svalue)) != svalue)
                {
                    throw new SecurityException("Invalid gamemode for this category.");
                }

                if (lot.Lot_IsOnline)
                {
                    throw new SecurityException("Lot must be offline to change skill mode!");
                }
                break;

            //roommate only
            case "Lot_Thumbnail":
                context.DemandAvatars(roomies, AvatarPermissions.WRITE);
                //TODO: needs to be generic data, png, size 288x288, less than 1MB
                break;

            case "Lot_IsOnline":
            case "Lot_NumOccupants":
            case "Lot_RoommateVec":
            case "Lot_SpotLightText":
                context.DemandInternalSystem();
                break;

            case "Lot_LotAdmitInfo.LotAdmitInfo_AdmitList":
            case "Lot_LotAdmitInfo.LotAdmitInfo_BanList":
                context.DemandAvatars(roomies, AvatarPermissions.WRITE);
                int atype = (path == "Lot_LotAdmitInfo.LotAdmitInfo_AdmitList") ? 0 : 1;
                using (var db = DAFactory.Get())
                {     //need to check db constraints
                    switch (type)
                    {
                    case MutationType.ARRAY_REMOVE_ITEM:
                        //Remove bookmark at index value
                        var removedAva = (uint)value;
                        db.LotAdmit.Delete(new DbLotAdmit
                        {
                            lot_id     = (int)lot.DbId,
                            avatar_id  = removedAva,
                            admit_type = (byte)atype
                        });
                        break;

                    case MutationType.ARRAY_SET_ITEM:
                        //Add a new bookmark
                        var newAva = (uint)value;
                        db.LotAdmit.Create(new DbLotAdmit
                        {
                            lot_id     = (int)lot.DbId,
                            avatar_id  = newAva,
                            admit_type = (byte)atype
                        });
                        break;
                    }
                }
                break;

            case "Lot_LotAdmitInfo.LotAdmitInfo_AdmitMode":
                context.DemandAvatars(roomies, AvatarPermissions.WRITE);
                //can only set valid values
                var mode = (byte)value;
                if (mode < 0 || mode > 3)
                {
                    throw new Exception("Invalid admit mode!");
                }
                break;

            default:
                throw new SecurityException("Field: " + path + " may not be mutated by users");
            }
        }