public void GenerateTerrain() { Terrain = new VMTSOSurroundingTerrain(); var coords = MapCoordinates.Unpack(LotPersist.location); var map = Realestate.GetMap(); for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { Terrain.BlendN[x, y] = map.GetBlend((coords.X - 1) + x, (coords.Y - 1) + y); } } for (int y = 0; y < 3; y++) { for (int x = 0; x < 3; x++) { Terrain.Roads[x, y] = map.GetRoad((coords.X - 1) + x, (coords.Y - 1) + y); } } for (int y = 0; y < 4; y++) { for (int x = 0; x < 4; x++) { Terrain.Height[x, y] = map.GetElevation((coords.X - 1) + x, (coords.Y - 1) + y); } } }
protected override void PreLoad(Callback <uint, Neighborhood> appender) { using (var db = DAFactory.Get()) { var nhoods = db.Neighborhoods.All(ShardId); var midnight = LotVisitUtils.Midnight(); //gets this morning's midnight (basically current date, with day reset) var activityBeginning = midnight - new TimeSpan(7, 0, 0, 0); var visits = db.LotVisits.StreamBetweenPlusNhood(ShardId, activityBeginning, midnight).ToList(); var enumerator = visits.GetEnumerator(); var nhoodHours = new Dictionary <uint, double>(); while (enumerator.MoveNext()) { var visit = enumerator.Current; var span = LotVisitUtils.CalculateDateOverlap(activityBeginning, midnight, visit.time_created, visit.time_closed.Value); if (nhoodHours.ContainsKey(visit.neighborhood_id)) { nhoodHours[visit.neighborhood_id] += span.TotalMinutes; } else { nhoodHours.Add(visit.neighborhood_id, span.TotalMinutes); } } var order = nhoodHours.OrderByDescending(x => x.Value).ToList(); WorstRating = nhoods.Count; foreach (var item in nhoods) { var lots = db.Lots.GetLocationsInNhood((uint)item.neighborhood_id); var avatars = db.Avatars.GetLivingInNhood((uint)item.neighborhood_id); var townHall = db.Lots.Get(item.town_hall_id ?? 0)?.location ?? 0; var cycle = (item.election_cycle_id == null) ? null : db.Elections.GetCycle(item.election_cycle_id.Value); var converted = HydrateOne(item, avatars, lots, townHall, cycle, visits, order); var intId = (uint)item.neighborhood_id; appender(intId, converted); } // var neighObj = nhoods.Select(x => { var loc = MapCoordinates.Unpack(x.location); var result = new CityNeighbourhood() { Name = x.name, Color = new Color(x.color), Description = x.description, GUID = x.guid, Location = new Point(loc.X, loc.Y), ID = x.neighborhood_id }; return(result); }).ToList(); Lots.CityRepresentation.City_NeighJSON = JsonConvert.SerializeObject(neighObj); } }
protected override Lot CreateInstance(uint key) { var coords = MapCoordinates.Unpack(key); var lot = base.CreateInstance(key); lot.Id = key; lot.Lot_Location = new Location() { Location_X = coords.X, Location_Y = coords.Y }; //TODO: Use the string tables lot.Lot_Name = "Retrieving..."; return(lot); }
//Should only get here for non-occupied lots that just need a price, we can avoid caching these protected override Lot LazyLoad(uint key) { var location = MapCoordinates.Unpack(key); //Empty lot return(new Lot { Id = key, Lot_IsOnline = false, Lot_Location = new Location { Location_X = location.X, Location_Y = location.Y }, //Lot_Price = 0, Lot_Price = (uint)Realestate.GetPurchasePrice(location.X, location.Y), Lot_OwnerVec = ImmutableList.Create <uint>(), Lot_RoommateVec = ImmutableList.Create <uint>(), Lot_Thumbnail = new cTSOGenericData(new byte[0]), Lot_ThumbnailCheckSum = key }); }
public void LoadAdj() { LOG.Info("Loading adj lots for lot with dbid = " + Context.DbId); HollowLots = new byte[9][]; var myPos = MapCoordinates.Unpack(LotPersist.location); foreach (var lot in LotAdj) { try { var adjLotStr = lot.lot_id.ToString("x8"); var path = Path.Combine(Config.SimNFS, "Lots/" + adjLotStr + "/hollow.fsoh"); var pos = MapCoordinates.Unpack(lot.location); var x = (pos.X - myPos.X) + 1; var y = (pos.Y - myPos.Y) + 1; if (x < 0 || x > 2 || y < 0 || y > 2) { continue; //out of range (why does this happen?) } 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); HollowLots[y * 3 + x] = file; } } catch (Exception e) { LOG.Warn("Failed to load adjacent lot :("); LOG.Warn(e.ToString()); //don't bother } } }
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, PurchaseLotRequest packet) { if (session.IsAnonymous) //CAS users can't do this. { return; } var isPurchasable = Realestate.IsPurchasable(packet.LotLocation_X, packet.LotLocation_Y); if (!isPurchasable) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.LOT_NOT_PURCHASABLE }); return; } var packedLocation = MapCoordinates.Pack(packet.LotLocation_X, packet.LotLocation_Y); var price = Realestate.GetPurchasePrice(packet.LotLocation_X, packet.LotLocation_Y); int resultFunds; uint lotId = 0; using (var db = DA.Get()) { var ownedLot = db.Lots.GetByOwner(session.AvatarId); if (ownedLot != null) { //we own the lot we are roomie of. var roommates = db.Roommates.GetLotRoommates(ownedLot.lot_id); var ds = await DataService.Get <FSO.Common.DataService.Model.Lot>(ownedLot.location); if (ds.Lot_IsOnline) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NOT_OFFLINE_FOR_MOVE //TODO: race condition might make this possible? }); return; } if (roommates.Count > 1) { //cannot start fresh with roommates for now. packet.StartFresh = false; } var oldLoc = MapCoordinates.Unpack(ownedLot.location); var moveCost = price - Realestate.GetPurchasePrice(oldLoc.X, oldLoc.Y); moveCost += 2000; //flat rate for moving location var transactionResult = db.Avatars.Transaction(session.AvatarId, uint.MaxValue, moveCost, 5); //expenses misc... maybe add specific for lot resultFunds = transactionResult.source_budget; if (!transactionResult.success) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.INSUFFICIENT_FUNDS, NewFunds = resultFunds }); return; } db.Lots.UpdateLocation(ownedLot.lot_id, packedLocation, packet.StartFresh); DataService.Invalidate <FSO.Common.DataService.Model.Lot>(ownedLot.location); //nullify old lot DataService.Invalidate <FSO.Common.DataService.Model.Lot>(packedLocation); //update new lot } else { //we may still be roomie in a lot. If we are, we must be removed from that lot. var myLots = db.Roommates.GetAvatarsLots(session.AvatarId); if (myLots.Count > 0) { //we can't be in the lot when this happens. Make sure city owns avatar. bool canEvict = session.AvatarClaimId != 0; if (!canEvict) { var claim = db.AvatarClaims.Get(session.AvatarClaimId); if (claim.owner == Context.Config.Call_Sign) { db.Roommates.RemoveRoommate(session.AvatarId, myLots[0].lot_id); canEvict = true; } else { canEvict = false; } } if (!canEvict) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.IN_LOT_CANT_EVICT }); return; } } var name = packet.Name; if (!GlobalRealestate.ValidateLotName(name)) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NAME_VALIDATION_ERROR }); return; } var transactionResult = db.Avatars.Transaction(session.AvatarId, uint.MaxValue, price, 5); //expenses misc... maybe add specific for lot resultFunds = transactionResult.source_budget; if (!transactionResult.success) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.INSUFFICIENT_FUNDS, NewFunds = resultFunds }); return; } try { lotId = db.Lots.Create(new DbLot { name = name, shard_id = Context.ShardId, location = packedLocation, owner_id = session.AvatarId, created_date = Epoch.Now, category_change_date = Epoch.Default, category = LotCategory.none, buildable_area = 1, description = "" }); DataService.Invalidate <FSO.Common.DataService.Model.Lot>(packedLocation); } catch (Exception ex) { var returnMoney = db.Avatars.Transaction(uint.MaxValue, session.AvatarId, price, 5); //refund //Name taken if (ex.Message == "NAME") { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NAME_TAKEN, //TODO: this can also happen if the location was taken. (location is a UNIQUE row) NewFunds = returnMoney.dest_budget }); } else { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.UNKNOWN, //likely already roommate somewhere else, or we got race condition'd by another roomie request NewFunds = returnMoney.dest_budget }); } return; } } } //lot init happens on first join, as part of the loading process. If the lot somehow crashes before first save, it'll just be a blank slate again. //TODO: Broadcast to the world a new lot exists. i think we do this? //Update my sim's lot var avatar = await DataService.Get <Avatar>(session.AvatarId); if (avatar != null) { avatar.Avatar_LotGridXY = packedLocation; } session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.SUCCESS, NewLotId = lotId, NewFunds = resultFunds }); }
public async void Handle(IVoltronSession session, PurchaseLotRequest packet) { if (session.IsAnonymous) //CAS users can't do this. { return; } var isPurchasable = Realestate.IsPurchasable(packet.LotLocation_X, packet.LotLocation_Y); if (!isPurchasable) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.LOT_NOT_PURCHASABLE }); return; } var packedLocation = MapCoordinates.Pack(packet.LotLocation_X, packet.LotLocation_Y); var price = Realestate.GetPurchasePrice(packet.LotLocation_X, packet.LotLocation_Y); if (packet.MayorMode) { price = 2000; } int resultFunds; uint lotId = 0; using (var db = DA.Get()) { if (db.Lots.GetByLocation(Context.ShardId, packedLocation) != null) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.LOT_TAKEN, }); return; } var targNhood = db.Neighborhoods.GetByLocation(packedLocation); var nhoodDS = await DataService.Get <Neighborhood>((uint)targNhood.neighborhood_id); DbLot ownedLot; if (packet.MayorMode) { // we want to move or place the town hall lot for the neighbourhood we're mayor of. // 1. find what neighbourhood we are mayor of. fail if none. // 2. verify the chosen location is in the correct neighborhood. fail if not // 3. get the town hall lot for our nhood. exists = move, doesn't = new var nhood = db.Neighborhoods.GetByMayor(session.AvatarId); if (nhood == null) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.TH_NOT_MAYOR }); return; } //verify the neighbourhood makes sense if (nhood.neighborhood_id != (targNhood?.neighborhood_id ?? 0)) { //we can't put this neighbourhood's town hall in another neighbourhood. session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.TH_INCORRECT_NHOOD }); return; } //find the town hall property if (nhood.town_hall_id == null) { ownedLot = null; } else { ownedLot = db.Lots.Get(nhood.town_hall_id.Value); } } else { if ((targNhood.flag & 1) > 0) { var me = db.Avatars.Get(session.AvatarId); if (me == null || me.moderation_level == 0) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NHOOD_RESERVED, }); return; } } ownedLot = db.Lots.GetByOwner(session.AvatarId); } if (ownedLot != null) { //we own the lot we are roomie of. var roommates = db.Roommates.GetLotRoommates(ownedLot.lot_id); var ds = await DataService.Get <FSO.Common.DataService.Model.Lot>(ownedLot.location); if (ds.Lot_IsOnline) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NOT_OFFLINE_FOR_MOVE //TODO: race condition might make this possible? }); return; } if (roommates.Count > 1) { //cannot start fresh with roommates for now. packet.StartFresh = false; } var oldLoc = MapCoordinates.Unpack(ownedLot.location); var moveCost = price - Realestate.GetPurchasePrice(oldLoc.X, oldLoc.Y); moveCost += 2000; //flat rate for moving location if (packet.MayorMode) { moveCost = 2000; } var transactionResult = db.Avatars.Transaction(session.AvatarId, uint.MaxValue, moveCost, 5); //expenses misc... maybe add specific for lot resultFunds = transactionResult.source_budget; if (!transactionResult.success) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.INSUFFICIENT_FUNDS, NewFunds = resultFunds }); return; } if (!db.Lots.UpdateLocation(ownedLot.lot_id, packedLocation, packet.StartFresh)) { //needs to refund the player. var transactionResult2 = db.Avatars.Transaction(uint.MaxValue, session.AvatarId, moveCost, 5); session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.LOT_TAKEN, NewFunds = transactionResult2.source_budget }); return; } DataService.Invalidate <FSO.Common.DataService.Model.Lot>(ownedLot.location); //nullify old lot DataService.Invalidate <FSO.Common.DataService.Model.Lot>(packedLocation); //update new lot if (packet.MayorMode) { if (nhoodDS != null) { nhoodDS.Neighborhood_TownHallXY = packedLocation; } } if (nhoodDS != null) { nhoodDS.Neighborhood_LotCount = (uint)db.Lots.GetLocationsInNhood(nhoodDS.Id).Count; nhoodDS.Neighborhood_AvatarCount = (uint)db.Avatars.GetLivingInNhood(nhoodDS.Id).Count; } var oldNhood = await DataService.Get <Neighborhood>(ownedLot.neighborhood_id); if (oldNhood != null) { oldNhood.Neighborhood_LotCount = (uint)db.Lots.GetLocationsInNhood(oldNhood.Id).Count; oldNhood.Neighborhood_AvatarCount = (uint)db.Avatars.GetLivingInNhood(oldNhood.Id).Count; } //update nhood move date for all roommates if (ownedLot.neighborhood_id != targNhood.neighborhood_id) { foreach (var roomie in roommates) { db.Avatars.UpdateMoveDate(roomie.avatar_id, Epoch.Now); } } } else { //we may still be roomie in a lot. If we are, we must be removed from that lot. if (!packet.MayorMode) { var myLots = db.Roommates.GetAvatarsLots(session.AvatarId); if (myLots.Count > 0) { if (myLots[0].permissions_level > 1) { //owner should not be able to move out of a lot implicitly session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.UNKNOWN }); return; } var lot = db.Lots.Get(myLots[0].lot_id); if (lot != null) { var kickResult = await Kernel.Get <ChangeRoommateHandler>().TryKick(lot.location, session.AvatarId, session.AvatarId); if (kickResult != Protocol.Electron.Model.ChangeRoommateResponseStatus.SELFKICK_SUCCESS) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.IN_LOT_CANT_EVICT }); return; } } /* * //we can't be in the lot when this happens. Make sure city owns avatar. * bool canEvict = session.AvatarClaimId != 0; * if (!canEvict) * { * var claim = db.AvatarClaims.Get(session.AvatarClaimId); * if (claim.owner == Context.Config.Call_Sign) * { * db.Roommates.RemoveRoommate(session.AvatarId, myLots[0].lot_id); * canEvict = true; * } * else canEvict = false; * } * * if (!canEvict) * { * session.Write(new PurchaseLotResponse() * { * Status = PurchaseLotStatus.FAILED, * Reason = PurchaseLotFailureReason.IN_LOT_CANT_EVICT * }); * return; * } */ } } var name = packet.Name; if (!GlobalRealestate.ValidateLotName(name)) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NAME_VALIDATION_ERROR }); return; } var transactionResult = db.Avatars.Transaction(session.AvatarId, uint.MaxValue, price, 5); //expenses misc... maybe add specific for lot resultFunds = transactionResult.source_budget; if (!transactionResult.success) { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.INSUFFICIENT_FUNDS, NewFunds = resultFunds }); return; } try { lotId = db.Lots.Create(new DbLot { name = name, shard_id = Context.ShardId, location = packedLocation, owner_id = session.AvatarId, created_date = Epoch.Now, category_change_date = Epoch.Default, category = (packet.MayorMode) ? LotCategory.community : LotCategory.none, neighborhood_id = (uint)targNhood.neighborhood_id, buildable_area = 1, description = "" }); DataService.Invalidate <FSO.Common.DataService.Model.Lot>(packedLocation); if (packet.MayorMode) { db.Neighborhoods.UpdateTownHall((uint)targNhood.neighborhood_id, lotId); if (nhoodDS != null) { nhoodDS.Neighborhood_TownHallXY = packedLocation; } } if (nhoodDS != null) { nhoodDS.Neighborhood_LotCount = (uint)db.Lots.GetLocationsInNhood(nhoodDS.Id).Count; nhoodDS.Neighborhood_AvatarCount = (uint)db.Avatars.GetLivingInNhood(nhoodDS.Id).Count; db.Avatars.UpdateMoveDate(session.AvatarId, Epoch.Now); } } catch (Exception ex) { var returnMoney = db.Avatars.Transaction(uint.MaxValue, session.AvatarId, price, 5); //refund //Name taken if (ex.Message == "NAME") { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.NAME_TAKEN, //TODO: this can also happen if the location was taken. (location is a UNIQUE row) NewFunds = returnMoney.dest_budget }); } else { session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.FAILED, Reason = PurchaseLotFailureReason.UNKNOWN, //likely already roommate somewhere else, or we got race condition'd by another roomie request NewFunds = returnMoney.dest_budget }); } return; } } } //lot init happens on first join, as part of the loading process. If the lot somehow crashes before first save, it'll just be a blank slate again. //Update my sim's lot var avatar = await DataService.Get <Avatar>(session.AvatarId); if (avatar != null) { avatar.Avatar_LotGridXY = packedLocation; } session.Write(new PurchaseLotResponse() { Status = PurchaseLotStatus.SUCCESS, NewLotId = lotId, NewFunds = resultFunds }); }