//LotAllocation public bool TryClaim(DbLot lot) { Lot = lot; //Write a db record to claim the lot using (var db = DAFactory.Get()) { var claim = db.LotClaims.TryCreate(new Database.DA.LotClaims.DbLotClaim { shard_id = Context.ShardId, lot_id = lot.lot_id, owner = Context.Config.Call_Sign }); ClaimId = claim; if (claim.HasValue) { return(true); } else { return(false); } } }
protected override Avatar LazyLoad(uint key, Avatar oldVal) { using (var db = DAFactory.Get) { var avatar = db.Avatars.Get(key); if (avatar == null) { return(null); } if (avatar.shard_id != ShardId) { return(null); } var myLots = db.Roommates.GetAvatarsLots(avatar.avatar_id); DbLot lot = null; if (myLots.Count > 0 && myLots.FirstOrDefault()?.is_pending == 0) { lot = db.Lots.Get(myLots.FirstOrDefault().lot_id); } //List<DbJobLevel> levels = db.Avatars.GetJobLevels(key); //List<DbRelationship> rels = db.Relationships.GetBidirectional(key); //List<DbBookmark> bookmarks = db.Bookmarks.GetByAvatarId(key); var ava = HydrateOne(avatar, lot); if (oldVal != null) { ava.Avatar_IsOnline = oldVal.Avatar_IsOnline; } return(ava); } }
private Avatar HydrateOne(DbAvatar dbAvatar, DbLot dbLot) { var result = new Avatar(); result.Avatar_Id = dbAvatar.avatar_id; result.Avatar_Name = dbAvatar.name; result.Avatar_IsOnline = false; result.Avatar_Description = dbAvatar.description; result.Avatar_Appearance = new AvatarAppearance { AvatarAppearance_BodyOutfitID = dbAvatar.body, AvatarAppearance_HeadOutfitID = dbAvatar.head, AvatarAppearance_SkinTone = dbAvatar.skin_tone }; var now = Epoch.Now; result.FetchTime = now; result.Avatar_Age = (uint)((now - dbAvatar.date) / ((long)60 * 60 * 24)); result.Avatar_Skills = new AvatarSkills { AvatarSkills_Body = dbAvatar.skill_body, AvatarSkills_LockLv_Body = dbAvatar.lock_body, AvatarSkills_Charisma = dbAvatar.skill_charisma, AvatarSkills_LockLv_Charisma = dbAvatar.lock_charisma, AvatarSkills_Cooking = dbAvatar.skill_cooking, AvatarSkills_LockLv_Cooking = dbAvatar.lock_cooking, AvatarSkills_Creativity = dbAvatar.skill_creativity, AvatarSkills_LockLv_Creativity = dbAvatar.lock_creativity, AvatarSkills_Logic = dbAvatar.skill_logic, AvatarSkills_LockLv_Logic = dbAvatar.lock_logic, AvatarSkills_Mechanical = dbAvatar.skill_mechanical, AvatarSkills_LockLv_Mechanical = dbAvatar.lock_mechanical }; result.Avatar_PrivacyMode = dbAvatar.privacy_mode; result.Avatar_SkillsLockPoints = (ushort)(20 + result.Avatar_Age / 7); result.Avatar_ModerationLevel = dbAvatar.moderation_level; result.Avatar_MayorNhood = (uint)(dbAvatar.mayor_nhood ?? 0); result.JobLevelProvider = JobLevelProvider; result.RatingProvider = RatingProvider; result.AvgRatingProvider = AvgRatingProvider; result.Avatar_CurrentJob = dbAvatar.current_job; result.Avatar_Top100ListFilter = new Top100ListFilter() { Top100ListFilter_ResultsVec = ImmutableList.Create <uint>(), Top100ListFilter_Top100ListID = 0, }; result.RelationshipProvider = RelationshipProvider; if (dbLot != null) { result.Avatar_LotGridXY = dbLot.location; } result.BookmarkProvider = BookmarkProvider; return(result); }
//LotAllocation public bool TryClaim(DbLot lot) { Lot = lot; //Write a db record to claim the lot using (var db = DAFactory.Get) { var claim = db.LotClaims.TryCreate(new Database.DA.LotClaims.DbLotClaim { shard_id = Context.ShardId, lot_id = lot.lot_id, owner = Context.Config.Call_Sign }); ClaimId = claim; if (claim.HasValue) { return(true); } else { var oldClaim = db.LotClaims.GetByLotID(lot.lot_id); if (oldClaim == null) { return(false); //what? } else if (oldClaim.owner == Context.Config.Call_Sign) { //something went wrong and this lot claim did not get freed... but the city does own it. //if we got here, then there was no allocation in the city before now. //therefore the only way the city could own a lot claim and not an allocation is if it got stuck somehow db.LotClaims.Delete((uint)oldClaim.claim_id, oldClaim.owner); claim = db.LotClaims.TryCreate(new Database.DA.LotClaims.DbLotClaim { shard_id = Context.ShardId, lot_id = lot.lot_id, owner = Context.Config.Call_Sign }); ClaimId = claim; if (claim.HasValue) { return(true); } else { return(false); } } else { return(false); } } } }
public LotContainer(IDAFactory da, LotContext context, ILotHost host, IKernel kernel, LotServerConfiguration config, IRealestateDomain realestate) { VM.UseWorld = false; DAFactory = da; Host = host; Context = context; Kernel = kernel; Config = config; JobLot = (context.Id & 0x40000000) > 0; if (JobLot) { var jobPacked = Context.DbId - 0x200; var jobLevel = (short)((jobPacked - 1) & 0xF); var jobType = (short)((jobPacked - 1) / 0xF); LotPersist = new DbLot { lot_id = Context.DbId, location = Context.Id, category = LotCategory.money, name = "{job:" + jobType + ":" + jobLevel + "}", admit_mode = 4 }; LotAdj = new List <DbLot>(); LotRoommates = new List <DbRoommate>(); Terrain = new VMTSOSurroundingTerrain(); for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { Terrain.Roads[x, y] = 0xF; //crossroads everywhere } } } else { using (var db = DAFactory.Get()) { LotPersist = db.Lots.Get(context.DbId); LotAdj = db.Lots.GetAdjToLocation(context.ShardId, LotPersist.location); LotRoommates = db.Roommates.GetLotRoommates(context.DbId); } Realestate = realestate.GetByShard(LotPersist.shard_id); GenerateTerrain(); } }
private void CreateDbObject(IDA da, VMEntityMarshal entity, DbLot lot) { var ownerID = ((VMTSOObjectState)entity.PlatformState).OwnerID; var obj = new DbObject() { budget = (int)((VMTSOEntityState)entity.PlatformState).Budget.Value, type = (entity.MasterGUID == 0) ? entity.GUID : entity.MasterGUID, lot_id = lot.lot_id, owner_id = (ownerID == 0) ? (uint?)null : ownerID, shard_id = lot.shard_id, dyn_obj_name = "", //get from multitile? value = 0 //get from multitile? }; if (!Options.Report) { uint id = da.Objects.Create(obj); PersistRemap[entity.PersistID] = id; entity.PersistID = id; } }
public void SetLot(DbLot lot, uint specialId, ClaimAction openAction) { Lot = lot; SpecialId = specialId; OpenAction = openAction; }
/// <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 })); } }
protected Lot HydrateOne(DbLot lot, List <DbRoommate> roommates, List <DbLotAdmit> admit) { var location = MapCoordinates.Unpack(lot.location); /* **experimental** we're leaving this up to ASP.NET api for now (behind cloudflare) * //attempt to load the lot's thumbnail. * var path = Path.Combine(NFS.GetBaseDirectory(), "Lots/" + lot.lot_id.ToString("x8") + "/thumb.png"); * cTSOGenericData thumb = null; * try * { * using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) * { * int numBytesToRead = Convert.ToInt32(fs.Length); * var file = new byte[(numBytesToRead)]; * fs.Read(file, 0, numBytesToRead); * thumb = new cTSOGenericData(file); * } * } * catch (Exception) { * thumb = new cTSOGenericData(new byte[0]); * } */ var result = new Lot { DbId = lot.lot_id, Id = lot.location, Lot_Name = lot.name, Lot_IsOnline = false, Lot_Location = new Location { Location_X = location.X, Location_Y = location.Y }, Lot_Price = (uint)Realestate.GetPurchasePrice(location.X, location.Y), Lot_LeaderID = lot.owner_id ?? 0, Lot_OwnerVec = ImmutableList.Create(lot.owner_id ?? 0), Lot_RoommateVec = ImmutableList.Create <uint>(), Lot_LotAdmitInfo = new LotAdmitInfo() { LotAdmitInfo_AdmitMode = lot.admit_mode }, Lot_NumOccupants = 0, Lot_Category = (byte)lot.category, Lot_SkillGamemode = lot.skill_mode, Lot_LastCatChange = lot.category_change_date, Lot_Description = lot.description, Lot_Thumbnail = new cTSOGenericData(new byte[0]), }; foreach (var roomie in roommates) { if (roomie.is_pending == 0) { result.Lot_RoommateVec = result.Lot_RoommateVec.Add(roomie.avatar_id); } } var admitL = new List <uint>(); var banL = new List <uint>(); foreach (var item in admit) { if (item.admit_type == 0) { admitL.Add(item.avatar_id); } else { banL.Add(item.avatar_id); } } result.Lot_LotAdmitInfo.LotAdmitInfo_AdmitList = ImmutableList.ToImmutableList(admitL); result.Lot_LotAdmitInfo.LotAdmitInfo_BanList = ImmutableList.ToImmutableList(banL); return(result); }
public async void Handle(IVoltronSession session, ChangeRoommateRequest packet) { try { if (session.IsAnonymous) { return; } using (var da = DAFactory.Get()) { if (packet.Type == ChangeRoommateType.POLL) { var lots = da.Roommates.GetAvatarsLots(session.AvatarId); foreach (var lot in lots) { if (lot.is_pending == 1) { var lotdb = da.Lots.Get(lot.lot_id); if (lotdb == null) { return; } session.Write(new ChangeRoommateRequest { Type = ChangeRoommateType.INVITE, AvatarId = lotdb.owner_id ?? 0, LotLocation = lotdb.location }); } } } else if (packet.Type == ChangeRoommateType.ACCEPT) { var lot = da.Lots.GetByLocation(Context.ShardId, packet.LotLocation); if (lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); return; } if (da.Roommates.AcceptRoommateRequest(session.AvatarId, lot.lot_id)) { var lotDS = await DataService.Get <FSO.Common.DataService.Model.Lot>(packet.LotLocation); if (lotDS != null) { lotDS.Lot_RoommateVec = lotDS.Lot_RoommateVec.Add(session.AvatarId); } var lotOwned = da.LotClaims.GetByLotID(lot.lot_id); if (lotOwned != null) { var lotServer = LotServers.GetLotServerSession(lotOwned.owner); if (lotServer != null) { //immediately notify lot of new roommate lotServer.Write(new NotifyLotRoommateChange() { AvatarId = session.AvatarId, LotId = lot.lot_id, Change = Protocol.Gluon.Model.ChangeType.ADD_ROOMMATE }); } } var avatar = await DataService.Get <Avatar>(session.AvatarId); if (avatar != null) { avatar.Avatar_LotGridXY = packet.LotLocation; } Status(session, ChangeRoommateResponseStatus.ACCEPT_SUCCESS); return; } else { Status(session, ChangeRoommateResponseStatus.NO_INVITE_PENDING); return; } } else if (packet.Type == ChangeRoommateType.DECLINE) { var lot = da.Lots.GetByLocation(Context.ShardId, packet.LotLocation); if (lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); return; } if (da.Roommates.DeclineRoommateRequest(session.AvatarId, lot.lot_id)) { Status(session, ChangeRoommateResponseStatus.DECLINE_SUCCESS); return; } else { Status(session, ChangeRoommateResponseStatus.NO_INVITE_PENDING); return; } } else { //verify that requester is definitely a roommate in the target lot var ownedLot = da.Lots.GetByOwner(session.AvatarId); var myLots = da.Roommates.GetAvatarsLots(session.AvatarId); if (packet.Type == ChangeRoommateType.INVITE) { //is invitee roommate somewhere else? count lot roommates and check for max var targ = da.Avatars.Get(packet.AvatarId); if (targ == null) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); } var targLots = da.Roommates.GetAvatarsLots(packet.AvatarId); if (targLots.Count > 0) { Status(session, ChangeRoommateResponseStatus.ROOMIE_ELSEWHERE); //request already pending or otherwise return; } var lotr = myLots.FirstOrDefault(); DbLot lot = null; if (lotr != null) { lot = da.Lots.Get(lotr.lot_id); } if (lotr == null || lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); //what?? return; } if (lot.owner_id != session.AvatarId) //only an owner can add roommates { Status(session, ChangeRoommateResponseStatus.YOU_ARE_NOT_OWNER); return; } var myLotRoomies = da.Roommates.GetLotRoommates(lotr.lot_id); if (myLotRoomies.Count >= 8) { //if pending roommates put us over, cancel some of them. //assume first is oldest request var pending = myLotRoomies.FirstOrDefault(x => x.is_pending == 1); if (pending == null) { Status(session, ChangeRoommateResponseStatus.TOO_MANY_ROOMMATES); return; } else { da.Roommates.DeclineRoommateRequest(pending.avatar_id, pending.lot_id); } } //create roommate request in database if (!da.Roommates.Create(new DbRoommate { avatar_id = packet.AvatarId, lot_id = lotr.lot_id, is_pending = 1, permissions_level = 0 })) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); return; } //if online, notify roommate of pending request. var targetSession = Sessions.GetByAvatarId(packet.AvatarId); if (targetSession != null) { targetSession.Write(new ChangeRoommateRequest() { Type = ChangeRoommateType.INVITE, AvatarId = session.AvatarId, LotLocation = lot.location }); } Status(session, ChangeRoommateResponseStatus.INVITE_SUCCESS); return; //if not, we'll catch them when they log in later. } else if (packet.Type == ChangeRoommateType.KICK) { var result = await TryKick(packet.LotLocation, session.AvatarId, packet.AvatarId); Status(session, result); } } } } catch (Exception e) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); } }
public async void Handle(IVoltronSession session, ChangeRoommateRequest packet) { try { if (session.IsAnonymous) { return; } using (var da = DAFactory.Get()) { if (packet.Type == ChangeRoommateType.POLL) { var lots = da.Roommates.GetAvatarsLots(session.AvatarId); foreach (var lot in lots) { if (lot.is_pending == 1) { var lotdb = da.Lots.Get(lot.lot_id); if (lotdb == null) { return; } session.Write(new ChangeRoommateRequest { Type = ChangeRoommateType.INVITE, AvatarId = lotdb.owner_id, LotLocation = lotdb.location }); } } } else if (packet.Type == ChangeRoommateType.ACCEPT) { var lot = da.Lots.GetByLocation(Context.ShardId, packet.LotLocation); if (lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); return; } if (da.Roommates.AcceptRoommateRequest(session.AvatarId, lot.lot_id)) { var lotDS = await DataService.Get <FSO.Common.DataService.Model.Lot>(packet.LotLocation); if (lotDS != null) { lotDS.Lot_RoommateVec = lotDS.Lot_RoommateVec.Add(session.AvatarId); } var lotOwned = da.LotClaims.GetByLotID(lot.lot_id); if (lotOwned != null) { var lotServer = LotServers.GetLotServerSession(lotOwned.owner); if (lotServer != null) { //immediately notify lot of new roommate lotServer.Write(new NotifyLotRoommateChange() { AvatarId = session.AvatarId, LotId = lot.lot_id, Change = Protocol.Gluon.Model.ChangeType.ADD_ROOMMATE }); } } var avatar = await DataService.Get <Avatar>(session.AvatarId); if (avatar != null) { avatar.Avatar_LotGridXY = packet.LotLocation; } Status(session, ChangeRoommateResponseStatus.ACCEPT_SUCCESS); return; } else { Status(session, ChangeRoommateResponseStatus.NO_INVITE_PENDING); return; } } else if (packet.Type == ChangeRoommateType.DECLINE) { var lot = da.Lots.GetByLocation(Context.ShardId, packet.LotLocation); if (lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); return; } if (da.Roommates.DeclineRoommateRequest(session.AvatarId, lot.lot_id)) { Status(session, ChangeRoommateResponseStatus.DECLINE_SUCCESS); return; } else { Status(session, ChangeRoommateResponseStatus.NO_INVITE_PENDING); return; } } else { //verify that requester is definitely a roommate in the target lot var ownedLot = da.Lots.GetByOwner(session.AvatarId); var myLots = da.Roommates.GetAvatarsLots(session.AvatarId); if (packet.Type == ChangeRoommateType.INVITE) { //is invitee roommate somewhere else? count lot roommates and check for max var targ = da.Avatars.Get(packet.AvatarId); if (targ == null) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); } var targLots = da.Roommates.GetAvatarsLots(packet.AvatarId); if (targLots.Count > 0) { Status(session, ChangeRoommateResponseStatus.ROOMIE_ELSEWHERE); //request already pending or otherwise return; } var lotr = myLots.FirstOrDefault(); DbLot lot = null; if (lotr != null) { lot = da.Lots.Get(lotr.lot_id); } if (lotr == null || lot == null) { Status(session, ChangeRoommateResponseStatus.LOT_DOESNT_EXIST); //what?? return; } if (lot.owner_id != session.AvatarId) //only an owner can add roommates { Status(session, ChangeRoommateResponseStatus.YOU_ARE_NOT_OWNER); return; } var myLotRoomies = da.Roommates.GetLotRoommates(lotr.lot_id); if (myLotRoomies.Count >= 8) { //if pending roommates put us over, cancel some of them. //assume first is oldest request var pending = myLotRoomies.FirstOrDefault(x => x.is_pending == 1); if (pending == null) { Status(session, ChangeRoommateResponseStatus.TOO_MANY_ROOMMATES); return; } else { da.Roommates.DeclineRoommateRequest(pending.avatar_id, pending.lot_id); } } //create roommate request in database if (!da.Roommates.Create(new DbRoommate { avatar_id = packet.AvatarId, lot_id = lotr.lot_id, is_pending = 1, permissions_level = 0 })) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); return; } //if online, notify roommate of pending request. var targetSession = Sessions.GetByAvatarId(packet.AvatarId); if (targetSession != null) { targetSession.Write(new ChangeRoommateRequest() { Type = ChangeRoommateType.INVITE, AvatarId = session.AvatarId, LotLocation = lot.location }); } Status(session, ChangeRoommateResponseStatus.INVITE_SUCCESS); return; //if not, we'll catch them when they log in later. } else if (packet.Type == ChangeRoommateType.KICK) { var lot = da.Lots.GetByLocation(Context.ShardId, packet.LotLocation); if (lot == null) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); return; } var roommates = da.Roommates.GetLotRoommates(lot.lot_id); if (roommates.Count(x => x.is_pending == 0) <= 1) { //we're the last roommate here. the lot must be closed. This will cause the lot to fall off-map. if (lot.owner_id != session.AvatarId) { //only the owner can delete their lot Status(session, ChangeRoommateResponseStatus.UNKNOWN); return; } //TODO: let user do this with their lot falling off map. for now they must use start fresh mode. Status(session, ChangeRoommateResponseStatus.UNKNOWN); return; } else if (roommates.Any(x => x.avatar_id == packet.AvatarId && x.is_pending == 0)) { //avatar can be removed from the lot. var selfDelete = false; if (session.AvatarId == packet.AvatarId) { //self deletes are allowed selfDelete = true; } else if (lot.owner_id != session.AvatarId) { //only the owner can kickout other roommates Status(session, ChangeRoommateResponseStatus.YOU_ARE_NOT_OWNER); return; } if (da.Roommates.RemoveRoommate(packet.AvatarId, lot.lot_id) == 0) { //nothing happened when we tried to remove this user's roommate status. Status(session, ChangeRoommateResponseStatus.YOU_ARE_NOT_ROOMMATE); return; } if (selfDelete && lot.owner_id == session.AvatarId) { //lot needs a new owner da.Lots.ReassignOwner(lot.lot_id); //database will assign oldest roommate as owner. } var lotDS = await DataService.Get <FSO.Common.DataService.Model.Lot>(packet.LotLocation); if (lotDS != null) { lotDS.Lot_RoommateVec = lotDS.Lot_RoommateVec.Remove(packet.AvatarId); var newLot = da.Lots.Get(lot.lot_id); lotDS.Lot_OwnerVec = ImmutableList.Create(newLot.owner_id); } //if online, notify the lot var lotOwned = da.LotClaims.GetByLotID(lot.lot_id); if (lotOwned != null) { var lotServer = LotServers.GetLotServerSession(lotOwned.owner); if (lotServer != null) { //immediately notify lot of new roommate lotServer.Write(new NotifyLotRoommateChange() { AvatarId = packet.AvatarId, LotId = lot.lot_id, Change = Protocol.Gluon.Model.ChangeType.REMOVE_ROOMMATE }); } } else { //try force the lot open var result = await Lots.TryFindOrOpen(lot.location, 0, session); } //TODO: if offline, force the lot to open so we can remove the kicked out roommate's objects. var avatar = await DataService.Get <Avatar>(packet.AvatarId); if (avatar != null) { avatar.Avatar_LotGridXY = 0; } //try to notify roommates foreach (var roomie in roommates) { var kickedMe = roomie.avatar_id == packet.AvatarId; if (roomie.is_pending == 0 && !(kickedMe && selfDelete) && session.AvatarId != roomie.avatar_id) { var targetSession = Sessions.GetByAvatarId(roomie.avatar_id); if (targetSession != null) { targetSession.Write(new ChangeRoommateResponse() { Type = (kickedMe)?ChangeRoommateResponseStatus.GOT_KICKED:ChangeRoommateResponseStatus.ROOMMATE_LEFT, Extra = packet.AvatarId }); } } } if (selfDelete) { Status(session, ChangeRoommateResponseStatus.SELFKICK_SUCCESS); return; } else { Status(session, ChangeRoommateResponseStatus.KICK_SUCCESS); return; } } else { //not roommate?? Status(session, ChangeRoommateResponseStatus.YOU_ARE_NOT_ROOMMATE); return; } //if target avatar is our avatar, we are moving out //if we are owner of the lot, set the new owner to the first (earliest) roommate entry in the database. //if we are the last person in the lot, the lot must be closed before doing this. //make sure all references are set to new owner! //remove roommate entry for target avatar. //update lot data service and avatar data service for targets. //if lot open, notify lot server of change (roommate add/remove AND new/same owner) //the lot will remove objects as necessary //future: if lot closed, special request to a lot server to quickly open an unjoinable instance of the lot to remove our objects. } } } } catch (Exception e) { Status(session, ChangeRoommateResponseStatus.UNKNOWN); } }
private Avatar HydrateOne(DbAvatar dbAvatar, DbLot dbLot, List <DbJobLevel> levels, List <DbRelationship> rels, List <DbBookmark> bookmarks) { var result = new Avatar(); result.Avatar_Id = dbAvatar.avatar_id; result.Avatar_Name = dbAvatar.name; result.Avatar_IsOnline = false; result.Avatar_Description = dbAvatar.description; result.Avatar_Appearance = new AvatarAppearance { AvatarAppearance_BodyOutfitID = dbAvatar.body, AvatarAppearance_HeadOutfitID = dbAvatar.head, AvatarAppearance_SkinTone = dbAvatar.skin_tone }; var now = Epoch.Now; result.FetchTime = now; result.Avatar_Age = (uint)((now - dbAvatar.date) / ((long)60 * 60 * 24)); result.Avatar_Skills = new AvatarSkills { AvatarSkills_Body = dbAvatar.skill_body, AvatarSkills_LockLv_Body = dbAvatar.lock_body, AvatarSkills_Charisma = dbAvatar.skill_charisma, AvatarSkills_LockLv_Charisma = dbAvatar.lock_charisma, AvatarSkills_Cooking = dbAvatar.skill_cooking, AvatarSkills_LockLv_Cooking = dbAvatar.lock_cooking, AvatarSkills_Creativity = dbAvatar.skill_creativity, AvatarSkills_LockLv_Creativity = dbAvatar.lock_creativity, AvatarSkills_Logic = dbAvatar.skill_logic, AvatarSkills_LockLv_Logic = dbAvatar.lock_logic, AvatarSkills_Mechanical = dbAvatar.skill_mechanical, AvatarSkills_LockLv_Mechanical = dbAvatar.lock_mechanical }; result.Avatar_PrivacyMode = dbAvatar.privacy_mode; result.Avatar_SkillsLockPoints = (ushort)(20 + result.Avatar_Age / 7); var jobs = new List <JobLevel>(); foreach (var level in levels) { jobs.Add(new JobLevel { JobLevel_JobType = level.job_type, JobLevel_JobExperience = level.job_experience, JobLevel_JobGrade = level.job_level }); } result.Avatar_JobLevelVec = ImmutableList.ToImmutableList(jobs); result.Avatar_CurrentJob = dbAvatar.current_job; result.Avatar_Top100ListFilter = new Top100ListFilter() { Top100ListFilter_ResultsVec = ImmutableList.Create <uint>(), Top100ListFilter_Top100ListID = 0, }; var fvec = new Dictionary <Tuple <uint, bool>, Relationship>(); foreach (var rel in rels) { bool outgoing = false; uint target = 0; if (rel.from_id == dbAvatar.avatar_id) { outgoing = true; target = rel.to_id; } else { target = rel.from_id; } var tuple = new Tuple <uint, bool>(target, outgoing); Relationship relObj = null; if (!fvec.TryGetValue(tuple, out relObj)) { relObj = new Relationship { Relationship_IsOutgoing = outgoing, Relationship_TargetID = target, Relationship_CommentID = rel.comment_id ?? 0 }; fvec.Add(tuple, relObj); } if (rel.index == 0) { relObj.Relationship_STR = (sbyte)rel.value; } else { relObj.Relationship_LTR = (sbyte)rel.value; } } result.Avatar_FriendshipVec = ImmutableList.ToImmutableList(fvec.Values); if (dbLot != null) { result.Avatar_LotGridXY = dbLot.location; } result.Avatar_BookmarksVec = ImmutableList.ToImmutableList(bookmarks.Select(x => { return(new Bookmark { Bookmark_Type = x.type, Bookmark_TargetID = x.target_id }); })); return(result); }
public int Run() { if (Options.RestoreFolder == null) { Console.WriteLine("Please pass: <shard id> <lot folder path>"); return(1); } Console.WriteLine("Scanning content, please wait..."); VMContext.InitVMConfig(false); Content.Content.Init(Config.GameLocation, Content.ContentMode.SERVER); Console.WriteLine($"Starting property restore - scanning { Options.RestoreFolder }..."); if (!Directory.Exists(Options.RestoreFolder)) { Console.WriteLine($"Could not find the given directory: { Options.RestoreFolder }"); return(1); } var files = Directory.EnumerateFiles(Options.RestoreFolder).Where(x => x.ToLowerInvariant().EndsWith(".fsov")).ToList(); if (files.Count == 0) { Console.WriteLine($"Specified folder did not contain any lot saves (*.fsov). Note that blueprint .xmls are not supported."); return(1); } using (var da = DAFactory.Get()) { foreach (var file in files) { Console.WriteLine($"===== { Path.GetFileName(file) } ====="); var data = File.ReadAllBytes(file); var vm = new VMMarshal(); VMTSOLotState state; try { using (var mem = new MemoryStream(data)) { vm.Deserialize(new BinaryReader(mem)); } state = (VMTSOLotState)vm.PlatformState; } catch (Exception e) { Console.WriteLine($"Could not read FSOV. ({e.Message}) Continuing..."); continue; } var lot = new DbLot(); lot.name = state.Name; lot.location = state.LotID; lot.description = "Restored from FSOV"; if (state.PropertyCategory == 255) { state.PropertyCategory = 11; } lot.category = (LotCategory)state.PropertyCategory; lot.owner_id = RemapAvatarID(da, state.OwnerID); lot.neighborhood_id = state.NhoodID; lot.ring_backup_num = 0; lot.shard_id = Options.ShardId; lot.skill_mode = state.SkillMode; var random = new Random(); OwnerID = lot.owner_id ?? 0; if (lot.owner_id == 0) { lot.owner_id = null; } Console.WriteLine($"Attempting to restore '{state.Name}', at location {lot.location}."); var originalName = lot.name; int addedOffset = 1; var existingName = da.Lots.GetByName(lot.shard_id, lot.name); while (existingName != null) { lot.name = originalName + " (" + (addedOffset++) + ")"; Console.WriteLine($"Lot already exists with name {originalName}. Trying with name {lot.name}."); existingName = da.Lots.GetByName(lot.shard_id, lot.name); } var existingLocation = da.Lots.GetByLocation(Options.ShardId, lot.location); while (existingLocation != null) { lot.location = (uint)(random.Next(512) | (random.Next(512) << 16)); Console.WriteLine($"Lot already exists at location {existingLocation.location}. Placing at random location {lot.location}."); existingLocation = da.Lots.GetByLocation(Options.ShardId, lot.location); } var objectFromInventory = 0; var objectFromLot = 0; var objectCreate = 0; var objectIgnore = 0; string lotFolder = "./"; if (!Options.Report) { Console.WriteLine($"Creating database entry for lot..."); try { lot.lot_id = (int)da.Lots.Create(lot); Console.WriteLine($"Database entry for lot created! (ID {lot.lot_id})"); } catch (Exception e) { Console.WriteLine("FATAL! Could not create lot in database."); Console.WriteLine(e.ToString()); continue; } Console.WriteLine($"Creating and populating data folder for lot..."); try { lotFolder = Path.Combine(Config.SimNFS, $"Lots/{lot.lot_id.ToString("x8")}/"); Directory.CreateDirectory(lotFolder); } catch (Exception e) { Console.WriteLine("FATAL! Could not create lot data in NFS."); Console.WriteLine(e.ToString()); continue; } } foreach (var obj in vm.Entities) { var estate = obj.PlatformState as VMTSOObjectState; if (estate != null) { estate.OwnerID = RemapAvatarID(da, estate.OwnerID); //make sure the owners exist } } //check the objects var processed = new HashSet <uint>(); foreach (var obj in vm.Entities) { if (obj.PersistID == 0 || processed.Contains(obj.PersistID) || obj is VMAvatarMarshal) { if (PersistRemap.ContainsKey(obj.PersistID)) { obj.PersistID = PersistRemap[obj.PersistID]; } continue; } processed.Add(obj.PersistID); try { //does this object exist in the database? var dbObj = da.Objects.Get(obj.PersistID); var guid = (obj.MasterGUID == 0) ? obj.GUID : obj.MasterGUID; if (dbObj == null) { Console.Write("++"); Console.Write(guid); Console.Write(": Does not exist in DB. Creating new entry..."); objectCreate++; CreateDbObject(da, obj, lot); Done(); } else { if (dbObj.lot_id != null) { Console.Write("!!"); Console.Write(dbObj.dyn_obj_name ?? dbObj.type.ToString()); Console.Write(": In another property! "); if (Options.Safe || Options.Objects) { if (Options.Objects) { Console.Write("Creating a new entry..."); objectCreate++; CreateDbObject(da, obj, lot); Done(); } else { Console.WriteLine("Object will be ignored."); objectIgnore++; } } else { Console.Write("Taking the object back..."); objectFromLot++; if (!Options.Report) { da.Objects.SetInLot(obj.PersistID, (uint)lot.lot_id); } Done(); } } else { Console.Write("~~"); Console.Write(dbObj.dyn_obj_name ?? dbObj.type.ToString()); Console.Write(": In a user's inventory. "); if (dbObj.type != guid) { Console.Write("(WRONG GUID - MAKING NEW OBJECT) "); } if (Options.Objects || dbObj.type != guid) { Console.Write("Creating a new entry..."); objectCreate++; CreateDbObject(da, obj, lot); Done(); } else { Console.Write("Taking the object back..."); objectFromInventory++; if (!Options.Report) { da.Objects.SetInLot(obj.PersistID, (uint)lot.lot_id); } Done(); } } } } catch (Exception e) { Console.WriteLine($"Failed - {e.Message}. Continuing..."); } } Console.WriteLine($"Objects created: {objectCreate}, Objects from inventory: {objectFromInventory}, Objects from other lot: {objectFromLot}, Objects ignored: {objectIgnore}"); Console.WriteLine($"Object/lot owner avatars missing (replaced with lot owner): {AvatarIDs.Count(x => x.Key != x.Value)}"); Console.WriteLine("Object scan complete! Serializing restored state..."); byte[] newData; using (var mem = new MemoryStream()) { using (var writer = new BinaryWriter(mem)) { vm.SerializeInto(writer); newData = mem.ToArray(); } } Console.WriteLine("New FSOV created. Finalizing restore..."); if (!Options.Report) { File.WriteAllBytes(Path.Combine(lotFolder, "state_0.fsov"), newData); Console.WriteLine($"Restoring {Path.GetFileName(file)} complete!"); da.Lots.UpdateRingBackup(lot.lot_id, 0); } else { Console.WriteLine($"Report for {Path.GetFileName(file)} complete!"); } } } Console.WriteLine("All properties processed. Press any key to exit."); Console.ReadKey(); return(0); }
public void Handle(IVoltronSession session, BulletinRequest message) { if (session.IsAnonymous) //CAS users can't do this. { return; } try { using (var da = DA.Get()) { switch (message.Type) { case BulletinRequestType.GET_MESSAGES: //when a user logs in they will send this request to recieve all messages after their last recieved message. { var msgs = da.BulletinPosts.GetByNhoodId(message.TargetNHood, 0); session.Write(new BulletinResponse() { Type = BulletinResponseType.MESSAGES, Messages = msgs.Select(x => ToItem(x)).ToArray() }); return; } case BulletinRequestType.DELETE_MESSAGE: //delete bulletin message { //check if either we own this post or we're admin var post = da.BulletinPosts.Get(message.Value); if (post == null) { session.Write(Code(BulletinResponseType.FAIL_MESSAGE_DOESNT_EXIST)); return; //doesn't exist } if (post.avatar_id != session.AvatarId) { //not the owner of this post. are we an admin? var myAva = da.Avatars.Get(session.AvatarId); if (myAva == null || myAva.moderation_level == 0) { session.Write(Code(BulletinResponseType.FAIL_CANT_DELETE)); return; } } da.BulletinPosts.SoftDelete(message.Value); session.Write(Code(BulletinResponseType.SUCCESS)); return; } case BulletinRequestType.PROMOTE_MESSAGE: //promote message to the mayor channel { //check if either we're mayor of this post's nhood or we're admin //also the post should like, exist. var post = da.BulletinPosts.Get(message.Value); if (post == null) { session.Write(Code(BulletinResponseType.FAIL_MESSAGE_DOESNT_EXIST)); return; } if ((post.flags & 1) > 0) { session.Write(Code(BulletinResponseType.FAIL_ALREADY_PROMOTED)); return; //already promoted. } if (post.type != DbBulletinType.community) { session.Write(Code(BulletinResponseType.FAIL_BAD_PERMISSION)); return; } var postNhood = da.Neighborhoods.Get((uint)post.neighborhood_id); if (postNhood == null || postNhood.mayor_id != session.AvatarId) { session.Write(Code(BulletinResponseType.FAIL_NOT_MAYOR)); return; //no permission } post.flags |= 1; da.BulletinPosts.SetTypeFlag(message.Value, DbBulletinType.mayor, (int)post.flags); session.Write(Code(BulletinResponseType.SUCCESS)); return; } case BulletinRequestType.CAN_POST_MESSAGE: case BulletinRequestType.CAN_POST_SYSTEM_MESSAGE: case BulletinRequestType.POST_SYSTEM_MESSAGE: case BulletinRequestType.POST_MESSAGE: { var type = (message.Type == BulletinRequestType.POST_SYSTEM_MESSAGE) ? DbBulletinType.system : DbBulletinType.community; var myAva = da.Avatars.Get(session.AvatarId); if (myAva == null) { session.Write(Code(BulletinResponseType.FAIL_UNKNOWN)); return; } var now = Epoch.Now; if (now - myAva.move_date < MOVE_LIMIT_PERIOD && myAva.moderation_level == 0) { session.Write(Code(BulletinResponseType.SEND_FAIL_JUST_MOVED)); return; } var myLotID = da.Roommates.GetAvatarsLots(session.AvatarId)?.FirstOrDefault(); DbLot myLot = (myLotID != null) ? da.Lots.Get(myLotID.lot_id) : null; if (myLot == null || myLot.neighborhood_id != message.TargetNHood || message.Type == BulletinRequestType.POST_SYSTEM_MESSAGE || message.Type == BulletinRequestType.CAN_POST_SYSTEM_MESSAGE) { //need to live in this nhood to post //if we're an admin we can ignore this if (myAva.moderation_level == 0) { session.Write(Code(BulletinResponseType.SEND_FAIL_NON_RESIDENT)); return; } } var postNhood = da.Neighborhoods.Get((uint)message.TargetNHood); if (postNhood == null) { session.Write(Code(BulletinResponseType.FAIL_UNKNOWN)); return; } if (session.AvatarId == postNhood.mayor_id && type == DbBulletinType.community) { type = DbBulletinType.mayor; } //are we nhood gameplay banned? var ban = da.Neighborhoods.GetNhoodBan(myAva.user_id); if (ban != null) { session.Write(new BulletinResponse { Type = BulletinResponseType.SEND_FAIL_GAMEPLAY_BAN, Message = ban.ban_reason, BanEndDate = ban.end_date }); return; } //verify post frequency var last = da.BulletinPosts.LastUserPost(myAva.user_id, message.TargetNHood); int frequency = 0; switch (type) { case DbBulletinType.mayor: frequency = POST_FREQ_LIMIT_MAYOR; break; case DbBulletinType.community: frequency = POST_FREQ_LIMIT; break; } if (Epoch.Now - (last?.date ?? 0) < frequency) { session.Write(Code(BulletinResponseType.SEND_FAIL_TOO_FREQUENT)); return; } if (message.Type == BulletinRequestType.CAN_POST_MESSAGE || message.Type == BulletinRequestType.CAN_POST_SYSTEM_MESSAGE) { session.Write(Code(BulletinResponseType.SUCCESS)); return; } int?lotID = null; if (message.LotID != 0) { //verify the lot ID if one is included var lot = da.Lots.GetByLocation(Context.ShardId, message.LotID); if (lot == null) { session.Write(Code(BulletinResponseType.SEND_FAIL_INVALID_LOT)); return; } lotID = (int)message.LotID; } if (message.Message.Length == 0 || message.Message.Length > 1000) { session.Write(Code(BulletinResponseType.SEND_FAIL_INVALID_MESSAGE)); return; } if (message.Title.Length == 0 || message.Title.Length > 64) { session.Write(Code(BulletinResponseType.SEND_FAIL_INVALID_TITLE)); return; } var db = new DbBulletinPost() { avatar_id = session.AvatarId, date = Epoch.Now, title = message.Title, body = message.Message, flags = 0, lot_id = lotID, neighborhood_id = (int)message.TargetNHood, type = type }; try { db.bulletin_id = da.BulletinPosts.Create(db); } catch (Exception e) { LOG.Error(e.ToString()); session.Write(Code(BulletinResponseType.FAIL_UNKNOWN)); return; } session.Write(new BulletinResponse() { Type = BulletinResponseType.SEND_SUCCESS, Messages = new BulletinItem[] { ToItem(db) } }); } break; } } } catch (Exception e) { LOG.Error(e.ToString()); session.Write(Code(BulletinResponseType.FAIL_UNKNOWN)); return; } }