diff --git a/OpenMetaverse.Http/EventQueueServer.cs b/OpenMetaverse.Http/EventQueueServer.cs index f21cf3a0..cc864c7b 100644 --- a/OpenMetaverse.Http/EventQueueServer.cs +++ b/OpenMetaverse.Http/EventQueueServer.cs @@ -70,8 +70,8 @@ namespace OpenMetaverse.Http HttpListener server; BlockingQueue eventQueue; int currentID; - bool running; - bool threadRunning; + volatile bool running; + volatile bool threadRunning; IHttpClientContext context; IHttpRequest request; IHttpResponse response; @@ -141,8 +141,11 @@ namespace OpenMetaverse.Http if (threadRunning) { Logger.Log.Info("[EventQueue] New connection opened to the event queue while a previous connection is open. Closing old connection"); - // Kill the previous handler thread before starting a new one - SendEvent(null); + + // If the old connection is still open, queue a signal to close it. Otherwise, just wait for the closed + // connection to be detected by the handler thread + if (context.Stream != null && context.Stream.CanWrite) + SendEvent(null); while (threadRunning && running) Thread.Sleep(50); @@ -188,7 +191,7 @@ namespace OpenMetaverse.Http EventQueueEvent eventQueueEvent = null; int totalMsPassed = 0; - while (running) + while (running && context.Stream != null && context.Stream.CanWrite) { if (eventQueue.Dequeue(BATCH_WAIT_INTERVAL, ref eventQueueEvent)) { diff --git a/OpenMetaverse/GridManager.cs b/OpenMetaverse/GridManager.cs index d5902874..02546c10 100644 --- a/OpenMetaverse/GridManager.cs +++ b/OpenMetaverse/GridManager.cs @@ -63,7 +63,7 @@ namespace OpenMetaverse MatureEvent = 3, /// Popular location Popular = 4, - /// Location belonging to the current agent + /// Locations of avatar groups in a region AgentLocations = 6, /// Land for sale LandForSale = 7, diff --git a/Programs/GridImageUpload/frmGridImageUpload.cs b/Programs/GridImageUpload/frmGridImageUpload.cs index 5aa82d48..da59ef8a 100644 --- a/Programs/GridImageUpload/frmGridImageUpload.cs +++ b/Programs/GridImageUpload/frmGridImageUpload.cs @@ -163,6 +163,8 @@ namespace GridImageUpload UploadData = OpenJPEG.EncodeFromImage(bitmap, chkLossless.Checked); Logger.Log("Finished encoding", Helpers.LogLevel.Info, Client); + + //System.IO.File.WriteAllBytes("out.jp2", UploadData); } } catch (Exception ex) diff --git a/Programs/Simian/Agent.cs b/Programs/Simian/Agent.cs index 44fe9e85..04644826 100644 --- a/Programs/Simian/Agent.cs +++ b/Programs/Simian/Agent.cs @@ -110,5 +110,27 @@ namespace Simian Avatar = avatar; Info = info; } + + public AvatarAppearancePacket BuildAppearancePacket() + { + AvatarAppearancePacket appearance = new AvatarAppearancePacket(); + appearance.ObjectData.TextureEntry = this.Avatar.Prim.Textures.GetBytes(); + appearance.Sender.ID = this.ID; + appearance.Sender.IsTrial = false; + + int count = this.Info.VisualParams != null ? this.Info.VisualParams.Length : 0; + + appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[count]; + for (int i = 0; i < count; i++) + { + appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock(); + appearance.VisualParam[i].ParamValue = this.Info.VisualParams[i]; + } + + if (count != 218) + Logger.Log("Built an odd appearance packet with VisualParams.Length=" + count, Helpers.LogLevel.Warning); + + return appearance; + } } } diff --git a/Programs/Simian/Extensions/AssetManager.cs b/Programs/Simian/Extensions/AssetManager.cs index 4a359057..ca63e1db 100644 --- a/Programs/Simian/Extensions/AssetManager.cs +++ b/Programs/Simian/Extensions/AssetManager.cs @@ -43,8 +43,9 @@ namespace Simian } } - LoadAssets(Simian.DATA_DIR); + LoadAssets(Simian.DEFAULT_ASSET_DIR); LoadAssets(UploadDir); + Logger.Log("Local asset store loaded with " + AssetStore.Count + " assets", Helpers.LogLevel.Info); return true; } diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index 95f417fe..4243f7c7 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -68,6 +68,7 @@ namespace Simian public ITaskInventoryProvider TaskInventory { get { return taskInventory; } } public IUDPProvider UDP { get { return udp; } } + public X509Certificate2 RegionCertificate { get { return regionCert; } } public uint RegionX { get { return regionX; } @@ -107,6 +108,7 @@ namespace Simian // Event queues for each avatar in the scene Dictionary eventQueues = new Dictionary(); int currentLocalID = 1; + X509Certificate2 regionCert; ulong regionHandle; UUID regionID = UUID.Random(); TerrainPatch[,] heightmap = new TerrainPatch[16, 16]; @@ -117,7 +119,7 @@ namespace Simian uint regionY; string regionName; Vector3 defaultPosition = new Vector3(128f, 128f, 30f); - Vector3 defaultLookAt = Vector3.UnitX; + Vector3 defaultLookAt = Vector3.UnitZ; /// Track the eight neighboring tiles around us RegionInfo[] neighbors = new RegionInfo[8]; /// List of callback URIs for pending client connections. When a new client connection @@ -135,6 +137,7 @@ namespace Simian this.regionName = regionInfo.Name; this.endpoint = regionInfo.IPAndPort; this.regionID = regionInfo.ID; + this.regionCert = regionCert; // Set the properties because this will automatically update the regionHandle RegionX = regionInfo.X; @@ -737,7 +740,7 @@ namespace Simian if (agent.Info.VisualParams != null) { // Send the appearance packet to all other clients - AvatarAppearancePacket appearance = BuildAppearancePacket(agent); + AvatarAppearancePacket appearance = agent.BuildAppearancePacket(); ForEachAgent( delegate(Agent recipient) { @@ -969,6 +972,66 @@ namespace Simian #endregion Capabilities Interfaces + public void InformClientOfNeighbors(Agent agent) + { + for (int i = 0; i < 8; i++) + { + if (!agent.NeighborConnections[i] && neighbors[i].Online) + { + Logger.Log("Sending enable_client for " + agent.FullName + " to neighbor " + neighbors[i].Name, Helpers.LogLevel.Info); + + // Create a callback for enable_client_complete + Uri callbackUri = server.Capabilities.CreateCapability(EnableClientCompleteCapHandler, false, null); + + OSDMap map = new OSDMap(); + map["agent_id"] = OSD.FromUUID(agent.ID); + map["session_id"] = OSD.FromUUID(agent.SessionID); + map["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID); + map["circuit_code"] = OSD.FromInteger((int)agent.CircuitCode); + map["first_name"] = OSD.FromString(agent.Info.FirstName); + map["last_name"] = OSD.FromString(agent.Info.LastName); + map["callback_uri"] = OSD.FromUri(callbackUri); + + AutoResetEvent waitEvent = new AutoResetEvent(false); + + CapsClient request = new CapsClient(neighbors[i].EnableClientCap); + request.OnComplete += + delegate(CapsClient client, OSD result, Exception error) + { + OSDMap response = result as OSDMap; + if (response != null) + { + bool success = response["success"].AsBoolean(); + Logger.Log("enable_client response: " + success, Helpers.LogLevel.Info); + + if (success) + { + // Send the EnableSimulator capability to clients + OSDMap llsdSimInfo = new OSDMap(3); + + llsdSimInfo.Add("Handle", OSD.FromULong(neighbors[i].Handle)); + llsdSimInfo.Add("IP", OSD.FromBinary(neighbors[i].IPAndPort.Address.GetAddressBytes())); + llsdSimInfo.Add("Port", OSD.FromInteger(neighbors[i].IPAndPort.Port)); + + OSDArray arr = new OSDArray(1); + arr.Add(llsdSimInfo); + + OSDMap llsdBody = new OSDMap(1); + llsdBody.Add("SimulatorInfo", arr); + + SendEvent(agent, "EnableSimulator", llsdBody); + } + } + waitEvent.Set(); + }; + request.StartRequest(map); + + if (!waitEvent.WaitOne(30 * 1000, false)) + Logger.Log("enable_client request timed out", Helpers.LogLevel.Warning); + } + } + } + #region Callback Handlers public bool SeedCapabilityHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) @@ -1044,7 +1107,7 @@ namespace Simian info.AccessLevel = "M"; info.FirstName = firstName; info.Height = 1.9f; - info.HomeLookAt = Vector3.UnitX; + info.HomeLookAt = Vector3.UnitZ; info.HomePosition = new Vector3(128f, 128f, 25f); info.HomeRegionHandle = regionHandle; info.ID = agentID; @@ -1197,7 +1260,7 @@ namespace Simian AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); complete.AgentData.AgentID = agent.ID; complete.AgentData.SessionID = agent.SessionID; - complete.Data.LookAt = Vector3.UnitX; // TODO: Properly implement LookAt someday + complete.Data.LookAt = Vector3.UnitZ; // TODO: Properly implement LookAt someday complete.Data.Position = agent.Avatar.Prim.Position; complete.Data.RegionHandle = regionHandle; complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); @@ -1205,117 +1268,16 @@ namespace Simian udp.SendPacket(agent.ID, complete, PacketCategory.Transaction); - // Send updates and appearances for every avatar to this new avatar - SynchronizeStateTo(agent); - //HACK: Notify everyone when someone logs on to the simulator OnlineNotificationPacket online = new OnlineNotificationPacket(); online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1]; online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); online.AgentBlock[0].AgentID = agent.ID; udp.BroadcastPacket(online, PacketCategory.State); - - // Initiate the connection process for this agent to neighboring regions - InformClientOfNeighbors(agent); } #endregion Callback Handlers - void InformClientOfNeighbors(Agent agent) - { - for (int i = 0; i < 8; i++) - { - if (!agent.NeighborConnections[i] && neighbors[i].Online) - { - Logger.Log("Sending enable_client for " + agent.FullName + " to neighbor " + neighbors[i].Name, Helpers.LogLevel.Info); - - // Create a callback for enable_client_complete - Uri callbackUri = server.Capabilities.CreateCapability(EnableClientCompleteCapHandler, false, null); - - OSDMap map = new OSDMap(); - map["agent_id"] = OSD.FromUUID(agent.ID); - map["session_id"] = OSD.FromUUID(agent.SessionID); - map["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID); - map["circuit_code"] = OSD.FromInteger((int)agent.CircuitCode); - map["first_name"] = OSD.FromString(agent.Info.FirstName); - map["last_name"] = OSD.FromString(agent.Info.LastName); - map["callback_uri"] = OSD.FromUri(callbackUri); - - AutoResetEvent waitEvent = new AutoResetEvent(false); - - CapsClient request = new CapsClient(neighbors[i].EnableClientCap); - request.OnComplete += - delegate(CapsClient client, OSD result, Exception error) - { - OSDMap response = result as OSDMap; - if (response != null) - { - bool success = response["success"].AsBoolean(); - Logger.Log("enable_client response: " + success, Helpers.LogLevel.Info); - - if (success) - { - // Send the EnableSimulator capability to clients - OSDMap llsdSimInfo = new OSDMap(3); - - llsdSimInfo.Add("Handle", OSD.FromULong(neighbors[i].Handle)); - llsdSimInfo.Add("IP", OSD.FromBinary(neighbors[i].IPAndPort.Address.GetAddressBytes())); - llsdSimInfo.Add("Port", OSD.FromInteger(neighbors[i].IPAndPort.Port)); - - OSDArray arr = new OSDArray(1); - arr.Add(llsdSimInfo); - - OSDMap llsdBody = new OSDMap(1); - llsdBody.Add("SimulatorInfo", arr); - - SendEvent(agent, "EnableSimulator", llsdBody); - } - } - waitEvent.Set(); - }; - request.StartRequest(map); - - if (!waitEvent.WaitOne(30 * 1000, false)) - Logger.Log("enable_client request timed out", Helpers.LogLevel.Warning); - } - } - } - - // HACK: The reduction provider will deprecate this at some point - void SynchronizeStateTo(Agent agent) - { - // Send the parcel overlay - parcels.SendParcelOverlay(agent); - - // Send object updates for objects and avatars - sceneObjects.ForEach(delegate(SimulationObject obj) - { - ObjectUpdatePacket update = new ObjectUpdatePacket(); - update.RegionData.RegionHandle = regionHandle; - update.RegionData.TimeDilation = (ushort)(physics.TimeDilation * (float)UInt16.MaxValue); - update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC); - - udp.SendPacket(agent.ID, update, PacketCategory.State); - }); - - // Send appearances for all avatars - ForEachAgent( - delegate(Agent otherAgent) - { - if (otherAgent != agent) - { - // Send appearances for this avatar - AvatarAppearancePacket appearance = BuildAppearancePacket(otherAgent); - udp.SendPacket(agent.ID, appearance, PacketCategory.State); - } - } - ); - - // Send terrain data - SendLayerData(agent); - } - void LoadTerrain(string mapFile) { byte[] rgbValues = new byte[256 * 256 * 3]; @@ -1384,18 +1346,6 @@ namespace Simian } } - void SendLayerData(Agent agent) - { - for (int y = 0; y < 16; y++) - { - for (int x = 0; x < 16; x++) - { - LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap[y, x].Height, x, y); - udp.SendPacket(agent.ID, layer, PacketCategory.Terrain); - } - } - } - void SendObjectPacket(SimulationObject obj, bool canUseCompressed, bool canUseImproved, PrimFlags creatorFlags, UpdateFlags updateFlags) { if (!canUseImproved && !canUseCompressed) @@ -1777,27 +1727,5 @@ namespace Simian #endregion ImprovedTerseObjectUpdate } } - - static AvatarAppearancePacket BuildAppearancePacket(Agent agent) - { - AvatarAppearancePacket appearance = new AvatarAppearancePacket(); - appearance.ObjectData.TextureEntry = agent.Avatar.Prim.Textures.GetBytes(); - appearance.Sender.ID = agent.ID; - appearance.Sender.IsTrial = false; - - int count = agent.Info.VisualParams != null ? agent.Info.VisualParams.Length : 0; - - appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[count]; - for (int i = 0; i < count; i++) - { - appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock(); - appearance.VisualParam[i].ParamValue = agent.Info.VisualParams[i]; - } - - if (count != 218) - Logger.Log("Built an odd appearance packet with VisualParams.Length=" + count, Helpers.LogLevel.Warning); - - return appearance; - } } } diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs index ce46dbaf..535439f6 100644 --- a/Programs/Simian/Interfaces/ISceneProvider.cs +++ b/Programs/Simian/Interfaces/ISceneProvider.cs @@ -133,6 +133,7 @@ namespace Simian ITaskInventoryProvider TaskInventory { get; } IUDPProvider UDP { get; } + X509Certificate2 RegionCertificate { get; } uint RegionX { get; set; } uint RegionY { get; set; } ulong RegionHandle { get; } @@ -198,5 +199,7 @@ namespace Simian bool EnableClientCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); bool EnableClientCompleteCapHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state); + + void InformClientOfNeighbors(Agent agent); } } diff --git a/Programs/Simian/SceneExtensions/ConnectionManagement.cs b/Programs/Simian/SceneExtensions/ConnectionManagement.cs index 5da484fc..476971db 100644 --- a/Programs/Simian/SceneExtensions/ConnectionManagement.cs +++ b/Programs/Simian/SceneExtensions/ConnectionManagement.cs @@ -20,6 +20,8 @@ namespace Simian scene.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, UseCircuitCodeHandler); scene.UDP.RegisterPacketCallback(PacketType.StartPingCheck, StartPingCheckHandler); scene.UDP.RegisterPacketCallback(PacketType.LogoutRequest, LogoutRequestHandler); + scene.UDP.RegisterPacketCallback(PacketType.AgentThrottle, AgentThrottleHandler); + scene.UDP.RegisterPacketCallback(PacketType.RegionHandshakeReply, RegionHandshakeReplyHandler); return true; } @@ -83,5 +85,70 @@ namespace Simian scene.ObjectRemove(this, agent.ID); } + + void AgentThrottleHandler(Packet packet, Agent agent) + { + AgentThrottlePacket throttle = (AgentThrottlePacket)packet; + + // TODO: These need to be transmitted to neighbor sims before child agent connections can be established + //throttle.Throttle.Throttles + + // Initiate the connection process for this agent to neighboring regions + scene.InformClientOfNeighbors(agent); + } + + void RegionHandshakeReplyHandler(Packet packet, Agent agent) + { + // Send updates and appearances for every avatar to this new avatar + SynchronizeStateTo(agent); + } + + // HACK: The reduction provider will deprecate this at some point + void SynchronizeStateTo(Agent agent) + { + // Send the parcel overlay + scene.Parcels.SendParcelOverlay(agent); + + // Send object updates for objects and avatars + scene.ForEachObject(delegate(SimulationObject obj) + { + ObjectUpdatePacket update = new ObjectUpdatePacket(); + update.RegionData.RegionHandle = scene.RegionHandle; + update.RegionData.TimeDilation = (ushort)(scene.Physics.TimeDilation * (float)UInt16.MaxValue); + update.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; + update.ObjectData[0] = SimulationObject.BuildUpdateBlock(obj.Prim, obj.Prim.Flags, obj.CRC); + + scene.UDP.SendPacket(agent.ID, update, PacketCategory.State); + }); + + // Send appearances for all avatars + scene.ForEachAgent( + delegate(Agent otherAgent) + { + if (otherAgent != agent) + { + // Send appearances for this avatar + AvatarAppearancePacket appearance = otherAgent.BuildAppearancePacket(); + scene.UDP.SendPacket(agent.ID, appearance, PacketCategory.State); + } + } + ); + + // Send terrain data + SendLayerData(agent); + } + + void SendLayerData(Agent agent) + { + for (int y = 0; y < 16; y++) + { + for (int x = 0; x < 16; x++) + { + float[,] heightmap = scene.GetTerrainPatch((uint)x, (uint)y); + LayerDataPacket layer = TerrainCompressor.CreateLandPacket(heightmap, x, y); + scene.UDP.SendPacket(agent.ID, layer, PacketCategory.Terrain); + } + } + } } } diff --git a/Programs/Simian/SceneExtensions/LLMap.cs b/Programs/Simian/SceneExtensions/LLMap.cs index 50fe44ac..ce838209 100644 --- a/Programs/Simian/SceneExtensions/LLMap.cs +++ b/Programs/Simian/SceneExtensions/LLMap.cs @@ -21,6 +21,9 @@ namespace Simian public class LLMap : IExtension { + static readonly UUID WATER_TEXTURE = new UUID("af588c7c-52b0-4d9e-a888-1fe9d6c35f45"); + static readonly UUID HYPERGRID_MAP_TEXTURE = new UUID("3f1f56ad-7811-42e6-b3c1-98b79fc5c360"); + ISceneProvider scene; public LLMap() @@ -33,6 +36,7 @@ namespace Simian scene.UDP.RegisterPacketCallback(PacketType.MapLayerRequest, MapLayerRequestHandler); scene.UDP.RegisterPacketCallback(PacketType.MapBlockRequest, MapBlockRequestHandler); + scene.UDP.RegisterPacketCallback(PacketType.MapItemRequest, MapItemRequestHandler); scene.UDP.RegisterPacketCallback(PacketType.TeleportRequest, TeleportRequestHandler); scene.UDP.RegisterPacketCallback(PacketType.TeleportLocationRequest, TeleportLocationRequestHandler); return true; @@ -56,7 +60,7 @@ namespace Simian reply.LayerData[0].Left = 0; reply.LayerData[0].Top = UInt16.MaxValue; reply.LayerData[0].Right = UInt16.MaxValue; - reply.LayerData[0].ImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f"); + reply.LayerData[0].ImageID = WATER_TEXTURE; scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction); } @@ -64,7 +68,10 @@ namespace Simian void MapBlockRequestHandler(Packet packet, Agent agent) { MapBlockRequestPacket request = (MapBlockRequestPacket)packet; - GridLayerType type = (GridLayerType)request.AgentData.Flags; + bool returnNonexistent = (request.AgentData.Flags == 0x10000); + GridLayerType type = (GridLayerType)(request.AgentData.Flags &~0x10000); + + // FIXME: Use returnNonexistent MapBlockReplyPacket reply = new MapBlockReplyPacket(); reply.AgentData.AgentID = agent.ID; @@ -85,7 +92,7 @@ namespace Simian reply.Data[1] = new MapBlockReplyPacket.DataBlock(); reply.Data[1].Access = (byte)SimAccess.Min; reply.Data[1].Agents = 0; - reply.Data[1].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f"); + reply.Data[1].MapImageID = HYPERGRID_MAP_TEXTURE; reply.Data[1].Name = Utils.StringToBytes("HyperGrid Portal to OSGrid"); reply.Data[1].RegionFlags = (uint)scene.RegionFlags; reply.Data[1].WaterHeight = (byte)scene.WaterHeight; @@ -95,6 +102,36 @@ namespace Simian scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction); } + void MapItemRequestHandler(Packet packet, Agent agent) + { + MapItemRequestPacket request = (MapItemRequestPacket)packet; + + GridLayerType layerType = (GridLayerType)request.AgentData.Flags; + GridItemType itemType = (GridItemType)request.RequestData.ItemType; + + uint regionX, regionY; + Utils.LongToUInts(request.RequestData.RegionHandle, out regionX, out regionY); + + RegionInfo regionInfo; + if (scene.Server.Grid.TryGetRegion(regionX, regionY, scene.RegionCertificate, out regionInfo)) + { + Logger.Log("MapItemRequest for " + itemType + " from layer " + layerType + " in " + regionInfo.Name, Helpers.LogLevel.Info); + + MapItemReplyPacket reply = new MapItemReplyPacket(); + reply.AgentData.AgentID = agent.ID; + reply.AgentData.Flags = request.AgentData.Flags; + reply.RequestData.ItemType = (uint)itemType; + reply.Data = new MapItemReplyPacket.DataBlock[0]; + + scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction); + } + else + { + Logger.Log("MapItemRequest for " + itemType + " from layer " + layerType + " in unknown region at " + regionX + "," + regionY, + Helpers.LogLevel.Warning); + } + } + void TeleportRequestHandler(Packet packet, Agent agent) { TeleportRequestPacket request = (TeleportRequestPacket)packet; diff --git a/bin/SimianData/DefaultAssets/hypergrid-3f1f56ad-7811-42e6-b3c1-98b79fc5c360.jp2 b/bin/SimianData/DefaultAssets/hypergrid-3f1f56ad-7811-42e6-b3c1-98b79fc5c360.jp2 new file mode 100644 index 00000000..c44cc875 Binary files /dev/null and b/bin/SimianData/DefaultAssets/hypergrid-3f1f56ad-7811-42e6-b3c1-98b79fc5c360.jp2 differ diff --git a/bin/SimianData/DefaultAssets/water-af588c7c-52b0-4d9e-a888-1fe9d6c35f45.jp2 b/bin/SimianData/DefaultAssets/water-af588c7c-52b0-4d9e-a888-1fe9d6c35f45.jp2 new file mode 100644 index 00000000..198aa8f0 Binary files /dev/null and b/bin/SimianData/DefaultAssets/water-af588c7c-52b0-4d9e-a888-1fe9d6c35f45.jp2 differ