aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs3
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs187
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs12
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs66
4 files changed, 169 insertions, 99 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index e9e2dca..9dd6663 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -202,6 +202,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
202 m_stopPacket = TexturePacketCount(); 202 m_stopPacket = TexturePacketCount();
203 } 203 }
204 204
205 //Give them at least two packets, to play nice with some broken viewers (SL also behaves this way)
206 if (m_stopPacket == 1 && Layers[0].End > FIRST_PACKET_SIZE) m_stopPacket++;
207
205 m_currentPacket = StartPacket; 208 m_currentPacket = StartPacket;
206 } 209 }
207 } 210 }
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 540fccc..9f29420 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -339,6 +339,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
339 private AgentUpdateArgs lastarg; 339 private AgentUpdateArgs lastarg;
340 private bool m_IsActive = true; 340 private bool m_IsActive = true;
341 private bool m_IsLoggingOut = false; 341 private bool m_IsLoggingOut = false;
342 private bool m_IsPresenceReady = false;
342 343
343 protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>(); 344 protected Dictionary<PacketType, PacketProcessor> m_packetHandlers = new Dictionary<PacketType, PacketProcessor>();
344 protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers 345 protected Dictionary<string, GenericMessage> m_genericPacketHandlers = new Dictionary<string, GenericMessage>(); //PauPaw:Local Generic Message handlers
@@ -361,6 +362,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
361 362
362 private Timer m_propertiesPacketTimer; 363 private Timer m_propertiesPacketTimer;
363 private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>(); 364 private List<ObjectPropertiesPacket.ObjectDataBlock> m_propertiesBlocks = new List<ObjectPropertiesPacket.ObjectDataBlock>();
365 private List<Packet> m_pendingPackets;
364 366
365 #endregion Class Members 367 #endregion Class Members
366 368
@@ -401,6 +403,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
401 get { return m_IsActive; } 403 get { return m_IsActive; }
402 set { m_IsActive = value; } 404 set { m_IsActive = value; }
403 } 405 }
406
404 public bool IsLoggingOut 407 public bool IsLoggingOut
405 { 408 {
406 get { return m_IsLoggingOut; } 409 get { return m_IsLoggingOut; }
@@ -464,18 +467,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
464 467
465 #region Client Methods 468 #region Client Methods
466 469
470
467 /// <summary> 471 /// <summary>
468 /// Shut down the client view 472 /// Shut down the client view
469 /// </summary> 473 /// </summary>
470 public void Close() 474 public void Close()
471 { 475 {
476 Close(true);
477 }
478
479 /// <summary>
480 /// Shut down the client view
481 /// </summary>
482 public void Close(bool sendStop)
483 {
472 m_log.DebugFormat( 484 m_log.DebugFormat(
473 "[CLIENT]: Close has been called for {0} attached to scene {1}", 485 "[CLIENT]: Close has been called for {0} attached to scene {1}",
474 Name, m_scene.RegionInfo.RegionName); 486 Name, m_scene.RegionInfo.RegionName);
475 487
476 // Send the STOP packet 488 if (sendStop)
477 DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); 489 {
478 OutPacket(disable, ThrottleOutPacketType.Unknown); 490 // Send the STOP packet
491 DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator);
492 OutPacket(disable, ThrottleOutPacketType.Unknown);
493 }
479 494
480 IsActive = false; 495 IsActive = false;
481 496
@@ -1041,6 +1056,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1041 public virtual void SendLayerData(float[] map) 1056 public virtual void SendLayerData(float[] map)
1042 { 1057 {
1043 Util.FireAndForget(DoSendLayerData, map); 1058 Util.FireAndForget(DoSendLayerData, map);
1059
1060 // Send it sync, and async. It's not that much data
1061 // and it improves user experience just so much!
1062 DoSendLayerData(map);
1044 } 1063 }
1045 1064
1046 /// <summary> 1065 /// <summary>
@@ -1053,16 +1072,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1053 1072
1054 try 1073 try
1055 { 1074 {
1056 //for (int y = 0; y < 16; y++) 1075 for (int y = 0; y < 16; y++)
1057 //{ 1076 {
1058 // for (int x = 0; x < 16; x++) 1077 for (int x = 0; x < 16; x+=4)
1059 // { 1078 {
1060 // SendLayerData(x, y, map); 1079 SendLayerPacket(x, y, map);
1061 // } 1080 }
1062 //} 1081 }
1063
1064 // Send LayerData in a spiral pattern. Fun!
1065 SendLayerTopRight(map, 0, 0, 15, 15);
1066 } 1082 }
1067 catch (Exception e) 1083 catch (Exception e)
1068 { 1084 {
@@ -1070,51 +1086,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1070 } 1086 }
1071 } 1087 }
1072 1088
1073 private void SendLayerTopRight(float[] map, int x1, int y1, int x2, int y2)
1074 {
1075 // Row
1076 for (int i = x1; i <= x2; i++)
1077 SendLayerData(i, y1, map);
1078
1079 // Column
1080 for (int j = y1 + 1; j <= y2; j++)
1081 SendLayerData(x2, j, map);
1082
1083 if (x2 - x1 > 0)
1084 SendLayerBottomLeft(map, x1, y1 + 1, x2 - 1, y2);
1085 }
1086
1087 void SendLayerBottomLeft(float[] map, int x1, int y1, int x2, int y2)
1088 {
1089 // Row in reverse
1090 for (int i = x2; i >= x1; i--)
1091 SendLayerData(i, y2, map);
1092
1093 // Column in reverse
1094 for (int j = y2 - 1; j >= y1; j--)
1095 SendLayerData(x1, j, map);
1096
1097 if (x2 - x1 > 0)
1098 SendLayerTopRight(map, x1 + 1, y1, x2, y2 - 1);
1099 }
1100
1101 /// <summary> 1089 /// <summary>
1102 /// Sends a set of four patches (x, x+1, ..., x+3) to the client 1090 /// Sends a set of four patches (x, x+1, ..., x+3) to the client
1103 /// </summary> 1091 /// </summary>
1104 /// <param name="map">heightmap</param> 1092 /// <param name="map">heightmap</param>
1105 /// <param name="px">X coordinate for patches 0..12</param> 1093 /// <param name="px">X coordinate for patches 0..12</param>
1106 /// <param name="py">Y coordinate for patches 0..15</param> 1094 /// <param name="py">Y coordinate for patches 0..15</param>
1107 // private void SendLayerPacket(float[] map, int y, int x) 1095 private void SendLayerPacket(int x, int y, float[] map)
1108 // { 1096 {
1109 // int[] patches = new int[4]; 1097 int[] patches = new int[4];
1110 // patches[0] = x + 0 + y * 16; 1098 patches[0] = x + 0 + y * 16;
1111 // patches[1] = x + 1 + y * 16; 1099 patches[1] = x + 1 + y * 16;
1112 // patches[2] = x + 2 + y * 16; 1100 patches[2] = x + 2 + y * 16;
1113 // patches[3] = x + 3 + y * 16; 1101 patches[3] = x + 3 + y * 16;
1114 1102
1115 // Packet layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches); 1103 float[] heightmap = (map.Length == 65536) ?
1116 // OutPacket(layerpack, ThrottleOutPacketType.Land); 1104 map :
1117 // } 1105 LLHeightFieldMoronize(map);
1106
1107 try
1108 {
1109 Packet layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1110 OutPacket(layerpack, ThrottleOutPacketType.Land);
1111 }
1112 catch
1113 {
1114 for (int px = x ; px < x + 4 ; px++)
1115 SendLayerData(px, y, map);
1116 }
1117 }
1118 1118
1119 /// <summary> 1119 /// <summary>
1120 /// Sends a specified patch to a client 1120 /// Sends a specified patch to a client
@@ -1134,7 +1134,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1134 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); 1134 LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches);
1135 layerpack.Header.Reliable = true; 1135 layerpack.Header.Reliable = true;
1136 1136
1137 OutPacket(layerpack, ThrottleOutPacketType.Land); 1137 OutPacket(layerpack, ThrottleOutPacketType.Task);
1138 } 1138 }
1139 catch (Exception e) 1139 catch (Exception e)
1140 { 1140 {
@@ -2206,6 +2206,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2206 OutPacket(sound, ThrottleOutPacketType.Task); 2206 OutPacket(sound, ThrottleOutPacketType.Task);
2207 } 2207 }
2208 2208
2209 public void SendTransferAbort(TransferRequestPacket transferRequest)
2210 {
2211 TransferAbortPacket abort = (TransferAbortPacket)PacketPool.Instance.GetPacket(PacketType.TransferAbort);
2212 abort.TransferInfo.TransferID = transferRequest.TransferInfo.TransferID;
2213 abort.TransferInfo.ChannelType = transferRequest.TransferInfo.ChannelType;
2214 m_log.Debug("[Assets] Aborting transfer; asset request failed");
2215 OutPacket(abort, ThrottleOutPacketType.Task);
2216 }
2217
2209 public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain) 2218 public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain)
2210 { 2219 {
2211 SoundTriggerPacket sound = (SoundTriggerPacket)PacketPool.Instance.GetPacket(PacketType.SoundTrigger); 2220 SoundTriggerPacket sound = (SoundTriggerPacket)PacketPool.Instance.GetPacket(PacketType.SoundTrigger);
@@ -3933,6 +3942,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3933 { 3942 {
3934 m_propertiesPacketTimer.Stop(); 3943 m_propertiesPacketTimer.Stop();
3935 3944
3945 if (m_propertiesBlocks.Count == 0)
3946 return;
3947
3936 proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count]; 3948 proper.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[m_propertiesBlocks.Count];
3937 3949
3938 int index = 0; 3950 int index = 0;
@@ -4924,6 +4936,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4924 (x.CameraLeftAxis != lastarg.CameraLeftAxis) || 4936 (x.CameraLeftAxis != lastarg.CameraLeftAxis) ||
4925 (x.CameraUpAxis != lastarg.CameraUpAxis) || 4937 (x.CameraUpAxis != lastarg.CameraUpAxis) ||
4926 (x.ControlFlags != lastarg.ControlFlags) || 4938 (x.ControlFlags != lastarg.ControlFlags) ||
4939 (x.ControlFlags != 0) ||
4927 (x.Far != lastarg.Far) || 4940 (x.Far != lastarg.Far) ||
4928 (x.Flags != lastarg.Flags) || 4941 (x.Flags != lastarg.Flags) ||
4929 (x.State != lastarg.State) || 4942 (x.State != lastarg.State) ||
@@ -5295,7 +5308,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5295 args.Channel = ch; 5308 args.Channel = ch;
5296 args.From = String.Empty; 5309 args.From = String.Empty;
5297 args.Message = Utils.BytesToString(msg); 5310 args.Message = Utils.BytesToString(msg);
5298 args.Type = ChatTypeEnum.Shout; 5311 args.Type = ChatTypeEnum.Region; //Behaviour in SL is that the response can be heard from any distance
5299 args.Position = new Vector3(); 5312 args.Position = new Vector3();
5300 args.Scene = Scene; 5313 args.Scene = Scene;
5301 args.Sender = this; 5314 args.Sender = this;
@@ -11176,18 +11189,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11176 } 11189 }
11177 11190
11178 /// <summary> 11191 /// <summary>
11192 /// This processes packets which have accumulated while the presence was still in the process of initialising.
11193 /// </summary>
11194 public void ProcessPendingPackets()
11195 {
11196 m_IsPresenceReady = true;
11197 if (m_pendingPackets == null)
11198 return;
11199 foreach (Packet p in m_pendingPackets)
11200 {
11201 ProcessInPacket(p);
11202 }
11203 m_pendingPackets.Clear();
11204 }
11205
11206 /// <summary>
11179 /// Entryway from the client to the simulator. All UDP packets from the client will end up here 11207 /// Entryway from the client to the simulator. All UDP packets from the client will end up here
11180 /// </summary> 11208 /// </summary>
11181 /// <param name="Pack">OpenMetaverse.packet</param> 11209 /// <param name="Pack">OpenMetaverse.packet</param>
11182 public void ProcessInPacket(Packet Pack) 11210 public void ProcessInPacket(Packet Pack)
11183 { 11211 {
11184 if (m_debugPacketLevel >= 255) 11212 if (!m_IsPresenceReady)
11185 m_log.DebugFormat("[CLIENT]: Packet IN {0}", Pack.Type); 11213 {
11214 if (m_pendingPackets == null)
11215 {
11216 m_pendingPackets = new List<Packet>();
11217 }
11218 m_pendingPackets.Add(Pack);
11219 }
11220 else
11221 {
11222 if (m_debugPacketLevel >= 255)
11223 m_log.DebugFormat("[CLIENT]: Packet IN {0}", Pack.Type);
11186 11224
11187 if (!ProcessPacketMethod(Pack)) 11225 if (!ProcessPacketMethod(Pack))
11188 m_log.Warn("[CLIENT]: unhandled packet " + Pack.Type); 11226 m_log.Warn("[CLIENT]: unhandled packet " + Pack.Type);
11189 11227
11190 PacketPool.Instance.ReturnPacket(Pack); 11228 PacketPool.Instance.ReturnPacket(Pack);
11229 }
11191 } 11230 }
11192 11231
11193 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) 11232 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
@@ -11424,7 +11463,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11424 11463
11425// m_log.DebugFormat("[CLIENT]: {0} requesting asset {1}", Name, requestID); 11464// m_log.DebugFormat("[CLIENT]: {0} requesting asset {1}", Name, requestID);
11426 11465
11466
11467 //Note, the bool returned from the below function is useless since it is always false.
11427 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived); 11468 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived);
11469
11428 } 11470 }
11429 11471
11430 /// <summary> 11472 /// <summary>
@@ -11454,6 +11496,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
11454 //m_log.Debug("asset request " + requestID); 11496 //m_log.Debug("asset request " + requestID);
11455 } 11497 }
11456 11498
11499 if (null == asset)
11500 {
11501 if ((m_hyperAssets != null) && (transferRequest.TransferInfo.SourceType < 2000))
11502 {
11503 // Try the user's inventory, but only if it's different from the regions'
11504 string userAssets = m_hyperAssets.GetUserAssetServer(AgentId);
11505 if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer()))
11506 {
11507 m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id);
11508 if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset)
11509 transferRequest.TransferInfo.SourceType = 2222; // marker
11510 else if (transferRequest.TransferInfo.SourceType == (int)SourceType.SimInventoryItem)
11511 transferRequest.TransferInfo.SourceType = 3333; // marker
11512
11513 m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived);
11514 return;
11515 }
11516 }
11517
11518 //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID);
11519
11520 //We need to send a TransferAbort here, so the client doesn't wait forever for the asset,
11521 //which causes it to not request any more for a while. Which is bad.
11522 SendTransferAbort(transferRequest);
11523 return;
11524 }
11525
11457 // Scripts cannot be retrieved by direct request 11526 // Scripts cannot be retrieved by direct request
11458 if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset && asset.Type == 10) 11527 if (transferRequest.TransferInfo.SourceType == (int)SourceType.Asset && asset.Type == 10)
11459 return; 11528 return;
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index 1b81105..f2bcc0b 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -899,6 +899,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
899 client.OnLogout += LogoutHandler; 899 client.OnLogout += LogoutHandler;
900 900
901 // Start the IClientAPI 901 // Start the IClientAPI
902 // Spin it off so that it doesn't clog up the LLUDPServer
903 //Util.FireAndForget(delegate(object o) { client.Start(); });
904
905 // NOTE: DO NOT CALL THIS ASYNCHRONOUSLY!!!!!
906 // This method will ultimately cause the modules to hook
907 // client events in OnNewClient. If they can't do this
908 // before further packets are processed, packets WILL BE LOST.
909 // This includes the all-important EconomyDataRequest!
910 // So using FireAndForget here WILL screw up money. Badly.
911 // You have been warned!
902 client.Start(); 912 client.Start();
903 } 913 }
904 else 914 else
@@ -915,7 +925,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
915 if (m_scene.TryGetClient(udpClient.AgentID, out client)) 925 if (m_scene.TryGetClient(udpClient.AgentID, out client))
916 { 926 {
917 client.IsLoggingOut = true; 927 client.IsLoggingOut = true;
918 client.Close(); 928 client.Close(false);
919 } 929 }
920 } 930 }
921 931
diff --git a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
index bdbd284..91e3d20 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/TokenBucket.cs
@@ -133,7 +133,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
133 this.parent = parent; 133 this.parent = parent;
134 MaxBurst = maxBurst; 134 MaxBurst = maxBurst;
135 DripRate = dripRate; 135 DripRate = dripRate;
136 lastDrip = Environment.TickCount & Int32.MaxValue; 136 lastDrip = Environment.TickCount;
137 } 137 }
138 138
139 /// <summary> 139 /// <summary>
@@ -144,40 +144,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
144 /// the bucket, otherwise false</returns> 144 /// the bucket, otherwise false</returns>
145 public bool RemoveTokens(int amount) 145 public bool RemoveTokens(int amount)
146 { 146 {
147 bool dummy;
148 return RemoveTokens(amount, out dummy);
149 }
150
151 /// <summary>
152 /// Remove a given number of tokens from the bucket
153 /// </summary>
154 /// <param name="amount">Number of tokens to remove from the bucket</param>
155 /// <param name="dripSucceeded">True if tokens were added to the bucket
156 /// during this call, otherwise false</param>
157 /// <returns>True if the requested number of tokens were removed from
158 /// the bucket, otherwise false</returns>
159 public bool RemoveTokens(int amount, out bool dripSucceeded)
160 {
161 if (maxBurst == 0) 147 if (maxBurst == 0)
162 { 148 {
163 dripSucceeded = true;
164 return true; 149 return true;
165 } 150 }
166 151
167 dripSucceeded = Drip(); 152 if (amount > maxBurst)
168
169 if (content - amount >= 0)
170 { 153 {
171 if (parent != null && !parent.RemoveTokens(amount)) 154 throw new Exception("amount " + amount + " exceeds maxBurst " + maxBurst);
172 return false; 155 }
173 156
174 content -= amount; 157 Drip();
175 return true; 158
159 if (content < amount)
160 {
161 return false;
176 } 162 }
177 else 163
164 if (parent != null && !parent.RemoveTokens(amount))
178 { 165 {
179 return false; 166 return false;
180 } 167 }
168
169 content -= amount;
170 return true;
181 } 171 }
182 172
183 /// <summary> 173 /// <summary>
@@ -193,25 +183,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
193 content = maxBurst; 183 content = maxBurst;
194 return true; 184 return true;
195 } 185 }
196 else
197 {
198 int now = Environment.TickCount & Int32.MaxValue;
199 int deltaMS = now - lastDrip;
200 186
201 if (deltaMS <= 0) 187 int now = Environment.TickCount;
202 { 188 int deltaMS = now - lastDrip;
203 if (deltaMS < 0) 189 lastDrip = now;
204 lastDrip = now;
205 return false;
206 }
207 190
208 int dripAmount = deltaMS * tokensPerMS; 191 if (deltaMS <= 0)
209 192 {
210 content = Math.Min(content + dripAmount, maxBurst); 193 return false;
211 lastDrip = now; 194 }
212 195
213 return true; 196 long dripAmount = (long)deltaMS * (long)tokensPerMS + (long)content;
197 if (dripAmount > maxBurst)
198 {
199 dripAmount = maxBurst;
214 } 200 }
201 content = (int)dripAmount;
202 return true;
215 } 203 }
216 } 204 }
217} 205}