diff --git a/libsecondlife/MainAvatarStatus.cs b/libsecondlife/MainAvatarStatus.cs index b9125b32..8d7c2e55 100644 --- a/libsecondlife/MainAvatarStatus.cs +++ b/libsecondlife/MainAvatarStatus.cs @@ -339,6 +339,27 @@ namespace libsecondlife UpdateTimer.Enabled = Client.Settings.SEND_AGENT_UPDATES; } + /// + /// Send an AgentUpdate with the camera set at the current agent + /// position and pointing towards the heading specified + /// + /// Camera rotation in radians + /// Whether to send the AgentUpdate reliable + /// or not + public void UpdateFromHeading(double heading, bool reliable) + { + Client.Self.Status.Camera.CameraCenter = Client.Self.Position; + Client.Self.Status.Camera.CameraAtAxis.X = (float)Math.Cos(heading); + Client.Self.Status.Camera.CameraAtAxis.Y = (float)Math.Sin(heading); + Client.Self.Status.Camera.CameraLeftAxis.X = (float)-Math.Sin(heading); + Client.Self.Status.Camera.CameraLeftAxis.Y = (float)Math.Cos(heading); + Client.Self.Status.Camera.BodyRotation.Z = (float)Math.Sin(heading / 2.0d); + Client.Self.Status.Camera.BodyRotation.W = (float)Math.Cos(heading / 2.0d); + Client.Self.Status.Camera.HeadRotation = Client.Self.Status.Camera.BodyRotation; + + Client.Self.Status.SendUpdate(reliable); + } + /// /// Send new AgentUpdate packet to update our current camera /// position and rotation diff --git a/libsecondlife/TerrainManager.cs b/libsecondlife/TerrainManager.cs index ce9817b9..1f6d930e 100644 --- a/libsecondlife/TerrainManager.cs +++ b/libsecondlife/TerrainManager.cs @@ -135,7 +135,7 @@ namespace libsecondlife /// True if the lookup was successful, otherwise false public bool TerrainHeightAtPoint(ulong regionHandle, int x, int y, out float height) { - if (x > 0 && x < 256 && y > 0 && y < 256) + if (x >= 0 && x < 256 && y >= 0 && y < 256) { lock (SimPatches) { diff --git a/libsecondlife/examples/Heightmap/frmHeightmap.cs b/libsecondlife/examples/Heightmap/frmHeightmap.cs index 44561866..55d96cb0 100644 --- a/libsecondlife/examples/Heightmap/frmHeightmap.cs +++ b/libsecondlife/examples/Heightmap/frmHeightmap.cs @@ -15,13 +15,10 @@ namespace Heightmap { private SecondLife Client = new SecondLife(); private PictureBox[,] Boxes = new PictureBox[16, 16]; - private System.Timers.Timer UpdateTimer = new System.Timers.Timer(500); + private System.Timers.Timer UpdateTimer = new System.Timers.Timer(1000); private string FirstName, LastName, Password; - LLVector3 center = new LLVector3(128, 128, 40); - LLVector3 up = new LLVector3(0, 0, 0.9999f); - LLVector3 forward = new LLVector3(0, 0.9999f, 0); - LLVector3 left = new LLVector3(0.9999f, 0, 0); + double heading = -Math.PI; public frmHeightmap(string firstName, string lastName, string password) { @@ -107,16 +104,11 @@ namespace Heightmap void UpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { - forward.Y += 0.2f; - left.X += 0.2f; - - if (forward.Y >= 1.0f) forward.Y = 0.0f; - if (left.X >= 1.0f) left.X = 0.0f; - // Spin our camera in circles at the center of the sim to load all the terrain - Client.Self.UpdateCamera(MainAvatar.ControlFlags.NONE, center, forward, left, up, - LLQuaternion.Identity, LLQuaternion.Identity, 384.0f, MainAvatar.AgentFlags.None, - MainAvatar.AgentState.None, false); + heading += 0.5d; + if (heading > Math.PI) heading = -Math.PI; + + Client.Self.Status.UpdateFromHeading(heading, false); } void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data) diff --git a/libsecondlife/examples/TestClient/ClientManager.cs b/libsecondlife/examples/TestClient/ClientManager.cs index 528dba59..be27372d 100644 --- a/libsecondlife/examples/TestClient/ClientManager.cs +++ b/libsecondlife/examples/TestClient/ClientManager.cs @@ -84,10 +84,16 @@ namespace libsecondlife.TestClient TestClient client = new TestClient(this); + // Optimize the throttle + client.Throttle.Wind = 0; + client.Throttle.Cloud = 0; + client.Throttle.Land = 1000000; + client.Throttle.Task = 1000000; + client.SimPrims = SimPrims; client.Master = account.Master; - if (this.startpos.sim != null) + if (!String.IsNullOrEmpty(this.startpos.sim)) { if (this.startpos.x == 0 || this.startpos.y == 0 || this.startpos.z == 0) { diff --git a/libsecondlife/examples/TestClient/Commands/ParcelInfoCommand.cs b/libsecondlife/examples/TestClient/Commands/ParcelInfoCommand.cs index 81ea339a..3221c609 100644 --- a/libsecondlife/examples/TestClient/Commands/ParcelInfoCommand.cs +++ b/libsecondlife/examples/TestClient/Commands/ParcelInfoCommand.cs @@ -8,7 +8,7 @@ namespace libsecondlife.TestClient { public class ParcelInfoCommand : Command { - private ParcelDownloader Parcels; + private ParcelDownloader ParcelDownloader; private ManualResetEvent ParcelsDownloaded = new ManualResetEvent(false); private int ParcelCount = 0; @@ -17,14 +17,14 @@ namespace libsecondlife.TestClient Name = "parcelinfo"; Description = "Prints out info about all the parcels in this simulator"; - Parcels = new ParcelDownloader(testClient); - Parcels.OnParcelsDownloaded += new ParcelDownloader.ParcelsDownloadedCallback(Parcels_OnParcelsDownloaded); + ParcelDownloader = new ParcelDownloader(testClient); + ParcelDownloader.OnParcelsDownloaded += new ParcelDownloader.ParcelsDownloadedCallback(Parcels_OnParcelsDownloaded); testClient.Network.OnDisconnected += new NetworkManager.DisconnectedCallback(Network_OnDisconnected); } public override string Execute(string[] args, LLUUID fromAgentID) { - Parcels.DownloadSimParcels(Client.Network.CurrentSim); + ParcelDownloader.DownloadSimParcels(Client.Network.CurrentSim); ParcelsDownloaded.Reset(); ParcelsDownloaded.WaitOne(20000, false); @@ -39,8 +39,10 @@ namespace libsecondlife.TestClient { foreach (KeyValuePair parcel in Parcels) { - Console.WriteLine("Parcels[{0}]: Name: \"{1}\", Description: \"{2}\" ACL Count: {3}", parcel.Key, - parcel.Value.Name, parcel.Value.Desc, parcel.Value.AccessList.Count); + WaterType type = ParcelDownloader.GetWaterType(map, parcel.Value.LocalID); + + Console.WriteLine("Parcels[{0}]: Name: \"{1}\", Description: \"{2}\" ACL Count: {3}, Location: {4}", + parcel.Key, parcel.Value.Name, parcel.Value.Desc, parcel.Value.AccessList.Count, type.ToString()); } ParcelCount = Parcels.Count; diff --git a/libsecondlife/examples/TestClient/Commands/RegionInfoCommand.cs b/libsecondlife/examples/TestClient/Commands/RegionInfoCommand.cs new file mode 100644 index 00000000..750175fb --- /dev/null +++ b/libsecondlife/examples/TestClient/Commands/RegionInfoCommand.cs @@ -0,0 +1,45 @@ +using System; +using System.Text; +using libsecondlife; + +namespace libsecondlife.TestClient +{ + public class RegionInfoCommand : Command + { + public RegionInfoCommand(TestClient testClient) + { + Name = "regioninfo"; + Description = "Prints out info about all the current region"; + } + + public override string Execute(string[] args, LLUUID fromAgentID) + { + StringBuilder output = new StringBuilder(); + output.AppendLine(Client.Network.CurrentSim.ToString()); + output.Append("Access: "); + output.AppendLine(Client.Network.CurrentSim.Access.ToString()); + output.Append("Flags: "); + output.AppendLine(Client.Network.CurrentSim.Flags.ToString()); + output.Append("TerrainBase0: "); + output.AppendLine(Client.Network.CurrentSim.TerrainBase0.ToStringHyphenated()); + output.Append("TerrainBase1: "); + output.AppendLine(Client.Network.CurrentSim.TerrainBase1.ToStringHyphenated()); + output.Append("TerrainBase2: "); + output.AppendLine(Client.Network.CurrentSim.TerrainBase2.ToStringHyphenated()); + output.Append("TerrainBase3: "); + output.AppendLine(Client.Network.CurrentSim.TerrainBase3.ToStringHyphenated()); + output.Append("TerrainDetail0: "); + output.AppendLine(Client.Network.CurrentSim.TerrainDetail0.ToStringHyphenated()); + output.Append("TerrainDetail1: "); + output.AppendLine(Client.Network.CurrentSim.TerrainDetail1.ToStringHyphenated()); + output.Append("TerrainDetail2: "); + output.AppendLine(Client.Network.CurrentSim.TerrainDetail2.ToStringHyphenated()); + output.Append("TerrainDetail3: "); + output.AppendLine(Client.Network.CurrentSim.TerrainDetail3.ToStringHyphenated()); + output.Append("Water Height: "); + output.AppendLine(Client.Network.CurrentSim.WaterHeight.ToString()); + + return output.ToString(); + } + } +} diff --git a/libsecondlife/examples/TestClient/TestClient.csproj b/libsecondlife/examples/TestClient/TestClient.csproj index c9870c71..a6153fec 100644 --- a/libsecondlife/examples/TestClient/TestClient.csproj +++ b/libsecondlife/examples/TestClient/TestClient.csproj @@ -71,6 +71,7 @@ Code + Code diff --git a/libsecondlife/libsecondlife.Utilities/Utilities.cs b/libsecondlife/libsecondlife.Utilities/Utilities.cs index c84c9332..d1b59bee 100644 --- a/libsecondlife/libsecondlife.Utilities/Utilities.cs +++ b/libsecondlife/libsecondlife.Utilities/Utilities.cs @@ -7,6 +7,21 @@ using libsecondlife.Packets; namespace libsecondlife.Utilities { + /// + /// + /// + public enum WaterType + { + /// + Unknown, + /// + Dry, + /// + Waterfront, + /// + Underwater + } + public static class Realism { public readonly static LLUUID TypingAnimation = new LLUUID("c541c47f-e0c0-058b-ad1a-d6ae3a4584d9"); @@ -91,14 +106,14 @@ namespace libsecondlife.Utilities CheckTimer.Elapsed += new System.Timers.ElapsedEventHandler(CheckTimer_Elapsed); } - public static bool PersistentLogin(SecondLife client, string firstName, string lastName, string password, - string userAgent, string author) + public static bool PersistentLogin(SecondLife client, string firstName, string lastName, string password, + string userAgent, string start, string author) { int unknownLogins = 0; Start: - if (client.Network.Login(firstName, lastName, password, userAgent, author)) + if (client.Network.Login(firstName, lastName, password, userAgent, start, author)) { client.Log("Logged in to " + client.Network.CurrentSim, Helpers.LogLevel.Info); return true; @@ -535,10 +550,8 @@ namespace libsecondlife.Utilities /// Dictionary of 64x64 arrays of parcels which have been successfully downloaded /// for each simulator (and their LocalID's, 0 = Null) private Dictionary ParcelMarked = new Dictionary(); - /// private Dictionary> Parcels = new Dictionary>(); - - private ArrayList active_sims; + private List active_sims = new List(); /// /// Default constructor @@ -549,8 +562,6 @@ namespace libsecondlife.Utilities Client = client; Client.Parcels.OnParcelProperties += new ParcelManager.ParcelPropertiesCallback(Parcels_OnParcelProperties); Client.Parcels.OnAccessListReply += new ParcelManager.ParcelAccessListReplyCallback(Parcels_OnParcelAccessList); - - active_sims = new ArrayList(); } public void DownloadSimParcels(Simulator simulator) @@ -578,12 +589,104 @@ namespace libsecondlife.Utilities Client.Parcels.PropertiesRequest(simulator, 0.0f, 0.0f, 0.0f, 0.0f, 0, false); } - private void Parcels_OnParcelAccessList(Simulator simulator, int sequenceID, int localID, uint flags, - List accessEntries) { - Parcels[simulator][localID].AccessList = accessEntries; - } + /// + /// + /// + /// + /// + /// + public WaterType GetWaterType(int[,] map, int localid) + { + if (!Client.Settings.STORE_LAND_PATCHES) + { + Client.Log("GetWaterType() will not work without Settings.STORE_LAND_PATCHES set to true", + Helpers.LogLevel.Error); + return WaterType.Unknown; + } - private void Parcels_OnParcelProperties(Parcel parcel, ParcelManager.ParcelResult result, int sequenceID, + bool underwater = false; + bool abovewater = false; + + for (int y = 0; y < 64; y++) + { + for (int x = 0; x < 64; x++) + { + if (map[y, x] == localid) + { + for (int y1 = 0; y1 < 4; y1++) + { + for (int x1 = 0; x1 < 4; x1++) + { + float height; + int tries = 0; + + CheckHeight: + tries++; + + if (Client.Terrain.TerrainHeightAtPoint(Client.Network.CurrentSim.Handle, + x * 4 + x1, y * 4 + y1, out height)) + { + if (height < Client.Network.CurrentSim.WaterHeight) + { + underwater = true; + } + else + { + abovewater = true; + } + } + else if (tries > 4) + { + Console.WriteLine("Too many tries on this terrain block, skipping"); + continue; + } + else + { + Console.WriteLine("Terrain height is null at {0},{1} retrying", + x * 4 + x1, y * 4 + y1); + + // Terrain at this point hasn't been downloaded, move the camera to this spot + // and try again + Client.Self.Status.Camera.CameraCenter.X = (float)(x * 4 + x1); + Client.Self.Status.Camera.CameraCenter.Y = (float)(y * 4 + y1); + Client.Self.Status.Camera.CameraCenter.Z = Client.Self.Position.Z; + Client.Self.Status.SendUpdate(true); + + Thread.Sleep(1000); + goto CheckHeight; + } + } + } + } + } + } + + if (underwater && abovewater) + { + return WaterType.Waterfront; + } + else if (abovewater) + { + return WaterType.Dry; + } + else if (underwater) + { + return WaterType.Underwater; + } + else + { + Client.Log("Error decoding terrain for parcel " + localid, Helpers.LogLevel.Error); + return WaterType.Unknown; + } + } + + private void Parcels_OnParcelAccessList(Simulator simulator, int sequenceID, int localID, uint flags, + List accessEntries) + { + Parcels[simulator][localID].AccessList = accessEntries; + } + + private void Parcels_OnParcelProperties(Parcel parcel, ParcelManager.ParcelResult result, int sequenceID, bool snapSelection) { // Check if this is for a simulator we're concerned with @@ -592,7 +695,7 @@ namespace libsecondlife.Utilities // Warn about parcel property request errors and bail out if (result == ParcelManager.ParcelResult.NoData) { - Client.Log("ParcelDownloader received a NoData response, sequenceID " + sequenceID, + Client.Log("ParcelDownloader received a NoData response, sequenceID " + sequenceID, Helpers.LogLevel.Warning); return; } @@ -600,46 +703,55 @@ namespace libsecondlife.Utilities // Warn about unexpected data and bail out if (!ParcelMarked.ContainsKey(parcel.Simulator)) { - Client.Log("ParcelDownloader received unexpected parcel data for " + parcel.Simulator, - Helpers.LogLevel.Info); + Client.Log("ParcelDownloader received unexpected parcel data for " + parcel.Simulator, + Helpers.LogLevel.Warning); return; } - Client.Parcels.AccessListRequest(parcel.Simulator, parcel.LocalID, ParcelManager.AccessList.Ban, 0); - int x, y, index, bit; int[,] markers = ParcelMarked[parcel.Simulator]; - Dictionary simParcels = Parcels[parcel.Simulator]; // Add this parcel to the dictionary of LocalID -> Parcel mappings - lock (simParcels) - if (!simParcels.ContainsKey(parcel.LocalID)) - simParcels[parcel.LocalID] = parcel; + lock (Parcels[parcel.Simulator]) + if (!Parcels[parcel.Simulator].ContainsKey(parcel.LocalID)) + Parcels[parcel.Simulator][parcel.LocalID] = parcel; + + // Request the access list for this parcel + Client.Parcels.AccessListRequest(parcel.Simulator, parcel.LocalID, + ParcelManager.AccessList.Both, 0); // Mark this area as downloaded for (y = 0; y < 64; y++) + { for (x = 0; x < 64; x++) + { if (markers[y, x] == 0) { index = (y * 64) + x; bit = index % 8; index >>= 3; - if ((parcel.Bitmap[index] & (1 << bit)) != 0) - markers[y, x] = parcel.LocalID; + if ((parcel.Bitmap[index] & (1 << bit)) != 0) + markers[y, x] = parcel.LocalID; } + } + } // Request parcel information for the next missing area for (y = 0; y < 64; y++) + { for (x = 0; x < 64; x++) + { if (markers[y, x] == 0) { Client.Parcels.PropertiesRequest(parcel.Simulator, - (y+1) * 4.0f, (x+1) * 4.0f, - y * 4.0f, x * 4.0f, 0, false); + (y + 1) * 4.0f, (x + 1) * 4.0f, + y * 4.0f, x * 4.0f, 0, false); return; } + } + } // If we get here, there are no more zeroes in the markers map lock (active_sims) @@ -649,11 +761,10 @@ namespace libsecondlife.Utilities if (OnParcelsDownloaded != null) { // This map is complete, fire callback - try { OnParcelsDownloaded(parcel.Simulator, simParcels, markers); } + try { OnParcelsDownloaded(parcel.Simulator, Parcels[parcel.Simulator], markers); } catch (Exception e) { Client.Log(e.ToString(), Helpers.LogLevel.Error); } } } } } } -