private void ParcelJoinHandler(Packet packet, LLAgent agent) { ParcelJoinPacket join = (ParcelJoinPacket)packet; int startX = (int)Math.Round(join.ParcelData.West) / 4; int startY = (int)Math.Round(join.ParcelData.South) / 4; int endX = ((int)Math.Round(join.ParcelData.East) / 4) - 1; int endY = ((int)Math.Round(join.ParcelData.North) / 4) - 1; if (startX < 0 || startY < 0 || endX < startX || endY < startY || endX > 63 || endY > 63 || startX > endX || startY > endY) { m_log.Warn(agent.Name + String.Format(" sent invalid ParcelJoin: West {0} South {1} East {2} North {3}", join.ParcelData.West, join.ParcelData.South, join.ParcelData.East, join.ParcelData.North)); return; } int largestArea = 0; Vector3 aabbMin, aabbMax; // Get the selected parcels List <SceneParcel> selectedParcels = new List <SceneParcel>(); for (int y = startY; y <= endY; y++) { for (int x = startX; x <= endX; x++) { SceneParcel parcel; if (m_parcels.TryGetParcel(m_parcels.GetParcelID(x, y), out parcel)) { if (!selectedParcels.Contains(parcel) && m_permissions.CanEditParcel(agent, parcel)) { // Largest parcel is the "master" parcel that smaller parcels are joined into int area = ParcelManager.GetParcelArea(parcel, out aabbMin, out aabbMax); if (area > largestArea) { largestArea = area; selectedParcels.Insert(0, parcel); } else { selectedParcels.Add(parcel); } } } } } // Enough parcels selected check if (selectedParcels.Count < 2) { m_scene.PresenceAlert(this, agent, "Not enough leased parcels in selection to join"); return; } // Same owner check for (int i = 1; i < selectedParcels.Count; i++) { if (selectedParcels[i].OwnerID != selectedParcels[0].OwnerID) { m_scene.PresenceAlert(this, agent, "All parcels must have the same owner before joining"); return; } } m_parcels.JoinParcels(selectedParcels); // Broadcast the new parcel overlay info m_scene.ForEachPresence(SendParcelOverlay); }