diff --git a/.hgtags b/.hgtags index 965770d48318d44c141f869c0eca54a7ce6f7d9d..7e1c0d2a0b02bc1bb2e50ef4073441c46a5498ed 100755 --- a/.hgtags +++ b/.hgtags @@ -72,35 +72,35 @@ b53a0576eec80614d7767ed72b40ed67aeff27c9 DRTVWR-38_2.5.2-release 461c8c65b5c799ddfe365422f9be9c0095d91e7d 2.6.0-beta1-tip 9e4641f4a7870c0f565a25a2971368d5a29516a1 2.6.0-beta2 9e4641f4a7870c0f565a25a2971368d5a29516a1 DRTVWR-41_2.6.0-beta2 +42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release +42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-beta1 c5bdef3aaa2744626aef3c217ce29e1900d357b3 2.6.1-start c5bdef3aaa2744626aef3c217ce29e1900d357b3 DRTVWR-43_2.6.1-beta1 +c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release +c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release 56b2778c743c2a964d82e1caf11084d76a87de2c 2.6.2-start d1203046bb653b763f835b04d184646949d8dd5c 2.6.2-beta1 d1203046bb653b763f835b04d184646949d8dd5c DRTVWR-45_2.6.2-beta1 -42f32494bac475d0737799346f6831558ae8bf5d 2.6.0-release -42f32494bac475d0737799346f6831558ae8bf5d DRTVWR-39_2.6.0-release -c9182ed77d427c759cfacf49a7b71a2e20d522aa 2.6.1-release -c9182ed77d427c759cfacf49a7b71a2e20d522aa DRTVWR-42_2.6.1-release +214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release +214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release 52b2263ab28f0976c689fd0b76c55a9eb027cdbf end-of-develop.py ec32f1045e7c2644015245df3a9933620aa194b8 2.6.3-start d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc 2.6.3-beta1 d7fcefabdf32bb61a9ea6d6037c1bb26190a85bc DRTVWR-47_2.6.3-beta1 0630e977504af5ea320c58d33cae4e1ddee793e9 2.6.3-beta2 0630e977504af5ea320c58d33cae4e1ddee793e9 DRTVWR-48_2.6.3-beta2 +8f2da1701c81a62352df2b8d413d27fb2cade9a6 2.6.3-release +8f2da1701c81a62352df2b8d413d27fb2cade9a6 DRTVWR-46_2.6.3-release 3178e311da3a8739a85363665006ea3c4610cad4 dons-headless-hackathon-work -214180ad5714ce8392b82bbebcc92f4babd98300 2.6.2-release -214180ad5714ce8392b82bbebcc92f4babd98300 DRTVWR-44_2.6.2-release 7db558aaa7c176f2022b3e9cfe38ac72f6d1fccd 2.6.5-beta1 7db558aaa7c176f2022b3e9cfe38ac72f6d1fccd DRTVWR-50_2.6.5-beta1 -8f2da1701c81a62352df2b8d413d27fb2cade9a6 2.6.3-release -8f2da1701c81a62352df2b8d413d27fb2cade9a6 DRTVWR-46_2.6.3-release 800cefce8d364ffdd2f383cbecb91294da3ea424 2.6.6-start bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 2.6.6-beta1 bb1075286b3b147b1dae2e3d6b2d56f04ff03f35 DRTVWR-52_2.6.6-beta1 -5e349dbe9cc84ea5795af8aeb6d473a0af9d4953 2.6.8-start dac76a711da5f1489a01c1fa62ec97d99c25736d 2.6.6-release dac76a711da5f1489a01c1fa62ec97d99c25736d DRTVWR-51_2.6.6-release +5e349dbe9cc84ea5795af8aeb6d473a0af9d4953 2.6.8-start beafa8a9bd1d1b670b7523d865204dc4a4b38eef 2.6.8-beta1 beafa8a9bd1d1b670b7523d865204dc4a4b38eef DRTVWR-55_2.6.8-beta1 be2000b946f8cb3de5f44b2d419287d4c48ec4eb 2.6.8-release @@ -119,50 +119,50 @@ e67da2c6e3125966dd49eef98b36317afac1fcfe 2.6.9-start 9f79a6ed8fdcd2f3dac33ea6b3236eeb278dccfe 2.7.2-start e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb 2.7.2-beta1 e0dc8b741eaa27dcdfbc9e956bb2579b954d15eb DRTVWR-63_2.7.2-beta1 -6a3e7e403bd19e45fdfc2fcc716867af3ab80861 2.7.3-start fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.1-release fe3a8e7973072ea62043c08b19b66626c1a720eb 2.7.2-release fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-60_2.7.1-release fe3a8e7973072ea62043c08b19b66626c1a720eb DRTVWR-62_2.7.2-release +6a3e7e403bd19e45fdfc2fcc716867af3ab80861 2.7.3-start 6af10678de4736222b2c3f7e010e984fb5b327de 2.7.4-start be963a4eef635542f9617d7f5fd22ba48fb71958 2.7.4-beta1 be963a4eef635542f9617d7f5fd22ba48fb71958 DRTVWR-67_2.7.4-beta1 +057f319dd8eccdf63a54d99686c68cdcb31b6abc 2.7.4-release +057f319dd8eccdf63a54d99686c68cdcb31b6abc DRTVWR-66_2.7.4-release 19a498fa62570f352d7d246f17e3c81cc1d82d8b 2.7.5-start 09984bfa6cae17e0f72d02b75c1b7393c65eecfc 2.7.5-beta1 09984bfa6cae17e0f72d02b75c1b7393c65eecfc DRTVWR-69_2.7.5-beta1 +6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release +6866d9df6efbd441c66451debd376d21211de39c DRTVWR-68_2.7.5-release e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-beta1 e1ed60913230dd64269a7f7fc52cbc6004f6d52c 2.8.0-start e1ed60913230dd64269a7f7fc52cbc6004f6d52c DRTVWR-71_2.8.0-beta1 -057f319dd8eccdf63a54d99686c68cdcb31b6abc 2.7.4-release -057f319dd8eccdf63a54d99686c68cdcb31b6abc DRTVWR-66_2.7.4-release -6866d9df6efbd441c66451debd376d21211de39c 2.7.5-release -6866d9df6efbd441c66451debd376d21211de39c DRTVWR-68_2.7.5-release +493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release +493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release 502f6a5deca9365ddae57db4f1e30172668e171e 2.8.1-start 2c7e459e0c883f8e406b932e41e60097e9ee077e 2.8.1-beta1 2c7e459e0c883f8e406b932e41e60097e9ee077e DRTVWR-73_2.8.1-beta1 -493d9127ee50e84ba08a736a65a23ca86f7a5b01 2.8.0-release -493d9127ee50e84ba08a736a65a23ca86f7a5b01 DRTVWR-70_2.8.0-release -54bc7823ad4e3a436fef79710f685a7372bbf795 2.8.2-start -ac0f1a132d35c02a58861d37cca75b0429ac9137 2.8.3-start 29e93d7e19991011bd12b5748142b11a5dcb4370 2.8.1-release 29e93d7e19991011bd12b5748142b11a5dcb4370 DRTVWR-72_2.8.1-release 4780e3bd2b3042f91be3426151f28c30d199bb3b 2.8.1-hotfix 4780e3bd2b3042f91be3426151f28c30d199bb3b DRTVWR-76_2.8.1-hotfix +54bc7823ad4e3a436fef79710f685a7372bbf795 2.8.2-start +ac0f1a132d35c02a58861d37cca75b0429ac9137 2.8.3-start 599677276b227357140dda35bea4a2c18e2e67b5 2.8.3-beta1 599677276b227357140dda35bea4a2c18e2e67b5 DRTVWR-75_2.8.3-beta1 +fb85792b84bf28428889c4cc966469d92e5dac4c 2.8.3-release +fb85792b84bf28428889c4cc966469d92e5dac4c DRTVWR-74_2.8.3-release 6b678ea52f90d5c14181661dcd2546e25bde483e 3.0.0-start b0be6ce3adfef3a014a2389d360539f8a86c5439 3.0.0-beta1 b0be6ce3adfef3a014a2389d360539f8a86c5439 DRTVWR-78_3.0.0-beta1 -fb85792b84bf28428889c4cc966469d92e5dac4c 2.8.3-release -fb85792b84bf28428889c4cc966469d92e5dac4c DRTVWR-74_2.8.3-release +1778f26b6d0ae762dec3ca37140f66620f2485d9 3.0.0-release +1778f26b6d0ae762dec3ca37140f66620f2485d9 DRTVWR-77_3.0.0-release 82a2079ffcb57ecb1b3849cb41376b443e1eb912 3.0.1-start 364fd63517fbacbbcb9129d096187171ba8c9e48 3.0.1-beta1 364fd63517fbacbbcb9129d096187171ba8c9e48 DRTVWR-81_3.0.1-beta1 f2412ecd6740803ea9452f1d17fd872e263a0df7 3.0.2-start 42784bf50fa01974bada2a1af3892ee09c93fcda 3.0.2-beta1 42784bf50fa01974bada2a1af3892ee09c93fcda DRTVWR-83_3.0.2-beta1 -1778f26b6d0ae762dec3ca37140f66620f2485d9 3.0.0-release -1778f26b6d0ae762dec3ca37140f66620f2485d9 DRTVWR-77_3.0.0-release e5c9af2d7980a99a71650be3a0cf7b2b3c3b897e 3.0.2-beta2 e5c9af2d7980a99a71650be3a0cf7b2b3c3b897e DRTVWR-86_3.0.2-beta2 b95ddac176ac944efdc85cbee94ac2e1eab44c79 3.0.3-start @@ -170,9 +170,9 @@ b95ddac176ac944efdc85cbee94ac2e1eab44c79 3.0.3-start 6694f3f062aa45f64ab391d25a3eb3d5eb1b0871 DRTVWR-85_3.0.3-beta1 61aa7974df089e8621fe9a4c69bcdefdb3cc208a 3.0.3-beta2 61aa7974df089e8621fe9a4c69bcdefdb3cc208a DRTVWR-89_3.0.3-beta2 -586907287be581817b2422b5137971b22d54ea48 3.0.4-start 0496d2f74043cf4e6058e76ac3db03d44cff42ce 3.0.3-release 0496d2f74043cf4e6058e76ac3db03d44cff42ce DRTVWR-84_3.0.3-release +586907287be581817b2422b5137971b22d54ea48 3.0.4-start 92a3aa04775438226399b19deee12ac3b5a62838 3.0.5-start c7282e59f374ee904bd793c3c444455e3399b0c5 3.1.0-start 2657fa785bbfac115852c41bd0adaff74c2ad5da 3.1.0-beta1 @@ -193,11 +193,11 @@ e440cd1dfbd128d7d5467019e497f7f803640ad6 DRTVWR-95_3.2.0-beta1 c4911ec8cd81e676dfd2af438b3e065407a94a7a 3.2.1-start 9e390d76807fa70d356b8716fb83b8ce42a629ef 3.2.1-beta1 9e390d76807fa70d356b8716fb83b8ce42a629ef DRTVWR-100_3.2.1-beta1 +a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release +a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release 40b46edba007d15d0059c80864b708b99c1da368 3.2.2-start 523df3e67378541498d516d52af4402176a26bac 3.2.2-beta1 523df3e67378541498d516d52af4402176a26bac DRTVWR-102_3.2.2-beta1 -a8c7030d6845186fac7c188be4323a0e887b4184 3.2.1-release -a8c7030d6845186fac7c188be4323a0e887b4184 DRTVWR-99_3.2.1-release 80f3e30d8aa4d8f674a48bd742aaa6d8e9eae0b5 3.2.3-start 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-beta1 3fe994349fae64fc40874bb59db387131eb35a41 3.2.4-start @@ -248,57 +248,67 @@ bb9932a7a5fd00edf52d95f354e3b37ae6a942db DRTVWR-156 6414ecdabc5d89515b08d1f872cf923ed3a5523a DRTVWR-148 2a3965b3ad202df7ea25d2be689291bb14a1280e DRTVWR-155 24a7281bef42bd4430ceb25db8b195449c2c7de3 DRTVWR-153 +a716684aa7c07c440b1de5815b8a1f3dd3fd8bfb DRTVWR-159 +9a78ac13f047056f788c4734dd91aebfe30970e3 DRTVWR-157 5910f8063a7e1ddddf504c2f35ca831cc5e8f469 DRTVWR-160 f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 3.3.3-beta1 f0a174c2adb4bc39b16722a61d7eeb4f2a1d4843 DRTVWR-144 -2d6c0634b11e6f3df11002b8510a72a0433da00a DRTVWR-164 +089e5c84b2dece68f2b016c842ef9b5de4786842 DRTVWR-161 600f3b3920d94de805ac6dc8bb6def9c069dd360 DRTVWR-162 +c08e2ac17a99973b2a94477659220b99b8847ae2 DRTVWR-163 +2d6c0634b11e6f3df11002b8510a72a0433da00a DRTVWR-164 80b5e5e9775966d3839331ffa7a16a60f9d7c930 DRTVWR-165 fdcc08a4f20ae9bb060f4693c8980d216534efdf 3.3.3-beta2 af5f3e43e6e4424b1da19d9e16f6b853a7b822ed DRTVWR-169 4b3c68199a86cabaa5d9466d7b0f7e141e901d7a 3.3.3-beta3 6428242e124b523813bfaf4c45b3d422f0298c81 3.3.3-release -a716684aa7c07c440b1de5815b8a1f3dd3fd8bfb DRTVWR-159 -9a78ac13f047056f788c4734dd91aebfe30970e3 DRTVWR-157 -089e5c84b2dece68f2b016c842ef9b5de4786842 DRTVWR-161 -c08e2ac17a99973b2a94477659220b99b8847ae2 DRTVWR-163 b9d0170b62eb1c7c3adaa37a0b13a833e5e659f9 DRTVWR-171 050e48759337249130f684b4a21080b683f61732 DRTVWR-168 09ef7fd1b0781f33b8a3a9af6236b7bcb4831910 DRTVWR-170 f87bfbe0b62d26f451d02a47c80ebef6b9168fc2 DRTVWR-158 f91d003091a61937a044652c4c674447f7dcbb7a 3.3.4-beta1 +005dfe5c4c377207d065fb27858d2eb0b53b143a DRTVWR-167 bce218b2b45b730b22cc51e4807aa8b571cadef3 DRTVWR-173 cbea6356ce9cb0c313b6777f10c5c14783264fcc DRTVWR-174 82b5330bc8b17d0d4b598832e9c5a92e90075682 3.3.4-beta2 57d221de3df94f90b55204313c2cef044a3c0ae2 DRTVWR-176 eb539c65e6ee26eea2bf373af2d0f4b52dc91289 DRTVWR-177 a8057e1b9a1246b434a27405be35e030f7d28b0c 3.3.4-beta3 +888768f162d2c0a8de1dcc5fb9a08bd8bd120a6b DRTVWR-175 4281aa899fb2cedb7a9ca7ce91c5c29d4aa69594 DRTVWR-180 5c08e1d8edd871807153603b690e3ee9dbb548aa DRTVWR-183 6c75f220b103db1420919c8b635fe53e2177f318 3.3.4-beta4 9cd174d3a54d93d409a7c346a15b8bfb40fc58f4 DRTVWR-184 ab2ffc547c8a8950ff187c4f6c95e5334fab597b 3.3.4-beta5 28e100d0379a2b0710c57647a28fc5239d3d7b99 3.3.4-release -005dfe5c4c377207d065fb27858d2eb0b53b143a DRTVWR-167 -888768f162d2c0a8de1dcc5fb9a08bd8bd120a6b DRTVWR-175 a8b3eca451a9eaab59987efb0ab1c4217e3f2dcc DRTVWR-182 1f27cdfdc54246484f8afbbe42ce48e954175cbd 3.4.0-beta1 -9ee9387789701d597130f879d9011a4958753862 DRTVWR-189 +81f6b745ef27f5915fd07f988fdec9944f2bb73e DRTVWR-186 47f0d08ba7ade0a3905074009067c6d3df7e16ae DRTVWR-190 +cc953f00956be52cc64c30637bbeec310eea603f DRTVWR-181 +c04e68e1b0034fd0a20815ae24c77e5f8428e822 DRTVWR-188 +9ee9387789701d597130f879d9011a4958753862 DRTVWR-189 421126293dcbde918e0da027ca0ab9deb5b4fbf2 DRTVWR-192 +4b2c52aecb7a75de31dbb12d9f5b9a251d8707be DRTVWR-191 33a2fc7a910ae29ff8b4850316ed7fbff9f64d33 DRTVWR-195 e9732c739c8a72a590216951505ea9c76a526a84 DRTVWR-193 +78ca0bbf43a92e8914d4cfa87d69a6717ef7d4cf DRTVWR-194 7602f61c804a512764e349c034c02ddabeefebc4 DRTVWR-196 ae5c83dd61d2d37c45f1d5b8bf2b036d87599f1b DRTVWR-198 507bdfbd6bf844a511c1ffeda4baa80016ed1346 DRTVWR-197 b1dbb1a83f48f93f6f878cff9e52d2cb635e145c 3.4.0-beta2 37402e2b19af970d51b0a814d79892cc5647532b DRTVWR-200 182a9bf30e81070361bb020a78003b1cf398e79c 3.4.0-beta3 +248f4acd92a706c79e842bc83d80baa7369c0c2e DRTVWR-203 6dfb0fba782c9233dd95f24ec48146db0d3f210b DRTVWR-199 7c9102fb998885621919f2474a002c35b583539b 3.3.4-release2 7649a3dff5ec22d3727377e5f02efd0f421e4cb5 DRTVWR-201 84fb70dfe3444e75a44fb4bee43e2fc8221cebdd 3.4.0-beta4 +de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202 573e863be2f26d3687161def4b9fea9b7038dda8 3.4.0-beta5 +34dbbe2b00afe90352d3acf8290eb10ab90d1c8b oz-build-test-tag +6ee71714935ffcd159db3d4f5800c1929aac54e1 DRTVWR-205 +7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206 8c9085066c78ed5f6c9379dc054c82a6fcdb1851 DRTVWR-207 351eea5f9dc192fc5ddea3b02958de97677a0a12 3.3.4-release3 af7b28e75bd5a629cd9e0dc46fb3f1757626f493 DRTVWR-212 @@ -309,6 +319,7 @@ ceed0b65a69f1eac20d523e0203320a32f9a3f3c DRTVWR-215 97977c67245f52db20eb15f1918cc0f24778cabc 3.4.0-release 5adb2b8f96c3cac88ad7c7d996d707f1b29df336 3.4.1-beta1 b3f74858a1c8720c82d0978f3877a3fc8ba459ec 3.4.1-beta1a +b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219 2b779f233ee6f38c89cb921650c773a96e63da92 DRTVWR-220 0b9d95f4bfb6867cbf56eaec51633b0da2f1262d DRTVWR-221 e6e553761829dc0270eaaa712b7cb0622535b076 3.4.1-beta3 @@ -333,16 +344,14 @@ baf97f06ae17223614c5e31aa42e71d87cff07fe DRTVWR-236 b2f21e3442542283a80e7eaebae9f833e5a927b6 DRTVWR-237 3f9be82de642d468c5fc272cb9d96b46b5498402 3.4.1-beta12 e59ffd3fe0838ae6b09b242a6e9df71761b88f41 3.4.1-release -81f6b745ef27f5915fd07f988fdec9944f2bb73e DRTVWR-186 -cc953f00956be52cc64c30637bbeec310eea603f DRTVWR-181 -c04e68e1b0034fd0a20815ae24c77e5f8428e822 DRTVWR-188 -4b2c52aecb7a75de31dbb12d9f5b9a251d8707be DRTVWR-191 -78ca0bbf43a92e8914d4cfa87d69a6717ef7d4cf DRTVWR-194 -248f4acd92a706c79e842bc83d80baa7369c0c2e DRTVWR-203 -de3be913f68813a9bac7d1c671fef96d1159bcd6 DRTVWR-202 -34dbbe2b00afe90352d3acf8290eb10ab90d1c8b oz-build-test-tag -6ee71714935ffcd159db3d4f5800c1929aac54e1 DRTVWR-205 -7b22c612fc756e0ea63b10b163e81d107f85dbf8 DRTVWR-206 -b61afe175b829c149d369524a4e974dfda99facf DRTVWR-219 32896d5e920ca9a29256ff3b747c2e99752aa5ae DRTVWR-217 704bbae7b182a1f2811a47a054e680522966f54a 3.4.2-beta1 +d799593b53ed733862e9a13871e318e886469377 DRTVWR-208 +e497dcde7a3653e384eb223a8a460030e89c294c DRTVWR-223 +288539fc0408ed4b69a99665de33bbbc2c3c08fe DRTVWR-216 +e664473c16df1d82ffaff382e7b3e023da202d52 3.4.2-beta2 +93ab02d83f51e30a3cabad98aff89601befd9413 DRTVWR-240 +0891d7a773a31397dcad48be3fa66531d567a821 DRTVWR-242 +710785535362b3cb801b6a3dc4703be3373bd0cd 3.4.2-beta3 +2aa72e3372a83dece4df9cf72fb1e7c34f90b5e3 DRTVWR-209 +f7bedce18ad52283e6072814db23318907261487 DRTVWR-238 diff --git a/autobuild.xml b/autobuild.xml index 05e0ac28aef6940f09acf2648eeb82d30a8b3111..9a92614dfdbcf00af82bf5a85823e15296ecde46 100644 --- a/autobuild.xml +++ b/autobuild.xml @@ -186,9 +186,9 @@ <key>archive</key> <map> <key>hash</key> - <string>d98078791ce345bf6168ce9ba53ca2d7</string> + <string>36aa500e13cdde61607b6e93065206ec</string> <key>url</key> - <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/222752/arch/Darwin/installer/boost-1.45.0-darwin-20110304.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Darwin/installer/boost-1.48.0-darwin-20120710.tar.bz2</string> </map> <key>name</key> <string>darwin</string> @@ -198,9 +198,9 @@ <key>archive</key> <map> <key>hash</key> - <string>a34e7fffdb94a6a4d8a2966b1f216da3</string> + <string>18602d44bd435eb0d7189f436ff2cb0f</string> <key>url</key> - <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-linux-20110310.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/Linux/installer/boost-1.48.0-linux-20120710.tar.bz2</string> </map> <key>name</key> <string>linux</string> @@ -210,9 +210,9 @@ <key>archive</key> <map> <key>hash</key> - <string>98be22c8833aa2bca184b9fa09fbb82b</string> + <string>dc8f5dc6be04c64bf3460b4932b18457</string> <key>url</key> - <string>http://s3.amazonaws.com/viewer-source-downloads/install_pkgs/boost-1.45.0-windows-20110124.tar.bz2</string> + <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-boost/rev/261457/arch/CYGWIN/installer/boost-1.48.0-windows-20120710.tar.bz2</string> </map> <key>name</key> <string>windows</string> diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 1cebb53a07df035e68d4334f2290a06dea2fc361..24c98bfadaa5e945c2ead2eb88ff6b94ed550bc2 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,6 +43,7 @@ add_subdirectory(cmake) add_subdirectory(${LIBS_OPEN_PREFIX}llaudio) add_subdirectory(${LIBS_OPEN_PREFIX}llcharacter) add_subdirectory(${LIBS_OPEN_PREFIX}llcommon) +add_subdirectory(${LIBS_OPEN_PREFIX}llcorehttp) add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llkdu) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake index 2135f0584c6520a57de8eb6bc07950abfd35b891..2af0bc1b304d0e454d397c06b28e97586997e2f3 100644 --- a/indra/cmake/Boost.cmake +++ b/indra/cmake/Boost.cmake @@ -12,12 +12,13 @@ if (STANDALONE) set(BOOST_SIGNALS_LIBRARY boost_signals-mt) set(BOOST_SYSTEM_LIBRARY boost_system-mt) set(BOOST_FILESYSTEM_LIBRARY boost_filesystem-mt) + set(BOOST_THREAD_LIBRARY boost_thread-mt) else (STANDALONE) use_prebuilt_binary(boost) set(Boost_INCLUDE_DIRS ${LIBS_PREBUILT_DIR}/include) if (WINDOWS) - set(BOOST_VERSION 1_45) + set(BOOST_VERSION 1_48) if(MSVC80) set(BOOST_PROGRAM_OPTIONS_LIBRARY optimized libboost_program_options-vc80-mt-${BOOST_VERSION} @@ -37,22 +38,55 @@ else (STANDALONE) else(MSVC80) # MSVC 10.0 config set(BOOST_PROGRAM_OPTIONS_LIBRARY - optimized libboost_program_options-vc100-mt-${BOOST_VERSION} - debug libboost_program_options-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_program_options-mt + debug libboost_program_options-mt-gd) set(BOOST_REGEX_LIBRARY - optimized libboost_regex-vc100-mt-${BOOST_VERSION} - debug libboost_regex-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_regex-mt + debug libboost_regex-mt-gd) set(BOOST_SYSTEM_LIBRARY - optimized libboost_system-vc100-mt-${BOOST_VERSION} - debug libboost_system-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_system-mt + debug libboost_system-mt-gd) set(BOOST_FILESYSTEM_LIBRARY - optimized libboost_filesystem-vc100-mt-${BOOST_VERSION} - debug libboost_filesystem-vc100-mt-gd-${BOOST_VERSION}) + optimized libboost_filesystem-mt + debug libboost_filesystem-mt-gd) + set(BOOST_THREAD_LIBRARY + optimized libboost_thread-mt + debug libboost_thread-mt-gd) endif (MSVC80) - elseif (DARWIN OR LINUX) - set(BOOST_PROGRAM_OPTIONS_LIBRARY boost_program_options) - set(BOOST_REGEX_LIBRARY boost_regex) - set(BOOST_SYSTEM_LIBRARY boost_system) - set(BOOST_FILESYSTEM_LIBRARY boost_filesystem) + elseif (LINUX) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) + elseif (DARWIN) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_PROGRAM_OPTIONS_LIBRARY + optimized boost_program_options-mt + debug boost_program_options-mt-d) + set(BOOST_REGEX_LIBRARY + optimized boost_regex-mt + debug boost_regex-mt-d) + set(BOOST_SYSTEM_LIBRARY + optimized boost_system-mt + debug boost_system-mt-d) + set(BOOST_FILESYSTEM_LIBRARY + optimized boost_filesystem-mt + debug boost_filesystem-mt-d) + set(BOOST_THREAD_LIBRARY + optimized boost_thread-mt + debug boost_thread-mt-d) endif (WINDOWS) endif (STANDALONE) diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake index 9f05c4cff21c4b6cdffa66232bc2982f8a59c675..a5483ba678e79d0048287f8d966b1b8b46a3ee6f 100644 --- a/indra/cmake/Copy3rdPartyLibs.cmake +++ b/indra/cmake/Copy3rdPartyLibs.cmake @@ -254,6 +254,12 @@ elseif(LINUX) libapr-1.so.0 libaprutil-1.so.0 libatk-1.0.so + libboost_program_options-mt.so.1.48.0 + libboost_regex-mt.so.1.48.0 + libboost_thread-mt.so.1.48.0 + libboost_filesystem-mt.so.1.48.0 + libboost_signals-mt.so.1.48.0 + libboost_system-mt.so.1.48.0 libbreakpad_client.so.0 libcollada14dom.so libcrypto.so.1.0.0 diff --git a/indra/cmake/LLAddBuildTest.cmake b/indra/cmake/LLAddBuildTest.cmake old mode 100755 new mode 100644 diff --git a/indra/cmake/LLCommon.cmake b/indra/cmake/LLCommon.cmake index d4694ad37a0caa461c56efe563dd266b98161c5d..8f7bb296cec0617ae4b7d297eba1ac16c019c441 100644 --- a/indra/cmake/LLCommon.cmake +++ b/indra/cmake/LLCommon.cmake @@ -24,7 +24,7 @@ endif (LINUX) add_definitions(${TCMALLOC_FLAG}) -set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a shared library.") +set(LLCOMMON_LINK_SHARED OFF CACHE BOOL "Build the llcommon target as a static library.") if(LLCOMMON_LINK_SHARED) add_definitions(-DLL_COMMON_LINK_SHARED=1) endif(LLCOMMON_LINK_SHARED) diff --git a/indra/cmake/LLCoreHttp.cmake b/indra/cmake/LLCoreHttp.cmake new file mode 100644 index 0000000000000000000000000000000000000000..61e4b23d986de222dec352c135443a545dfdc873 --- /dev/null +++ b/indra/cmake/LLCoreHttp.cmake @@ -0,0 +1,16 @@ +# -*- cmake -*- + +include(CARes) +include(CURL) +include(OpenSSL) +include(Boost) + +set(LLCOREHTTP_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llcorehttp + ${CARES_INCLUDE_DIRS} + ${CURL_INCLUDE_DIRS} + ${OPENSSL_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIRS} + ) + +set(LLCOREHTTP_LIBRARIES llcorehttp) diff --git a/indra/cmake/LLPrimitive.cmake b/indra/cmake/LLPrimitive.cmake index f15a2c2649781ba9e5c4b2446bc322b0f1b7e0f3..ab39cbb6be88597566dfac2d3c3c256100e44018 100644 --- a/indra/cmake/LLPrimitive.cmake +++ b/indra/cmake/LLPrimitive.cmake @@ -15,10 +15,10 @@ if (WINDOWS) optimized llprimitive debug libcollada14dom22-d optimized libcollada14dom22 - debug libboost_filesystem-vc100-mt-gd-1_45 - optimized libboost_filesystem-vc100-mt-1_45 - debug libboost_system-vc100-mt-gd-1_45 - optimized libboost_system-vc100-mt-1_45 + debug libboost_filesystem-mt-gd + optimized libboost_filesystem-mt + debug libboost_system-mt-gd + optimized libboost_system-mt ) else (WINDOWS) set(LLPRIMITIVE_LIBRARIES diff --git a/indra/lib/python/indra/util/llmanifest.py b/indra/lib/python/indra/util/llmanifest.py index bfcb2597094c8acfbbd547d6855938d667aa4d71..97cc31bba0cd58650644ec03e9b8586ec0ba4734 100644 --- a/indra/lib/python/indra/util/llmanifest.py +++ b/indra/lib/python/indra/util/llmanifest.py @@ -626,6 +626,23 @@ class LLManifest(object): d = src_re.sub(d_template, s.replace('\\', '/')) yield os.path.normpath(s), os.path.normpath(d) + def path2basename(self, path, file): + """ + It is a common idiom to write: + self.path(os.path.join(somedir, somefile), somefile) + + So instead you can write: + self.path2basename(somedir, somefile) + + Note that this is NOT the same as: + self.path(os.path.join(somedir, somefile)) + + which is the same as: + temppath = os.path.join(somedir, somefile) + self.path(temppath, temppath) + """ + return self.path(os.path.join(path, file), file) + def path(self, src, dst=None): sys.stdout.write("Processing %s => %s ... " % (src, dst)) sys.stdout.flush() @@ -671,6 +688,10 @@ class LLManifest(object): print "%d files" % count + # Let caller check whether we processed as many files as expected. In + # particular, let caller notice 0. + return count + def do(self, *actions): self.actions = actions self.construct() diff --git a/indra/llcharacter/lleditingmotion.cpp b/indra/llcharacter/lleditingmotion.cpp index 66b3c2bd25f15196ac8f23e5e88527a175f9143d..0d0b85ba60480ff4a1c61c7f3c4a416bfd3790b4 100644 --- a/indra/llcharacter/lleditingmotion.cpp +++ b/indra/llcharacter/lleditingmotion.cpp @@ -214,8 +214,10 @@ BOOL LLEditingMotion::onUpdate(F32 time, U8* joint_mask) target = target * target_dist; if (!target.isFinite()) { - llerrs << "Non finite target in editing motion with target distance of " << target_dist << + // Don't error out here, set a fail-safe target vector + llwarns << "Non finite target in editing motion with target distance of " << target_dist << " and focus point " << focus_pt << llendl; + target.setVec(1.f, 1.f, 1.f); } mTarget.setPosition( target + mParentJoint.getPosition()); diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index 5cfcdab41cd331c43df806e628e0ace851492b89..f0a5603d06ca537a510f6572f64ff55ddb031e43 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -59,4 +59,8 @@ #include "llerror.h" #include "llfile.h" +// Boost 1.45 had version 2 as the default for the filesystem library, +// 1.48 has version 3 as the default. Keep compatibility for now. +#define BOOST_FILESYSTEM_VERSION 2 + #endif diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index af33ce666f1558193d3d2818cf0c3a5e98d6ee7a..034546c3f39f669e10566b80231cbb83a5596f68 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -168,7 +168,7 @@ public: void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); } void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); } Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++ - Type operator --(int) { return apr_atomic_dec32(&mData); } // Type-- + Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise) private: apr_uint32_t mData; diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 7f4f670ed0fdd85173995095d42e217a32c6b3ef..6b549e4b6f9655116d5fb76fedd6a0aea390ad42 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -1451,9 +1451,12 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option } case LLSD::TypeUUID: + { ostr.put('u'); - ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES); + LLSD::UUID value = data.asUUID(); + ostr.write((const char*)(&value.mData), UUID_BYTES); break; + } case LLSD::TypeString: ostr.put('s'); diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index fa0eb9f72c7a501b92bf26d6c2accc25d9993138..0c326797449792f1d6138abba8e7947c3f4a7e11 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -47,7 +47,8 @@ std::string ll_safe_string(const char* in) std::string ll_safe_string(const char* in, S32 maxlen) { - if(in) return std::string(in, maxlen); + if(in && maxlen > 0 ) return std::string(in, maxlen); + return std::string(); } diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index c2fbb544a88db36e1d7e850d9dd4231c17ef1490..1d56a52c32ee4c1dbaccc8fbcd603e9ef43ae2a7 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -71,6 +71,13 @@ LL_COMMON_API void assert_main_thread() } } +void LLThread::registerThreadID() +{ +#if !LL_DARWIN + sThreadID = ++sIDIter; +#endif +} + // // Handed to the APR thread creation function // diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index 115bf475532fa8f12720da5dcf6e9cded9fa7fad..5c8bbca2cadb651862a9fcac74c595b1108d1404 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -88,6 +88,11 @@ public: U32 getID() const { return mID; } + // Called by threads *not* created via LLThread to register some + // internal state used by LLMutex. You must call this once early + // in the running thread to prevent collisions with the main thread. + static void registerThreadID(); + private: BOOL mPaused; diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index 05289881d00f610ddd252c6ab9648284a7d00c6c..4c3bc674af9d5e9a971684e6b3d4d559814169d3 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -95,6 +95,7 @@ namespace tut ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); + ensure("keep compiler quiet", unpack_bufsize == unpack_bufsize); } // U32 packing diff --git a/indra/llcommon/tests/reflection_test.cpp b/indra/llcommon/tests/reflection_test.cpp index 59491cd1fe26e7037ef7047252947b81b8b31574..8980ebb1f1d1b9c129c803808e52a59f66b2955c 100644 --- a/indra/llcommon/tests/reflection_test.cpp +++ b/indra/llcommon/tests/reflection_test.cpp @@ -207,7 +207,7 @@ namespace tut const LLReflective* reflective = property->get(aggregated_data); // Wrong reflective type, should throw exception. // useless op to get rid of compiler warning. - reflective = NULL; + reflective = reflective; } catch(...) { diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..8632a2b72234d4e435fe2b6b4b3caee8aefa866b --- /dev/null +++ b/indra/llcorehttp/CMakeLists.txt @@ -0,0 +1,184 @@ +# -*- cmake -*- + +project(llcorehttp) + +include(00-Common) +include(GoogleMock) +include(CURL) +include(CARes) +include(OpenSSL) +include(ZLIB) +include(LLCoreHttp) +include(LLAddBuildTest) +include(LLMessage) +include(LLCommon) +include(Tut) + +include_directories (${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories( + ${LLMESSAGE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} + ) + +set(llcorehttp_SOURCE_FILES + bufferarray.cpp + bufferstream.cpp + httpcommon.cpp + httpheaders.cpp + httpoptions.cpp + httprequest.cpp + httpresponse.cpp + _httplibcurl.cpp + _httpopcancel.cpp + _httpoperation.cpp + _httpoprequest.cpp + _httpopsetget.cpp + _httpopsetpriority.cpp + _httppolicy.cpp + _httppolicyclass.cpp + _httppolicyglobal.cpp + _httpreplyqueue.cpp + _httprequestqueue.cpp + _httpservice.cpp + _refcounted.cpp + ) + +set(llcorehttp_HEADER_FILES + CMakeLists.txt + + bufferarray.h + bufferstream.h + httpcommon.h + httphandler.h + httpheaders.h + httpoptions.h + httprequest.h + httpresponse.h + _httpinternal.h + _httplibcurl.h + _httpopcancel.h + _httpoperation.h + _httpoprequest.h + _httpopsetget.h + _httpopsetpriority.h + _httppolicy.h + _httppolicyclass.h + _httppolicyglobal.h + _httpreadyqueue.h + _httpreplyqueue.h + _httprequestqueue.h + _httpservice.h + _mutex.h + _refcounted.h + _thread.h + ) + +set_source_files_properties(${llcorehttp_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) +if (DARWIN OR LINUX) + # Boost headers define unused members in condition_variable so... + set_source_files_properties(${llcorehttp_SOURCE_FILES} + PROPERTIES COMPILE_FLAGS -Wno-unused-variable) +endif (DARWIN OR LINUX) + +list(APPEND llcorehttp_SOURCE_FILES ${llcorehttp_HEADER_FILES}) + +add_library (llcorehttp ${llcorehttp_SOURCE_FILES}) +target_link_libraries( + llcorehttp + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + +# tests +if (LL_TESTS) + SET(llcorehttp_TEST_SOURCE_FILES + tests/test_allocator.cpp + ) + + set(llcorehttp_TEST_HEADER_FILS + tests/test_httpstatus.hpp + tests/test_refcounted.hpp + tests/test_httpoperation.hpp + tests/test_httprequest.hpp + tests/test_httprequestqueue.hpp + tests/test_httpheaders.hpp + tests/test_bufferarray.hpp + tests/test_bufferstream.hpp + ) + + set_source_files_properties(${llcorehttp_TEST_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + + list(APPEND llcorehttp_TEST_SOURCE_FILES ${llcorehttp_TEST_HEADER_FILES}) + + # LL_ADD_PROJECT_UNIT_TESTS(llcorehttp "${llcorehttp_TEST_SOURCE_FILES}") + + # set(TEST_DEBUG on) + set(test_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + + LL_ADD_INTEGRATION_TEST(llcorehttp + "${llcorehttp_TEST_SOURCE_FILES}" + "${test_libs}" + ${PYTHON_EXECUTABLE} + "${CMAKE_CURRENT_SOURCE_DIR}/tests/test_llcorehttp_peer.py" + ) + + # + # Example Programs + # + SET(llcorehttp_EXAMPLE_SOURCE_FILES + examples/http_texture_load.cpp + ) + + set(example_libs + ${LLCOREHTTP_LIBRARIES} + ${WINDOWS_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${GOOGLEMOCK_LIBRARIES} + ${CURL_LIBRARIES} + ${CARES_LIBRARIES} + ${OPENSSL_LIBRARIES} + ${CRYPTO_LIBRARIES} + ${BOOST_THREAD_LIBRARY} + ) + + add_executable(http_texture_load + ${llcorehttp_EXAMPLE_SOURCE_FILES} + ) + set_target_properties(http_texture_load + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${EXE_STAGING_DIR}" + ) + + if (WINDOWS) + # The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target. + set_target_properties(http_texture_load + PROPERTIES + LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:WINDOWS /INCLUDE:__tcmalloc" + LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO" + LINK_FLAGS_RELEASE "" + ) + endif (WINDOWS) + + target_link_libraries(http_texture_load ${example_libs}) + +endif (LL_TESTS) + diff --git a/indra/llcorehttp/_httpinternal.h b/indra/llcorehttp/_httpinternal.h new file mode 100644 index 0000000000000000000000000000000000000000..465e2036b3217c04db379d62e3a4e58fc685b2c0 --- /dev/null +++ b/indra/llcorehttp/_httpinternal.h @@ -0,0 +1,146 @@ +/** + * @file _httpinternal.h + * @brief Implementation constants and magic numbers + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_INTERNAL_H_ +#define _LLCORE_HTTP_INTERNAL_H_ + + +// If you find this included in a public interface header, +// something wrong is probably happening. + + +// -------------------------------------------------------------------- +// General library to-do list +// +// - Implement policy classes. Structure is mostly there just didn't +// need it for the first consumer. +// - Consider Removing 'priority' from the request interface. Its use +// in an always active class can lead to starvation of low-priority +// requests. Requires coodination of priority values across all +// components that share a class. Changing priority across threads +// is slightly expensive (relative to gain) and hasn't been completely +// implemented. And the major user of priority, texture fetches, +// may not really need it. +// - Set/get for global policy and policy classes is clumsy. Rework +// it heading in a direction that allows for more dynamic behavior. +// - Move HttpOpRequest::prepareRequest() to HttpLibcurl for the +// pedantic. +// - Update downloader and other long-duration services are going to +// need a progress notification. Initial idea is to introduce a +// 'repeating request' which can piggyback on another request and +// persist until canceled or carrier completes. Current queue +// structures allow an HttpOperation object to be enqueued +// repeatedly, so... +// - Investigate making c-ares' re-implementation of a resolver library +// more resilient or more intelligent on Mac. Part of the DNS failure +// lies in here. The mechanism also looks a little less dynamic +// than needed in an environments where networking is changing. +// - Global optimizations: 'borrowing' connections from other classes, +// HTTP pipelining. +// - Dynamic/control system stuff: detect problems and self-adjust. +// This won't help in the face of the router problems we've looked +// at, however. Detect starvation due to UDP activity and provide +// feedback to it. +// +// Integration to-do list +// - LLTextureFetch still needs a major refactor. The use of +// LLQueuedThread makes it hard to inspect workers and do the +// resource waiting we're now doing. Rebuild along simpler lines +// some of which are suggested in new commentary at the top of +// the main source file. +// - Expand areas of usage eventually leading to the removal of LLCurl. +// Rough order of expansion: +// . Mesh fetch +// . Avatar names +// . Group membership lists +// . Caps access in general +// . 'The rest' +// - Adapt texture cache, image decode and other image consumers to +// the BufferArray model to reduce data copying. Alternatively, +// adapt this library to something else. +// +// -------------------------------------------------------------------- + + +// If '1', internal ready queues will not order ready +// requests by priority, instead it's first-come-first-served. +// Reprioritization requests have the side-effect of then +// putting the modified request at the back of the ready queue. + +#define LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY 1 + + +namespace LLCore +{ + +// Maxium number of policy classes that can be defined. +// *TODO: Currently limited to the default class, extend. +const int HTTP_POLICY_CLASS_LIMIT = 1; + +// Debug/informational tracing. Used both +// as a global option and in per-request traces. +const int HTTP_TRACE_OFF = 0; +const int HTTP_TRACE_LOW = 1; +const int HTTP_TRACE_CURL_HEADERS = 2; +const int HTTP_TRACE_CURL_BODIES = 3; + +const int HTTP_TRACE_MIN = HTTP_TRACE_OFF; +const int HTTP_TRACE_MAX = HTTP_TRACE_CURL_BODIES; + +// Request retry limits +const int HTTP_RETRY_COUNT_DEFAULT = 5; +const int HTTP_RETRY_COUNT_MIN = 0; +const int HTTP_RETRY_COUNT_MAX = 100; + +const int HTTP_REDIRECTS_DEFAULT = 10; + +// Timeout value used for both connect and protocol exchange. +// Retries and time-on-queue are not included and aren't +// accounted for. +const long HTTP_REQUEST_TIMEOUT_DEFAULT = 30L; +const long HTTP_REQUEST_TIMEOUT_MIN = 0L; +const long HTTP_REQUEST_TIMEOUT_MAX = 3600L; + +// Limits on connection counts +const int HTTP_CONNECTION_LIMIT_DEFAULT = 8; +const int HTTP_CONNECTION_LIMIT_MIN = 1; +const int HTTP_CONNECTION_LIMIT_MAX = 256; + +// Tuning parameters + +// Time worker thread sleeps after a pass through the +// request, ready and active queues. +const int HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS = 2; + +// Block allocation size (a tuning parameter) is found +// in bufferarray.h. + +// Compatibility controls +const bool HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX = true; + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_INTERNAL_H_ diff --git a/indra/llcorehttp/_httplibcurl.cpp b/indra/llcorehttp/_httplibcurl.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6fe0bfc7d1a1dcdab3eebfabdd80f83a6a522853 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.cpp @@ -0,0 +1,373 @@ +/** + * @file _httplibcurl.cpp + * @brief Internal definitions of the Http libcurl thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httplibcurl.h" + +#include "httpheaders.h" +#include "bufferarray.h" +#include "_httpoprequest.h" +#include "_httppolicy.h" + +#include "llhttpstatuscodes.h" + + +namespace LLCore +{ + + +HttpLibcurl::HttpLibcurl(HttpService * service) + : mService(service), + mPolicyCount(0), + mMultiHandles(NULL) +{} + + +HttpLibcurl::~HttpLibcurl() +{ + shutdown(); + + mService = NULL; +} + + +void HttpLibcurl::shutdown() +{ + while (! mActiveOps.empty()) + { + HttpOpRequest * op(* mActiveOps.begin()); + mActiveOps.erase(mActiveOps.begin()); + + cancelRequest(op); + op->release(); + } + + if (mMultiHandles) + { + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + if (mMultiHandles[policy_class]) + { + curl_multi_cleanup(mMultiHandles[policy_class]); + mMultiHandles[policy_class] = 0; + } + } + + delete [] mMultiHandles; + mMultiHandles = NULL; + } + + mPolicyCount = 0; +} + + +void HttpLibcurl::start(int policy_count) +{ + llassert_always(policy_count <= HTTP_POLICY_CLASS_LIMIT); + llassert_always(! mMultiHandles); // One-time call only + + mPolicyCount = policy_count; + mMultiHandles = new CURLM * [mPolicyCount]; + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + mMultiHandles[policy_class] = curl_multi_init(); + } +} + + +// Give libcurl some cycles, invoke it's callbacks, process +// completed requests finalizing or issuing retries as needed. +// +// If active list goes empty *and* we didn't queue any +// requests for retry, we return a request for a hard +// sleep otherwise ask for a normal polling interval. +HttpService::ELoopSpeed HttpLibcurl::processTransport() +{ + HttpService::ELoopSpeed ret(HttpService::REQUEST_SLEEP); + + // Give libcurl some cycles to do I/O & callbacks + for (int policy_class(0); policy_class < mPolicyCount; ++policy_class) + { + if (! mMultiHandles[policy_class]) + continue; + + int running(0); + CURLMcode status(CURLM_CALL_MULTI_PERFORM); + do + { + running = 0; + status = curl_multi_perform(mMultiHandles[policy_class], &running); + } + while (0 != running && CURLM_CALL_MULTI_PERFORM == status); + + // Run completion on anything done + CURLMsg * msg(NULL); + int msgs_in_queue(0); + while ((msg = curl_multi_info_read(mMultiHandles[policy_class], &msgs_in_queue))) + { + if (CURLMSG_DONE == msg->msg) + { + CURL * handle(msg->easy_handle); + CURLcode result(msg->data.result); + + if (completeRequest(mMultiHandles[policy_class], handle, result)) + { + // Request is still active, don't get too sleepy + ret = HttpService::NORMAL; + } + handle = NULL; // No longer valid on return + } + else if (CURLMSG_NONE == msg->msg) + { + // Ignore this... it shouldn't mean anything. + ; + } + else + { + LL_WARNS_ONCE("CoreHttp") << "Unexpected message from libcurl. Msg code: " + << msg->msg + << LL_ENDL; + } + msgs_in_queue = 0; + } + } + + if (! mActiveOps.empty()) + { + ret = HttpService::NORMAL; + } + return ret; +} + + +// Caller has provided us with a ref count on op. +void HttpLibcurl::addOp(HttpOpRequest * op) +{ + llassert_always(op->mReqPolicy < mPolicyCount); + llassert_always(mMultiHandles[op->mReqPolicy] != NULL); + + // Create standard handle + if (! op->prepareRequest(mService)) + { + // Couldn't issue request, fail with notification + // *TODO: Need failure path + return; + } + + // Make the request live + curl_multi_add_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + op->mCurlActive = true; + + if (op->mTracing > HTTP_TRACE_OFF) + { + HttpPolicy & policy(mService->getPolicy()); + + LL_INFOS("CoreHttp") << "TRACE, ToActiveQueue, Handle: " + << static_cast<HttpHandle>(op) + << ", Actives: " << mActiveOps.size() + << ", Readies: " << policy.getReadyCount(op->mReqPolicy) + << LL_ENDL; + } + + // On success, make operation active + mActiveOps.insert(op); +} + + +// Implements the transport part of any cancel operation. +// See if the handle is an active operation and if so, +// use the more complicated transport-based cancelation +// method to kill the request. +bool HttpLibcurl::cancel(HttpHandle handle) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(handle)); + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + return false; + } + + // Cancel request + cancelRequest(op); + + // Drop references + mActiveOps.erase(it); + op->release(); + + return true; +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. Caller is expected to +// remove the op from the active list and release the op *after* +// calling this method. It must be called first to deliver the +// op to the reply queue with refcount intact. +void HttpLibcurl::cancelRequest(HttpOpRequest * op) +{ + // Deactivate request + op->mCurlActive = false; + + // Detach from multi and recycle handle + curl_multi_remove_handle(mMultiHandles[op->mReqPolicy], op->mCurlHandle); + curl_easy_cleanup(op->mCurlHandle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestCanceled, Handle: " + << static_cast<HttpHandle>(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Cancel op and deliver for notification + op->cancel(); +} + + +// *NOTE: cancelRequest logic parallels completeRequest logic. +// Keep them synchronized as necessary. +bool HttpLibcurl::completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status) +{ + HttpOpRequest * op(NULL); + curl_easy_getinfo(handle, CURLINFO_PRIVATE, &op); + + if (handle != op->mCurlHandle || ! op->mCurlActive) + { + LL_WARNS("CoreHttp") << "libcurl handle and HttpOpRequest handle in disagreement or inactive request." + << " Handle: " << static_cast<HttpHandle>(handle) + << LL_ENDL; + return false; + } + + active_set_t::iterator it(mActiveOps.find(op)); + if (mActiveOps.end() == it) + { + LL_WARNS("CoreHttp") << "libcurl completion for request not on active list. Continuing." + << " Handle: " << static_cast<HttpHandle>(handle) + << LL_ENDL; + return false; + } + + // Deactivate request + mActiveOps.erase(it); + op->mCurlActive = false; + + // Set final status of request if it hasn't failed by other mechanisms yet + if (op->mStatus) + { + op->mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, status); + } + if (op->mStatus) + { + int http_status(HTTP_OK); + + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_status); + if (http_status >= 100 && http_status <= 999) + { + char * cont_type(NULL); + curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &cont_type); + if (cont_type) + { + op->mReplyConType = cont_type; + } + op->mStatus = HttpStatus(http_status); + } + else + { + LL_WARNS("CoreHttp") << "Invalid HTTP response code (" + << http_status << ") received from server." + << LL_ENDL; + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INVALID_HTTP_STATUS); + } + } + + // Detach from multi and recycle handle + curl_multi_remove_handle(multi_handle, handle); + curl_easy_cleanup(handle); + op->mCurlHandle = NULL; + + // Tracing + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, RequestComplete, Handle: " + << static_cast<HttpHandle>(op) + << ", Status: " << op->mStatus.toHex() + << LL_ENDL; + } + + // Dispatch to next stage + HttpPolicy & policy(mService->getPolicy()); + bool still_active(policy.stageAfterCompletion(op)); + + return still_active; +} + + +int HttpLibcurl::getActiveCount() const +{ + return mActiveOps.size(); +} + + +int HttpLibcurl::getActiveCountInClass(int policy_class) const +{ + int count(0); + + for (active_set_t::const_iterator iter(mActiveOps.begin()); + mActiveOps.end() != iter; + ++iter) + { + if ((*iter)->mReqPolicy == policy_class) + { + ++count; + } + } + + return count; +} + + +// --------------------------------------- +// Free functions +// --------------------------------------- + + +struct curl_slist * append_headers_to_slist(const HttpHeaders * headers, struct curl_slist * slist) +{ + for (HttpHeaders::container_t::const_iterator it(headers->mHeaders.begin()); + + headers->mHeaders.end() != it; + ++it) + { + slist = curl_slist_append(slist, (*it).c_str()); + } + return slist; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httplibcurl.h b/indra/llcorehttp/_httplibcurl.h new file mode 100644 index 0000000000000000000000000000000000000000..611f029ef5194d50e7e1395c70bf97f5cf93c620 --- /dev/null +++ b/indra/llcorehttp/_httplibcurl.h @@ -0,0 +1,129 @@ +/** + * @file _httplibcurl.h + * @brief Declarations for internal class providing libcurl transport. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_LIBCURL_H_ +#define _LLCORE_HTTP_LIBCURL_H_ + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include <curl/curl.h> +#include <curl/multi.h> + +#include <set> + +#include "httprequest.h" +#include "_httpservice.h" +#include "_httpinternal.h" + + +namespace LLCore +{ + + +class HttpPolicy; +class HttpOpRequest; +class HttpHeaders; + + +/// Implements libcurl-based transport for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. + +class HttpLibcurl +{ +public: + HttpLibcurl(HttpService * service); + virtual ~HttpLibcurl(); + +private: + HttpLibcurl(const HttpLibcurl &); // Not defined + void operator=(const HttpLibcurl &); // Not defined + +public: + /// Give cycles to libcurl to run active requests. Completed + /// operations (successful or failed) will be retried or handed + /// over to the reply queue as final responses. + /// + /// @return Indication of how long this method is + /// willing to wait for next service call. + HttpService::ELoopSpeed processTransport(); + + /// Add request to the active list. Caller is expected to have + /// provided us with a reference count on the op to hold the + /// request. (No additional references will be added.) + void addOp(HttpOpRequest * op); + + /// One-time call to set the number of policy classes to be + /// serviced and to create the resources for each. Value + /// must agree with HttpPolicy::setPolicies() call. + void start(int policy_count); + + /// Synchronously stop libcurl operations. All active requests + /// are canceled and removed from libcurl's handling. Easy + /// handles are detached from their multi handles and released. + /// Multi handles are also released. Canceled requests are + /// completed with canceled status and made available on their + /// respective reply queues. + /// + /// Can be restarted with a start() call. + void shutdown(); + + /// Return global and per-class counts of active requests. + int getActiveCount() const; + int getActiveCountInClass(int policy_class) const; + + /// Attempt to cancel a request identified by handle. + /// + /// Interface shadows HttpService's method. + /// + /// @return True if handle was found and operation canceled. + /// + bool cancel(HttpHandle handle); + +protected: + /// Invoked when libcurl has indicated a request has been processed + /// to completion and we need to move the request to a new state. + bool completeRequest(CURLM * multi_handle, CURL * handle, CURLcode status); + + /// Invoked to cancel an active request, mainly during shutdown + /// and destroy. + void cancelRequest(HttpOpRequest * op); + +protected: + typedef std::set<HttpOpRequest *> active_set_t; + +protected: + HttpService * mService; // Simple reference, not owner + active_set_t mActiveOps; + int mPolicyCount; + CURLM ** mMultiHandles; +}; // end class HttpLibcurl + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_LIBCURL_H_ diff --git a/indra/llcorehttp/_httpopcancel.cpp b/indra/llcorehttp/_httpopcancel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c1912eb3db8584e70cb2a683cb83c3530b46468f --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.cpp @@ -0,0 +1,73 @@ +/** + * @file _httpopcancel.cpp + * @brief Definitions for internal class HttpOpCancel + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopcancel.h" + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" + +#include "_httpservice.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpCancel +// ================================== + + +HttpOpCancel::HttpOpCancel(HttpHandle handle) + : HttpOperation(), + mHandle(handle) +{} + + +HttpOpCancel::~HttpOpCancel() +{} + + +// Immediately search for the request on various queues +// and cancel operations if found. Return the status of +// the search and cancel as the status of this request. +// The canceled request will return a canceled status to +// its handler. +void HttpOpCancel::stageFromRequest(HttpService * service) +{ + if (! service->cancel(mHandle)) + { + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopcancel.h b/indra/llcorehttp/_httpopcancel.h new file mode 100644 index 0000000000000000000000000000000000000000..336dfdc573d632ff052d2f02b2231ac5934c45de --- /dev/null +++ b/indra/llcorehttp/_httpopcancel.h @@ -0,0 +1,78 @@ +/** + * @file _httpopcancel.h + * @brief Internal declarations for the HttpOpCancel subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPCANCEL_H_ +#define _LLCORE_HTTP_OPCANCEL_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include <curl/curl.h> + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpCancel requests that a previously issued request +/// be canceled, if possible. This includes active requests +/// that may be in the middle of an HTTP transaction. Any +/// completed request will not be canceled and will return +/// its final status unchanged and *this* request will complete +/// with an HE_HANDLE_NOT_FOUND error status. + +class HttpOpCancel : public HttpOperation +{ +public: + /// @param handle Handle of previously-issued request to + /// be canceled. + HttpOpCancel(HttpHandle handle); + +protected: + virtual ~HttpOpCancel(); // Use release() + +private: + HttpOpCancel(const HttpOpCancel &); // Not defined + void operator=(const HttpOpCancel &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +public: + // Request data + HttpHandle mHandle; +}; // end class HttpOpCancel + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPCANCEL_H_ + diff --git a/indra/llcorehttp/_httpoperation.cpp b/indra/llcorehttp/_httpoperation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5cf5bc59308ee2a50ce8bb917ce8f78ec4604164 --- /dev/null +++ b/indra/llcorehttp/_httpoperation.cpp @@ -0,0 +1,248 @@ +/** + * @file _httpoperation.cpp + * @brief Definitions for internal classes based on HttpOperation + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoperation.h" + +#include "httphandler.h" +#include "httpresponse.h" +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httpinternal.h" + +#include "lltimer.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOperation +// ================================== + + +HttpOperation::HttpOperation() + : LLCoreInt::RefCounted(true), + mReplyQueue(NULL), + mUserHandler(NULL), + mReqPolicy(HttpRequest::DEFAULT_POLICY_ID), + mReqPriority(0U), + mTracing(0) +{ + mMetricCreated = totalTime(); +} + + +HttpOperation::~HttpOperation() +{ + setReplyPath(NULL, NULL); +} + + +void HttpOperation::setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * user_handler) +{ + if (reply_queue != mReplyQueue) + { + if (mReplyQueue) + { + mReplyQueue->release(); + } + + if (reply_queue) + { + reply_queue->addRef(); + } + + mReplyQueue = reply_queue; + } + + // Not refcounted + mUserHandler = user_handler; +} + + + +void HttpOperation::stageFromRequest(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromRequest method may not be called." + << LL_ENDL; +} + + +void HttpOperation::stageFromReady(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromReady method may not be called." + << LL_ENDL; +} + + +void HttpOperation::stageFromActive(HttpService *) +{ + // Default implementation should never be called. This + // indicates an operation making a transition that isn't + // defined. + LL_ERRS("HttpCore") << "Default stageFromActive method may not be called." + << LL_ENDL; +} + + +void HttpOperation::visitNotifier(HttpRequest *) +{ + if (mUserHandler) + { + HttpResponse * response = new HttpResponse(); + + response->setStatus(mStatus); + mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); + + response->release(); + } +} + + +HttpStatus HttpOperation::cancel() +{ + HttpStatus status; + + return status; +} + + +void HttpOperation::addAsReply() +{ + if (mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, ToReplyQueue, Handle: " + << static_cast<HttpHandle>(this) + << LL_ENDL; + } + + if (mReplyQueue) + { + addRef(); + mReplyQueue->addOp(this); + } +} + + +// ================================== +// HttpOpStop +// ================================== + + +HttpOpStop::HttpOpStop() + : HttpOperation() +{} + + +HttpOpStop::~HttpOpStop() +{} + + +void HttpOpStop::stageFromRequest(HttpService * service) +{ + // Do operations + service->stopRequested(); + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpNull +// ================================== + + +HttpOpNull::HttpOpNull() + : HttpOperation() +{} + + +HttpOpNull::~HttpOpNull() +{} + + +void HttpOpNull::stageFromRequest(HttpService * service) +{ + // Perform op + // Nothing to perform. This doesn't fall into the libcurl + // ready/active queues, it just bounces over to the reply + // queue directly. + + // Prepare response if needed + addAsReply(); +} + + +// ================================== +// HttpOpSpin +// ================================== + + +HttpOpSpin::HttpOpSpin(int mode) + : HttpOperation(), + mMode(mode) +{} + + +HttpOpSpin::~HttpOpSpin() +{} + + +void HttpOpSpin::stageFromRequest(HttpService * service) +{ + if (0 == mMode) + { + // Spin forever + while (true) + { + ms_sleep(100); + } + } + else + { + ms_sleep(1); // backoff interlock plumbing a bit + this->addRef(); + if (! service->getRequestQueue().addOp(this)) + { + this->release(); + } + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpoperation.h b/indra/llcorehttp/_httpoperation.h new file mode 100644 index 0000000000000000000000000000000000000000..914627fad0a9e9f8d3931995d33b409da0ffb55f --- /dev/null +++ b/indra/llcorehttp/_httpoperation.h @@ -0,0 +1,262 @@ +/** + * @file _httpoperation.h + * @brief Internal declarations for HttpOperation and sub-classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPERATION_H_ +#define _LLCORE_HTTP_OPERATION_H_ + + +#include "httpcommon.h" +#include "httprequest.h" +#include "_refcounted.h" + + +namespace LLCore +{ + +class HttpReplyQueue; +class HttpHandler; +class HttpService; + +/// HttpOperation is the base class for all request/reply +/// pairs. +/// +/// Operations are expected to be of two types: immediate +/// and queued. Immediate requests go to the singleton +/// request queue and when picked up by the worker thread +/// are executed immediately and there results placed on +/// the supplied reply queue. Queued requests (namely for +/// HTTP operations), go to the request queue, are picked +/// up and moved to a ready queue where they're ordered by +/// priority and managed by the policy component, are +/// then activated issuing HTTP requests and moved to an +/// active list managed by the transport (libcurl) component +/// and eventually finalized when a response is available +/// and status and data return via reply queue. +/// +/// To manage these transitions, derived classes implement +/// three methods: stageFromRequest, stageFromReady and +/// stageFromActive. Immediate requests will only override +/// stageFromRequest which will perform the operation and +/// return the result by invoking addAsReply() to put the +/// request on a reply queue. Queued requests will involve +/// all three stage methods. +/// +/// Threading: not thread-safe. Base and derived classes +/// provide no locking. Instances move across threads +/// via queue-like interfaces that are thread compatible +/// and those interfaces establish the access rules. + +class HttpOperation : public LLCoreInt::RefCounted +{ +public: + /// Threading: called by a consumer/application thread. + HttpOperation(); + +protected: + /// Threading: called by any thread. + virtual ~HttpOperation(); // Use release() + +private: + HttpOperation(const HttpOperation &); // Not defined + void operator=(const HttpOperation &); // Not defined + +public: + /// Register a reply queue and a handler for completion notifications. + /// + /// Invokers of operations that want to receive notification that an + /// operation has been completed do so by binding a reply queue and + /// a handler object to the request. + /// + /// @param reply_queue Pointer to the reply queue where completion + /// notifications are to be queued (typically + /// by addAsReply()). This will typically be + /// the reply queue referenced by the request + /// object. This method will increment the + /// refcount on the queue holding the queue + /// until delivery is complete. Using a reply_queue + /// even if the handler is NULL has some benefits + /// for memory deallocation by keeping it in the + /// originating thread. + /// + /// @param handler Possibly NULL pointer to a non-refcounted + //// handler object to be invoked (onCompleted) + /// when the operation is finished. Note that + /// the handler object is never dereferenced + /// by the worker thread. This is passible data + /// until notification is performed. + /// + /// Threading: called by application thread. + /// + void setReplyPath(HttpReplyQueue * reply_queue, + HttpHandler * handler); + + /// The three possible staging steps in an operation's lifecycle. + /// Asynchronous requests like HTTP operations move from the + /// request queue to the ready queue via stageFromRequest. Then + /// from the ready queue to the active queue by stageFromReady. And + /// when complete, to the reply queue via stageFromActive and the + /// addAsReply utility. + /// + /// Immediate mode operations (everything else) move from the + /// request queue to the reply queue directly via stageFromRequest + /// and addAsReply with no existence on the ready or active queues. + /// + /// These methods will take out a reference count on the request, + /// caller only needs to dispose of its reference when done with + /// the request. + /// + /// Threading: called by worker thread. + /// + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + /// Delivers a notification to a handler object on completion. + /// + /// Once a request is complete and it has been removed from its + /// reply queue, a handler notification may be delivered by a + /// call to HttpRequest::update(). This method does the necessary + /// dispatching. + /// + /// Threading: called by application thread. + /// + virtual void visitNotifier(HttpRequest *); + + /// Cancels the operation whether queued or active. + /// Final status of the request becomes canceled (an error) and + /// that will be delivered to caller via notification scheme. + /// + /// Threading: called by worker thread. + /// + virtual HttpStatus cancel(); + +protected: + /// Delivers request to reply queue on completion. After this + /// call, worker thread no longer accesses the object and it + /// is owned by the reply queue. + /// + /// Threading: called by worker thread. + /// + void addAsReply(); + +protected: + HttpReplyQueue * mReplyQueue; // Have refcount + HttpHandler * mUserHandler; // Naked pointer + +public: + // Request Data + HttpRequest::policy_t mReqPolicy; + HttpRequest::priority_t mReqPriority; + + // Reply Data + HttpStatus mStatus; + + // Tracing, debug and metrics + HttpTime mMetricCreated; + int mTracing; +}; // end class HttpOperation + + +/// HttpOpStop requests the servicing thread to shutdown +/// operations, cease pulling requests from the request +/// queue and release shared resources (particularly +/// those shared via reference count). The servicing +/// thread will then exit. The underlying thread object +/// remains so that another thread can join on the +/// servicing thread prior to final cleanup. The +/// request *does* generate a reply on the response +/// queue, if requested. + +class HttpOpStop : public HttpOperation +{ +public: + HttpOpStop(); + +protected: + virtual ~HttpOpStop(); + +private: + HttpOpStop(const HttpOpStop &); // Not defined + void operator=(const HttpOpStop &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpStop + + +/// HttpOpNull is a do-nothing operation used for testing via +/// a basic loopback pattern. It's executed immediately by +/// the servicing thread which bounces a reply back to the +/// caller without any further delay. + +class HttpOpNull : public HttpOperation +{ +public: + HttpOpNull(); + +protected: + virtual ~HttpOpNull(); + +private: + HttpOpNull(const HttpOpNull &); // Not defined + void operator=(const HttpOpNull &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +}; // end class HttpOpNull + + +/// HttpOpSpin is a test-only request that puts the worker +/// thread into a cpu spin. Used for unit tests and cleanup +/// evaluation. You do not want to use this in production. +class HttpOpSpin : public HttpOperation +{ +public: + // 0 does a hard spin in the operation + // 1 does a soft spin continuously requeuing itself + HttpOpSpin(int mode); + +protected: + virtual ~HttpOpSpin(); + +private: + HttpOpSpin(const HttpOpSpin &); // Not defined + void operator=(const HttpOpSpin &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + int mMode; +}; // end class HttpOpSpin + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPERATION_H_ + diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7db19b18410ccf7d8293580a9c992c31e297f3d2 --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.cpp @@ -0,0 +1,906 @@ +/** + * @file _httpoprequest.cpp + * @brief Definitions for internal class HttpOpRequest + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpoprequest.h" + +#include <cstdio> +#include <algorithm> + +#include "httpcommon.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" +#include "httpoptions.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httppolicyglobal.h" +#include "_httplibcurl.h" +#include "_httpinternal.h" + +#include "llhttpstatuscodes.h" +#include "llproxy.h" + +namespace +{ + +// Attempts to parse a 'Content-Range:' header. Caller must already +// have verified that the header tag is present. The 'buffer' argument +// will be processed by strtok_r calls which will modify the buffer. +// +// @return -1 if invalid and response should be dropped, 0 if valid an +// correct, 1 if couldn't be parsed. If 0, the first, last, +// and length arguments are also written. 'length' may be +// 0 if the length wasn't available to the server. +// +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length); + + +// Take data from libcurl's CURLOPT_DEBUGFUNCTION callback and +// escape and format it for a tracing line in logging. Absolutely +// anything including NULs can be in the data. If @scrub is true, +// non-printing or non-ascii characters are replaced with spaces +// otherwise a %XX form of escaping is used. +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, + std::string & safe_line); + + +// OS-neutral string comparisons of various types +int os_strncasecmp(const char *s1, const char *s2, size_t n); +int os_strcasecmp(const char *s1, const char *s2); +char * os_strtok_r(char *str, const char *delim, char **saveptr); + + +static const char * const hdr_whitespace(" \t"); +static const char * const hdr_separator(": \t"); + +} // end anonymous namespace + + +namespace LLCore +{ + + +HttpOpRequest::HttpOpRequest() + : HttpOperation(), + mProcFlags(0U), + mReqMethod(HOR_GET), + mReqBody(NULL), + mReqOffset(0), + mReqLength(0), + mReqHeaders(NULL), + mReqOptions(NULL), + mCurlActive(false), + mCurlHandle(NULL), + mCurlService(NULL), + mCurlHeaders(NULL), + mCurlBodyPos(0), + mReplyBody(NULL), + mReplyOffset(0), + mReplyLength(0), + mReplyFullLength(0), + mReplyHeaders(NULL), + mPolicyRetries(0), + mPolicyRetryAt(HttpTime(0)), + mPolicyRetryLimit(HTTP_RETRY_COUNT_DEFAULT) +{ + // *NOTE: As members are added, retry initialization/cleanup + // may need to be extended in @see prepareRequest(). +} + + + +HttpOpRequest::~HttpOpRequest() +{ + if (mReqBody) + { + mReqBody->release(); + mReqBody = NULL; + } + + if (mReqOptions) + { + mReqOptions->release(); + mReqOptions = NULL; + } + + if (mReqHeaders) + { + mReqHeaders->release(); + mReqHeaders = NULL; + } + + if (mCurlHandle) + { + curl_easy_cleanup(mCurlHandle); + mCurlHandle = NULL; + } + + mCurlService = NULL; + + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } +} + + +void HttpOpRequest::stageFromRequest(HttpService * service) +{ + addRef(); + service->getPolicy().addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromReady(HttpService * service) +{ + addRef(); + service->getTransport().addOp(this); // transfers refcount +} + + +void HttpOpRequest::stageFromActive(HttpService * service) +{ + if (mReplyLength) + { + // If non-zero, we received and processed a Content-Range + // header with the response. Verify that what it says + // is consistent with the received data. + if (mReplyLength != mReplyBody->size()) + { + // Not as expected, fail the request + mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + } + + if (mCurlHeaders) + { + // We take these headers out of the request now as they were + // allocated originally in this thread and the notifier doesn't + // need them. This eliminates one source of heap moving across + // threads. + + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + + addAsReply(); +} + + +void HttpOpRequest::visitNotifier(HttpRequest * request) +{ + if (mUserHandler) + { + HttpResponse * response = new HttpResponse(); + response->setStatus(mStatus); + response->setBody(mReplyBody); + response->setHeaders(mReplyHeaders); + if (mReplyOffset || mReplyLength) + { + // Got an explicit offset/length in response + response->setRange(mReplyOffset, mReplyLength, mReplyFullLength); + } + response->setContentType(mReplyConType); + + mUserHandler->onCompleted(static_cast<HttpHandle>(this), response); + + response->release(); + } +} + + +HttpStatus HttpOpRequest::cancel() +{ + mStatus = HttpStatus(HttpStatus::LLCORE, HE_OP_CANCELED); + + addAsReply(); + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, NULL, options, headers); + mReqMethod = HOR_GET; + mReqOffset = offset; + mReqLength = len; + if (offset || len) + { + mProcFlags |= PF_SCAN_RANGE_HEADER; + } + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_POST; + + return HttpStatus(); +} + + +HttpStatus HttpOpRequest::setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + setupCommon(policy_id, priority, url, body, options, headers); + mReqMethod = HOR_PUT; + + return HttpStatus(); +} + + +void HttpOpRequest::setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers) +{ + mProcFlags = 0U; + mReqPolicy = policy_id; + mReqPriority = priority; + mReqURL = url; + if (body) + { + body->addRef(); + mReqBody = body; + } + if (headers && ! mReqHeaders) + { + headers->addRef(); + mReqHeaders = headers; + } + if (options && ! mReqOptions) + { + options->addRef(); + mReqOptions = options; + if (options->getWantHeaders()) + { + mProcFlags |= PF_SAVE_HEADERS; + } + mPolicyRetryLimit = options->getRetries(); + mPolicyRetryLimit = llclamp(mPolicyRetryLimit, HTTP_RETRY_COUNT_MIN, HTTP_RETRY_COUNT_MAX); + mTracing = (std::max)(mTracing, llclamp(options->getTrace(), HTTP_TRACE_MIN, HTTP_TRACE_MAX)); + } +} + + +// Sets all libcurl options and data for a request. +// +// Used both for initial requests and to 'reload' for +// a retry, generally with a different CURL handle. +// Junk may be left around from a failed request and that +// needs to be cleaned out. +// +HttpStatus HttpOpRequest::prepareRequest(HttpService * service) +{ + // Scrub transport and result data for retried op case + mCurlActive = false; + mCurlHandle = NULL; + mCurlService = NULL; + if (mCurlHeaders) + { + curl_slist_free_all(mCurlHeaders); + mCurlHeaders = NULL; + } + mCurlBodyPos = 0; + + if (mReplyBody) + { + mReplyBody->release(); + mReplyBody = NULL; + } + mReplyOffset = 0; + mReplyLength = 0; + mReplyFullLength = 0; + if (mReplyHeaders) + { + mReplyHeaders->release(); + mReplyHeaders = NULL; + } + mReplyConType.clear(); + + // *FIXME: better error handling later + HttpStatus status; + + // Get policy options + HttpPolicyGlobal & policy(service->getPolicy().getGlobalOptions()); + + mCurlHandle = curl_easy_init(); + curl_easy_setopt(mCurlHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(mCurlHandle, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_NOPROGRESS, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_URL, mReqURL.c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PRIVATE, this); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + + if (HTTP_ENABLE_LINKSYS_WRT54G_V5_DNS_FIX) + { + // The Linksys WRT54G V5 router has an issue with frequent + // DNS lookups from LAN machines. If they happen too often, + // like for every HTTP request, the router gets annoyed after + // about 700 or so requests and starts issuing TCP RSTs to + // new connections. Reuse the DNS lookups for even a few + // seconds and no RSTs. + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 15); + } + else + { + // *TODO: Revisit this old DNS timeout setting - may no longer be valid + // I don't think this is valid anymore, the Multi shared DNS + // cache is working well. For the case of naked easy handles, + // consider using a shared DNS object. + curl_easy_setopt(mCurlHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); + } + curl_easy_setopt(mCurlHandle, CURLOPT_AUTOREFERER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_MAXREDIRS, HTTP_REDIRECTS_DEFAULT); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEFUNCTION, writeCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_WRITEDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_READFUNCTION, readCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_READDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYPEER, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_SSL_VERIFYHOST, 0); + + const std::string * opt_value(NULL); + long opt_long(0L); + policy.get(HttpRequest::GP_LLPROXY, &opt_long); + if (opt_long) + { + // Use the viewer-based thread-safe API which has a + // fast/safe check for proxy enable. Would like to + // encapsulate this someway... + LLProxy::getInstance()->applyProxySettings(mCurlHandle); + } + else if (policy.get(HttpRequest::GP_HTTP_PROXY, &opt_value)) + { + // *TODO: This is fine for now but get fuller socks5/ + // authentication thing going later.... + curl_easy_setopt(mCurlHandle, CURLOPT_PROXY, opt_value->c_str()); + curl_easy_setopt(mCurlHandle, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + } + if (policy.get(HttpRequest::GP_CA_PATH, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAPATH, opt_value->c_str()); + } + if (policy.get(HttpRequest::GP_CA_FILE, &opt_value)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_CAINFO, opt_value->c_str()); + } + + switch (mReqMethod) + { + case HOR_GET: + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPGET, 1); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + break; + + case HOR_POST: + { + curl_easy_setopt(mCurlHandle, CURLOPT_POST, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_ENCODING, ""); + long data_size(0); + if (mReqBody) + { + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, static_cast<void *>(NULL)); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDSIZE, data_size); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + } + break; + + case HOR_PUT: + { + curl_easy_setopt(mCurlHandle, CURLOPT_UPLOAD, 1); + long data_size(0); + if (mReqBody) + { + data_size = mReqBody->size(); + } + curl_easy_setopt(mCurlHandle, CURLOPT_INFILESIZE, data_size); + curl_easy_setopt(mCurlHandle, CURLOPT_POSTFIELDS, (void *) NULL); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Expect:"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Connection: keep-alive"); + mCurlHeaders = curl_slist_append(mCurlHeaders, "Keep-alive: 300"); + } + break; + + default: + LL_ERRS("CoreHttp") << "Invalid HTTP method in request: " + << int(mReqMethod) << ". Can't recover." + << LL_ENDL; + break; + } + + // Tracing + if (mTracing >= HTTP_TRACE_CURL_HEADERS) + { + curl_easy_setopt(mCurlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGDATA, this); + curl_easy_setopt(mCurlHandle, CURLOPT_DEBUGFUNCTION, debugCallback); + } + + // There's a CURLOPT for this now... + if ((mReqOffset || mReqLength) && HOR_GET == mReqMethod) + { + static const char * const fmt1("Range: bytes=%lu-%lu"); + static const char * const fmt2("Range: bytes=%lu-"); + + char range_line[64]; + +#if LL_WINDOWS + _snprintf_s(range_line, sizeof(range_line), sizeof(range_line) - 1, + (mReqLength ? fmt1 : fmt2), + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); +#else + snprintf(range_line, sizeof(range_line), + (mReqLength ? fmt1 : fmt2), + (unsigned long) mReqOffset, (unsigned long) (mReqOffset + mReqLength - 1)); +#endif // LL_WINDOWS + range_line[sizeof(range_line) - 1] = '\0'; + mCurlHeaders = curl_slist_append(mCurlHeaders, range_line); + } + + mCurlHeaders = curl_slist_append(mCurlHeaders, "Pragma:"); + + // Request options + long timeout(HTTP_REQUEST_TIMEOUT_DEFAULT); + if (mReqOptions) + { + timeout = mReqOptions->getTimeout(); + timeout = llclamp(timeout, HTTP_REQUEST_TIMEOUT_MIN, HTTP_REQUEST_TIMEOUT_MAX); + } + curl_easy_setopt(mCurlHandle, CURLOPT_TIMEOUT, timeout); + curl_easy_setopt(mCurlHandle, CURLOPT_CONNECTTIMEOUT, timeout); + + // Request headers + if (mReqHeaders) + { + // Caller's headers last to override + mCurlHeaders = append_headers_to_slist(mReqHeaders, mCurlHeaders); + } + curl_easy_setopt(mCurlHandle, CURLOPT_HTTPHEADER, mCurlHeaders); + + if (mProcFlags & (PF_SCAN_RANGE_HEADER | PF_SAVE_HEADERS)) + { + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERFUNCTION, headerCallback); + curl_easy_setopt(mCurlHandle, CURLOPT_HEADERDATA, this); + } + + if (status) + { + mCurlService = service; + } + return status; +} + + +size_t HttpOpRequest::writeCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + if (! op->mReplyBody) + { + op->mReplyBody = new BufferArray(); + } + const size_t req_size(size * nmemb); + const size_t write_size(op->mReplyBody->append(static_cast<char *>(data), req_size)); + return write_size; +} + + +size_t HttpOpRequest::readCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + if (! op->mReqBody) + { + return 0; + } + const size_t req_size(size * nmemb); + const size_t body_size(op->mReqBody->size()); + if (body_size <= op->mCurlBodyPos) + { + LL_WARNS("HttpCore") << "Request body position beyond body size. Aborting request." + << LL_ENDL; + return 0; + } + + const size_t do_size((std::min)(req_size, body_size - op->mCurlBodyPos)); + const size_t read_size(op->mReqBody->read(op->mCurlBodyPos, static_cast<char *>(data), do_size)); + op->mCurlBodyPos += read_size; + return read_size; +} + + +size_t HttpOpRequest::headerCallback(void * data, size_t size, size_t nmemb, void * userdata) +{ + static const char status_line[] = "HTTP/"; + static const size_t status_line_len = sizeof(status_line) - 1; + + static const char con_ran_line[] = "content-range:"; + static const size_t con_ran_line_len = sizeof(con_ran_line) - 1; + + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + const size_t hdr_size(size * nmemb); + const char * hdr_data(static_cast<const char *>(data)); // Not null terminated + + if (hdr_size >= status_line_len && ! strncmp(status_line, hdr_data, status_line_len)) + { + // One of possibly several status lines. Reset what we know and start over + // taking results from the last header stanza we receive. + op->mReplyOffset = 0; + op->mReplyLength = 0; + op->mReplyFullLength = 0; + op->mStatus = HttpStatus(); + if (op->mReplyHeaders) + { + op->mReplyHeaders->mHeaders.clear(); + } + } + + // Nothing in here wants a final CR/LF combination. Remove + // it as much as possible. + size_t wanted_hdr_size(hdr_size); + if (wanted_hdr_size && '\n' == hdr_data[wanted_hdr_size - 1]) + { + if (--wanted_hdr_size && '\r' == hdr_data[wanted_hdr_size - 1]) + { + --wanted_hdr_size; + } + } + + // Save header if caller wants them in the response + if (op->mProcFlags & PF_SAVE_HEADERS) + { + // Save headers in response + if (! op->mReplyHeaders) + { + op->mReplyHeaders = new HttpHeaders; + } + op->mReplyHeaders->mHeaders.push_back(std::string(hdr_data, wanted_hdr_size)); + } + + // Detect and parse 'Content-Range' headers + if (op->mProcFlags & PF_SCAN_RANGE_HEADER) + { + char hdr_buffer[128]; // Enough for a reasonable header + size_t frag_size((std::min)(wanted_hdr_size, sizeof(hdr_buffer) - 1)); + + memcpy(hdr_buffer, hdr_data, frag_size); + hdr_buffer[frag_size] = '\0'; + if (frag_size > con_ran_line_len && + ! os_strncasecmp(hdr_buffer, con_ran_line, con_ran_line_len)) + { + unsigned int first(0), last(0), length(0); + int status; + + if (! (status = parse_content_range_header(hdr_buffer, &first, &last, &length))) + { + // Success, record the fragment position + op->mReplyOffset = first; + op->mReplyLength = last - first + 1; + op->mReplyFullLength = length; + } + else if (-1 == status) + { + // Response is badly formed and shouldn't be accepted + op->mStatus = HttpStatus(HttpStatus::LLCORE, HE_INV_CONTENT_RANGE_HDR); + } + else + { + // Ignore the unparsable. + LL_INFOS_ONCE("CoreHttp") << "Problem parsing odd Content-Range header: '" + << std::string(hdr_data, frag_size) + << "'. Ignoring." + << LL_ENDL; + } + } + } + + return hdr_size; +} + + +int HttpOpRequest::debugCallback(CURL * handle, curl_infotype info, char * buffer, size_t len, void * userdata) +{ + HttpOpRequest * op(static_cast<HttpOpRequest *>(userdata)); + + std::string safe_line; + std::string tag; + bool logit(false); + len = (std::min)(len, size_t(256)); // Keep things reasonable in all cases + + switch (info) + { + case CURLINFO_TEXT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "TEXT"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_IN: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "HEADERIN"; + escape_libcurl_debug_data(buffer, len, true, safe_line); + logit = true; + } + break; + + case CURLINFO_HEADER_OUT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "HEADEROUT"; + escape_libcurl_debug_data(buffer, 2 * len, true, safe_line); // Goes out as one line + logit = true; + } + break; + + case CURLINFO_DATA_IN: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "DATAIN"; + logit = true; + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + case CURLINFO_DATA_OUT: + if (op->mTracing >= HTTP_TRACE_CURL_HEADERS) + { + tag = "DATAOUT"; + logit = true; + if (op->mTracing >= HTTP_TRACE_CURL_BODIES) + { + escape_libcurl_debug_data(buffer, len, false, safe_line); + } + else + { + std::ostringstream out; + out << len << " Bytes"; + safe_line = out.str(); + } + } + break; + + default: + logit = false; + break; + } + + if (logit) + { + LL_INFOS("CoreHttp") << "TRACE, LibcurlDebug, Handle: " + << static_cast<HttpHandle>(op) + << ", Type: " << tag + << ", Data: " << safe_line + << LL_ENDL; + } + + return 0; +} + + +} // end namespace LLCore + + +// ======================================= +// Anonymous Namespace +// ======================================= + +namespace +{ + +int parse_content_range_header(char * buffer, + unsigned int * first, + unsigned int * last, + unsigned int * length) +{ + char * tok_state(NULL), * tok(NULL); + bool match(true); + + if (! os_strtok_r(buffer, hdr_separator, &tok_state)) + match = false; + if (match && (tok = os_strtok_r(NULL, hdr_whitespace, &tok_state))) + match = 0 == os_strcasecmp("bytes", tok); + if (match && ! (tok = os_strtok_r(NULL, " \t", &tok_state))) + match = false; + if (match) + { + unsigned int lcl_first(0), lcl_last(0), lcl_len(0); + +#if LL_WINDOWS + if (3 == sscanf_s(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#else + if (3 == sscanf(tok, "%u-%u/%u", &lcl_first, &lcl_last, &lcl_len)) +#endif // LL_WINDOWS + { + if (lcl_first > lcl_last || lcl_last >= lcl_len) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = lcl_len; + return 0; + } +#if LL_WINDOWS + if (2 == sscanf_s(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#else + if (2 == sscanf(tok, "%u-%u/*", &lcl_first, &lcl_last)) +#endif // LL_WINDOWS + { + if (lcl_first > lcl_last) + return -1; + *first = lcl_first; + *last = lcl_last; + *length = 0; + return 0; + } + } + + // Header is there but badly/unexpectedly formed, try to ignore it. + return 1; +} + + +void escape_libcurl_debug_data(char * buffer, size_t len, bool scrub, std::string & safe_line) +{ + std::string out; + len = (std::min)(len, size_t(200)); + out.reserve(3 * len); + for (int i(0); i < len; ++i) + { + unsigned char uc(static_cast<unsigned char>(buffer[i])); + + if (uc < 32 || uc > 126) + { + if (scrub) + { + out.append(1, ' '); + } + else + { + static const char hex[] = "0123456789ABCDEF"; + char convert[4]; + + convert[0] = '%'; + convert[1] = hex[(uc >> 4) % 16]; + convert[2] = hex[uc % 16]; + convert[3] = '\0'; + out.append(convert); + } + } + else + { + out.append(1, buffer[i]); + } + } + safe_line.swap(out); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ +#if LL_WINDOWS + return _strnicmp(s1, s2, n); +#else + return strncasecmp(s1, s2, n); +#endif // LL_WINDOWS +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ +#if LL_WINDOWS + return _stricmp(s1, s2); +#else + return strcasecmp(s1, s2); +#endif // LL_WINDOWS +} + + +char * os_strtok_r(char *str, const char *delim, char ** savestate) +{ +#if LL_WINDOWS + return strtok_s(str, delim, savestate); +#else + return strtok_r(str, delim, savestate); +#endif +} + + +} // end anonymous namespace + + diff --git a/indra/llcorehttp/_httpoprequest.h b/indra/llcorehttp/_httpoprequest.h new file mode 100644 index 0000000000000000000000000000000000000000..7b65d177833b2f635b5e4aca9d9701e1b28710ae --- /dev/null +++ b/indra/llcorehttp/_httpoprequest.h @@ -0,0 +1,219 @@ +/** + * @file _httpoprequest.h + * @brief Internal declarations for the HttpOpRequest subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPREQUEST_H_ +#define _LLCORE_HTTP_OPREQUEST_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include <string> +#include <curl/curl.h> + +#include "httpcommon.h" +#include "httprequest.h" +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +class BufferArray; +class HttpHeaders; +class HttpOptions; + + +/// HttpOpRequest requests a supported HTTP method invocation with +/// option and header overrides. +/// +/// Essentially an RPC to get an HTTP GET, POST or PUT executed +/// asynchronously with options to override behaviors and HTTP +/// headers. +/// +/// Constructor creates a raw object incapable of useful work. +/// A subsequent call to one of the setupXXX() methods provides +/// the information needed to make a working request which can +/// then be enqueued to a request queue. +/// + +class HttpOpRequest : public HttpOperation +{ +public: + HttpOpRequest(); + +protected: + virtual ~HttpOpRequest(); // Use release() + +private: + HttpOpRequest(const HttpOpRequest &); // Not defined + void operator=(const HttpOpRequest &); // Not defined + +public: + enum EMethod + { + HOR_GET, + HOR_POST, + HOR_PUT + }; + + virtual void stageFromRequest(HttpService *); + virtual void stageFromReady(HttpService *); + virtual void stageFromActive(HttpService *); + + virtual void visitNotifier(HttpRequest * request); + +public: + /// Setup Methods + /// + /// Basically an RPC setup for each type of HTTP method + /// invocation with one per method type. These are + /// generally invoked right after construction. + /// + /// Threading: called by application thread + /// + HttpStatus setupGet(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupGetByteRange(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupPost(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + HttpStatus setupPut(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + // Internal method used to setup the libcurl options for a request. + // Does all the libcurl handle setup in one place. + // + // Threading: called by worker thread + // + HttpStatus prepareRequest(HttpService * service); + + virtual HttpStatus cancel(); + +protected: + // Common setup for all the request methods. + // + // Threading: called by application thread + // + void setupCommon(HttpRequest::policy_t policy_id, + HttpRequest::priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers); + + // libcurl operational callbacks + // + // Threading: called by worker thread + // + static size_t writeCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t readCallback(void * data, size_t size, size_t nmemb, void * userdata); + static size_t headerCallback(void * data, size_t size, size_t nmemb, void * userdata); + static int debugCallback(CURL *, curl_infotype info, char * buffer, size_t len, void * userdata); + +protected: + unsigned int mProcFlags; + static const unsigned int PF_SCAN_RANGE_HEADER = 0x00000001U; + static const unsigned int PF_SAVE_HEADERS = 0x00000002U; + +public: + // Request data + EMethod mReqMethod; + std::string mReqURL; + BufferArray * mReqBody; + off_t mReqOffset; + size_t mReqLength; + HttpHeaders * mReqHeaders; + HttpOptions * mReqOptions; + + // Transport data + bool mCurlActive; + CURL * mCurlHandle; + HttpService * mCurlService; + curl_slist * mCurlHeaders; + size_t mCurlBodyPos; + + // Result data + HttpStatus mStatus; + BufferArray * mReplyBody; + off_t mReplyOffset; + size_t mReplyLength; + size_t mReplyFullLength; + HttpHeaders * mReplyHeaders; + std::string mReplyConType; + + // Policy data + int mPolicyRetries; + HttpTime mPolicyRetryAt; + int mPolicyRetryLimit; +}; // end class HttpOpRequest + + +/// HttpOpRequestCompare isn't an operation but a uniform comparison +/// functor for STL containers that order by priority. Mainly +/// used for the ready queue container but defined here. +class HttpOpRequestCompare +{ +public: + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mReqPriority > rhs->mReqPriority; + } +}; // end class HttpOpRequestCompare + + +// --------------------------------------- +// Free functions +// --------------------------------------- + +// Internal function to append the contents of an HttpHeaders +// instance to a curl_slist object. +curl_slist * append_headers_to_slist(const HttpHeaders *, curl_slist * slist); + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPREQUEST_H_ + diff --git a/indra/llcorehttp/_httpopsetget.cpp b/indra/llcorehttp/_httpopsetget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8198528a9bd70b39575add6c1820607b2c4cbe1b --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.cpp @@ -0,0 +1,97 @@ +/** + * @file _httpopsetget.cpp + * @brief Definitions for internal class HttpOpSetGet + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetget.h" + +#include "httpcommon.h" + +#include "_httpservice.h" +#include "_httppolicy.h" + + +namespace LLCore +{ + + +// ================================== +// HttpOpSetget +// ================================== + + +HttpOpSetGet::HttpOpSetGet() + : HttpOperation(), + mIsGlobal(false), + mDoSet(false), + mSetting(-1), // Nothing requested + mLongValue(0L) +{} + + +HttpOpSetGet::~HttpOpSetGet() +{} + + +void HttpOpSetGet::setupGet(HttpRequest::EGlobalPolicy setting) +{ + mIsGlobal = true; + mSetting = setting; +} + + +void HttpOpSetGet::setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value) +{ + mIsGlobal = true; + mDoSet = true; + mSetting = setting; + mStrValue = value; +} + + +void HttpOpSetGet::stageFromRequest(HttpService * service) +{ + HttpPolicyGlobal & pol_opt(service->getPolicy().getGlobalOptions()); + HttpRequest::EGlobalPolicy setting(static_cast<HttpRequest::EGlobalPolicy>(mSetting)); + + if (mDoSet) + { + mStatus = pol_opt.set(setting, mStrValue); + } + if (mStatus) + { + const std::string * value(NULL); + if ((mStatus = pol_opt.get(setting, &value))) + { + mStrValue = *value; + } + } + + addAsReply(); +} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/_httpopsetget.h b/indra/llcorehttp/_httpopsetget.h new file mode 100644 index 0000000000000000000000000000000000000000..6966b9d94e6f6b9c8cac7f5bbdacf0c0c4d0066f --- /dev/null +++ b/indra/llcorehttp/_httpopsetget.h @@ -0,0 +1,83 @@ +/** + * @file _httpopsetget.h + * @brief Internal declarations for the HttpOpSetGet subclass + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPSETGET_H_ +#define _LLCORE_HTTP_OPSETGET_H_ + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include "httpcommon.h" + +#include <curl/curl.h> + +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetGet requests dynamic changes to policy and +/// configuration settings. +/// +/// *NOTE: Expect this to change. Don't really like it yet. + +class HttpOpSetGet : public HttpOperation +{ +public: + HttpOpSetGet(); + +protected: + virtual ~HttpOpSetGet(); // Use release() + +private: + HttpOpSetGet(const HttpOpSetGet &); // Not defined + void operator=(const HttpOpSetGet &); // Not defined + +public: + /// Threading: called by application thread + void setupGet(HttpRequest::EGlobalPolicy setting); + void setupSet(HttpRequest::EGlobalPolicy setting, const std::string & value); + + virtual void stageFromRequest(HttpService *); + +public: + // Request data + bool mIsGlobal; + bool mDoSet; + int mSetting; + long mLongValue; + std::string mStrValue; + +}; // end class HttpOpSetGet + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_OPSETGET_H_ + diff --git a/indra/llcorehttp/_httpopsetpriority.cpp b/indra/llcorehttp/_httpopsetpriority.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d48c7a0b7d63bc58f43a3ab34fd669ec2f05eae2 --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.cpp @@ -0,0 +1,63 @@ +/** + * @file _httpopsetpriority.cpp + * @brief Definitions for internal classes based on HttpOpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpopsetpriority.h" + +#include "httpresponse.h" +#include "httphandler.h" +#include "_httpservice.h" + + +namespace LLCore +{ + + +HttpOpSetPriority::HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority) + : HttpOperation(), + mHandle(handle), + mPriority(priority) +{} + + +HttpOpSetPriority::~HttpOpSetPriority() +{} + + +void HttpOpSetPriority::stageFromRequest(HttpService * service) +{ + // Do operations + if (! service->changePriority(mHandle, mPriority)) + { + // Request not found, fail the final status + mStatus = HttpStatus(HttpStatus::LLCORE, HE_HANDLE_NOT_FOUND); + } + + // Move directly to response queue + addAsReply(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpopsetpriority.h b/indra/llcorehttp/_httpopsetpriority.h new file mode 100644 index 0000000000000000000000000000000000000000..31706b737c29a0f855a51194731e37b79c72407a --- /dev/null +++ b/indra/llcorehttp/_httpopsetpriority.h @@ -0,0 +1,73 @@ +/** + * @file _httpsetpriority.h + * @brief Internal declarations for HttpSetPriority + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_SETPRIORITY_H_ +#define _LLCORE_HTTP_SETPRIORITY_H_ + + +#include "httpcommon.h" +#include "httprequest.h" +#include "_httpoperation.h" +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// HttpOpSetPriority is an immediate request that +/// searches the various queues looking for a given +/// request handle and changing it's priority if +/// found. +/// +/// *NOTE: This will very likely be removed in the near future +/// when priority is removed from the library. + +class HttpOpSetPriority : public HttpOperation +{ +public: + HttpOpSetPriority(HttpHandle handle, HttpRequest::priority_t priority); + +protected: + virtual ~HttpOpSetPriority(); + +private: + HttpOpSetPriority(const HttpOpSetPriority &); // Not defined + void operator=(const HttpOpSetPriority &); // Not defined + +public: + virtual void stageFromRequest(HttpService *); + +protected: + // Request Data + HttpHandle mHandle; + HttpRequest::priority_t mPriority; +}; // end class HttpOpSetPriority + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SETPRIORITY_H_ + diff --git a/indra/llcorehttp/_httppolicy.cpp b/indra/llcorehttp/_httppolicy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..76c1e2243166b98ed0a50e7d4db0ee4ffa251c32 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.cpp @@ -0,0 +1,387 @@ +/** + * @file _httppolicy.cpp + * @brief Internal definitions of the Http policy thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "_httppolicy.h" + +#include "_httpoprequest.h" +#include "_httpservice.h" +#include "_httplibcurl.h" +#include "_httppolicyclass.h" + +#include "lltimer.h" + + +namespace LLCore +{ + + +// Per-policy-class data for a running system. +// Collection of queues, parameters, history, metrics, etc. +// for a single policy class. +// +// Threading: accessed only by worker thread +struct HttpPolicy::State +{ +public: + State() + : mConnMax(HTTP_CONNECTION_LIMIT_DEFAULT), + mConnAt(HTTP_CONNECTION_LIMIT_DEFAULT), + mConnMin(1), + mNextSample(0), + mErrorCount(0), + mErrorFactor(0) + {} + + HttpReadyQueue mReadyQueue; + HttpRetryQueue mRetryQueue; + + HttpPolicyClass mOptions; + + long mConnMax; + long mConnAt; + long mConnMin; + + HttpTime mNextSample; + unsigned long mErrorCount; + unsigned long mErrorFactor; +}; + + +HttpPolicy::HttpPolicy(HttpService * service) + : mActiveClasses(0), + mState(NULL), + mService(service) +{} + + +HttpPolicy::~HttpPolicy() +{ + shutdown(); + + mService = NULL; +} + + +void HttpPolicy::shutdown() +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + HttpRetryQueue & retryq(mState[policy_class].mRetryQueue); + while (! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + retryq.pop(); + + op->cancel(); + op->release(); + } + + HttpReadyQueue & readyq(mState[policy_class].mReadyQueue); + while (! readyq.empty()) + { + HttpOpRequest * op(readyq.top()); + readyq.pop(); + + op->cancel(); + op->release(); + } + } + delete [] mState; + mState = NULL; + mActiveClasses = 0; +} + + +void HttpPolicy::start(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes) +{ + llassert_always(! mState); + + mGlobalOptions = global; + mActiveClasses = classes.size(); + mState = new State [mActiveClasses]; + for (int i(0); i < mActiveClasses; ++i) + { + mState[i].mOptions = classes[i]; + mState[i].mConnMax = classes[i].mConnectionLimit; + mState[i].mConnAt = mState[i].mConnMax; + mState[i].mConnMin = 2; + } +} + + +void HttpPolicy::addOp(HttpOpRequest * op) +{ + const int policy_class(op->mReqPolicy); + + op->mPolicyRetries = 0; + mState[policy_class].mReadyQueue.push(op); +} + + +void HttpPolicy::retryOp(HttpOpRequest * op) +{ + static const HttpTime retry_deltas[] = + { + 250000, // 1st retry in 0.25 S, etc... + 500000, + 1000000, + 2000000, + 5000000 // ... to every 5.0 S. + }; + static const int delta_max(int(LL_ARRAY_SIZE(retry_deltas)) - 1); + + const HttpTime now(totalTime()); + const int policy_class(op->mReqPolicy); + + const HttpTime delta(retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)]); + op->mPolicyRetryAt = now + delta; + ++op->mPolicyRetries; + LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) + << " retry " << op->mPolicyRetries + << " scheduled for +" << (delta / HttpTime(1000)) + << " mS. Status: " << op->mStatus.toHex() + << LL_ENDL; + if (op->mTracing > 0) + { + LL_INFOS("CoreHttp") << "TRACE, ToRetryQueue, Handle: " + << static_cast<HttpHandle>(op) + << LL_ENDL; + } + mState[policy_class].mRetryQueue.push(op); +} + + +// Attempt to deliver requests to the transport layer. +// +// Tries to find HTTP requests for each policy class with +// available capacity. Starts with the retry queue first +// looking for requests that have waited long enough then +// moves on to the ready queue. +// +// If all queues are empty, will return an indication that +// the worker thread may sleep hard otherwise will ask for +// normal polling frequency. +// +HttpService::ELoopSpeed HttpPolicy::processReadyQueue() +{ + const HttpTime now(totalTime()); + HttpService::ELoopSpeed result(HttpService::REQUEST_SLEEP); + HttpLibcurl & transport(mService->getTransport()); + + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + int active(transport.getActiveCountInClass(policy_class)); + int needed(state.mConnAt - active); // Expect negatives here + + HttpRetryQueue & retryq(state.mRetryQueue); + HttpReadyQueue & readyq(state.mReadyQueue); + + if (needed > 0) + { + // First see if we have any retries... + while (needed > 0 && ! retryq.empty()) + { + HttpOpRequest * op(retryq.top()); + if (op->mPolicyRetryAt > now) + break; + + retryq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + + // Now go on to the new requests... + while (needed > 0 && ! readyq.empty()) + { + HttpOpRequest * op(readyq.top()); + readyq.pop(); + + op->stageFromReady(mService); + op->release(); + + --needed; + } + } + + if (! readyq.empty() || ! retryq.empty()) + { + // If anything is ready, continue looping... + result = HttpService::NORMAL; + } + } // end foreach policy_class + + return result; +} + + +bool HttpPolicy::changePriority(HttpHandle handle, HttpRequest::priority_t priority) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + // We don't scan retry queue because a priority change there + // is meaningless. The request will be issued based on retry + // intervals not priority value, which is now moot. + + // Scan ready queue for requests that match policy + HttpReadyQueue::container_type & c(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c.begin()); c.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast<HttpHandle>(*cur) == handle) + { + HttpOpRequest * op(*cur); + c.erase(cur); // All iterators are now invalidated + op->mReqPriority = priority; + state.mReadyQueue.push(op); // Re-insert using adapter class + return true; + } + } + } + + return false; +} + + +bool HttpPolicy::cancel(HttpHandle handle) +{ + for (int policy_class(0); policy_class < mActiveClasses; ++policy_class) + { + State & state(mState[policy_class]); + + // Scan retry queue + HttpRetryQueue::container_type & c1(state.mRetryQueue.get_container()); + for (HttpRetryQueue::container_type::iterator iter(c1.begin()); c1.end() != iter;) + { + HttpRetryQueue::container_type::iterator cur(iter++); + + if (static_cast<HttpHandle>(*cur) == handle) + { + HttpOpRequest * op(*cur); + c1.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + + // Scan ready queue + HttpReadyQueue::container_type & c2(state.mReadyQueue.get_container()); + for (HttpReadyQueue::container_type::iterator iter(c2.begin()); c2.end() != iter;) + { + HttpReadyQueue::container_type::iterator cur(iter++); + + if (static_cast<HttpHandle>(*cur) == handle) + { + HttpOpRequest * op(*cur); + c2.erase(cur); // All iterators are now invalidated + op->cancel(); + op->release(); + return true; + } + } + } + + return false; +} + + +bool HttpPolicy::stageAfterCompletion(HttpOpRequest * op) +{ + static const HttpStatus cant_connect(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + static const HttpStatus cant_res_proxy(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_PROXY); + static const HttpStatus cant_res_host(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_RESOLVE_HOST); + static const HttpStatus send_error(HttpStatus::EXT_CURL_EASY, CURLE_SEND_ERROR); + static const HttpStatus recv_error(HttpStatus::EXT_CURL_EASY, CURLE_RECV_ERROR); + static const HttpStatus upload_failed(HttpStatus::EXT_CURL_EASY, CURLE_UPLOAD_FAILED); + static const HttpStatus op_timedout(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + static const HttpStatus post_error(HttpStatus::EXT_CURL_EASY, CURLE_HTTP_POST_ERROR); + + // Retry or finalize + if (! op->mStatus) + { + // If this failed, we might want to retry. Have to inspect + // the status a little more deeply for those reasons worth retrying... + if (op->mPolicyRetries < op->mPolicyRetryLimit && + ((op->mStatus.isHttpStatus() && op->mStatus.mType >= 499 && op->mStatus.mType <= 599) || + cant_connect == op->mStatus || + cant_res_proxy == op->mStatus || + cant_res_host == op->mStatus || + send_error == op->mStatus || + recv_error == op->mStatus || + upload_failed == op->mStatus || + op_timedout == op->mStatus || + post_error == op->mStatus)) + { + // Okay, worth a retry. We include 499 in this test as + // it's the old 'who knows?' error from many grid services... + retryOp(op); + return true; // still active/ready + } + } + + // This op is done, finalize it delivering it to the reply queue... + if (! op->mStatus) + { + LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) + << " failed after " << op->mPolicyRetries + << " retries. Reason: " << op->mStatus.toString() + << " (" << op->mStatus.toHex() << ")" + << LL_ENDL; + } + else if (op->mPolicyRetries) + { + LL_WARNS("CoreHttp") << "HTTP request " << static_cast<HttpHandle>(op) + << " succeeded on retry " << op->mPolicyRetries << "." + << LL_ENDL; + } + + op->stageFromActive(mService); + op->release(); + return false; // not active +} + + +int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const +{ + if (policy_class < mActiveClasses) + { + return (mState[policy_class].mReadyQueue.size() + + mState[policy_class].mRetryQueue.size()); + } + return 0; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicy.h b/indra/llcorehttp/_httppolicy.h new file mode 100644 index 0000000000000000000000000000000000000000..03d92c0b8e176656480c07a692a5d58a7977bbf4 --- /dev/null +++ b/indra/llcorehttp/_httppolicy.h @@ -0,0 +1,161 @@ +/** + * @file _httppolicy.h + * @brief Declarations for internal class enforcing policy decisions. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_H_ +#define _LLCORE_HTTP_POLICY_H_ + + +#include "httprequest.h" +#include "_httpservice.h" +#include "_httpreadyqueue.h" +#include "_httpretryqueue.h" +#include "_httppolicyglobal.h" +#include "_httppolicyclass.h" +#include "_httpinternal.h" + + +namespace LLCore +{ + +class HttpReadyQueue; +class HttpOpRequest; + + +/// Implements class-based queuing policies for an HttpService instance. +/// +/// Threading: Single-threaded. Other than for construction/destruction, +/// all methods are expected to be invoked in a single thread, typically +/// a worker thread of some sort. +class HttpPolicy +{ +public: + HttpPolicy(HttpService *); + virtual ~HttpPolicy(); + +private: + HttpPolicy(const HttpPolicy &); // Not defined + void operator=(const HttpPolicy &); // Not defined + +public: + /// Cancel all ready and retry requests sending them to + /// their notification queues. Release state resources + /// making further request handling impossible. + /// + /// Threading: called by worker thread + void shutdown(); + + /// Deliver policy definitions and enable handling of + /// requests. One-time call invoked before starting + /// the worker thread. + /// + /// Threading: called by application thread + void start(const HttpPolicyGlobal & global, + const std::vector<HttpPolicyClass> & classes); + + /// Give the policy layer some cycles to scan the ready + /// queue promoting higher-priority requests to active + /// as permited. + /// + /// @return Indication of how soon this method + /// should be called again. + /// + /// Threading: called by worker thread + HttpService::ELoopSpeed processReadyQueue(); + + /// Add request to a ready queue. Caller is expected to have + /// provided us with a reference count to hold the request. (No + /// additional references will be added.) + /// + /// OpRequest is owned by the request queue after this call + /// and should not be modified by anyone until retrieved + /// from queue. + /// + /// Threading: called by any thread + void addOp(HttpOpRequest *); + + /// Similar to addOp, used when a caller wants to retry a + /// request that has failed. It's placed on a special retry + /// queue but ordered by retry time not priority. Otherwise, + /// handling is the same and retried operations are considered + /// before new ones but that doesn't guarantee completion + /// order. + /// + /// Threading: called by worker thread + void retryOp(HttpOpRequest *); + + /// Attempt to change the priority of an earlier request. + /// Request that Shadows HttpService's method + /// + /// Threading: called by worker thread + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + /// Attempt to cancel a previous request. + /// Shadows HttpService's method as well + /// + /// Threading: called by worker thread + bool cancel(HttpHandle handle); + + /// When transport is finished with an op and takes it off the + /// active queue, it is delivered here for dispatch. Policy + /// may send it back to the ready/retry queues if it needs another + /// go or we may finalize it and send it on to the reply queue. + /// + /// @return Returns true of the request is still active + /// or ready after staging, false if has been + /// sent on to the reply queue. + /// + /// Threading: called by worker thread + bool stageAfterCompletion(HttpOpRequest * op); + + // Get pointer to global policy options. Caller is expected + // to do context checks like no setting once running. + /// + /// Threading: called by any thread *but* the object may + /// only be modified by the worker thread once running. + /// + HttpPolicyGlobal & getGlobalOptions() + { + return mGlobalOptions; + } + + /// Get ready counts for a particular policy class + /// + /// Threading: called by worker thread + int getReadyCount(HttpRequest::policy_t policy_class) const; + +protected: + struct State; + + int mActiveClasses; + State * mState; + HttpService * mService; // Naked pointer, not refcounted, not owner + HttpPolicyGlobal mGlobalOptions; + +}; // end class HttpPolicy + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_H_ diff --git a/indra/llcorehttp/_httppolicyclass.cpp b/indra/llcorehttp/_httppolicyclass.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a23b81322c6b0803ccfdf5ef896dea917da854f6 --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.cpp @@ -0,0 +1,125 @@ +/** + * @file _httppolicyclass.cpp + * @brief Definitions for internal class defining class policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyclass.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyClass::HttpPolicyClass() + : mSetMask(0UL), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mPerHostConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mPipelining(0) +{} + + +HttpPolicyClass::~HttpPolicyClass() +{} + + +HttpPolicyClass & HttpPolicyClass::operator=(const HttpPolicyClass & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mPerHostConnectionLimit = other.mPerHostConnectionLimit; + mPipelining = other.mPipelining; + } + return *this; +} + + +HttpPolicyClass::HttpPolicyClass(const HttpPolicyClass & other) + : mSetMask(other.mSetMask), + mConnectionLimit(other.mConnectionLimit), + mPerHostConnectionLimit(other.mPerHostConnectionLimit), + mPipelining(other.mPipelining) +{} + + +HttpStatus HttpPolicyClass::set(HttpRequest::EClassPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + mPerHostConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), mConnectionLimit); + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + mPipelining = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyClass::get(HttpRequest::EClassPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::CP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::CP_PER_HOST_CONNECTION_LIMIT: + src = &mPerHostConnectionLimit; + break; + + case HttpRequest::CP_ENABLE_PIPELINING: + src = &mPipelining; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyclass.h b/indra/llcorehttp/_httppolicyclass.h new file mode 100644 index 0000000000000000000000000000000000000000..d175413cbd489887699d6167fea117fb9968c4b1 --- /dev/null +++ b/indra/llcorehttp/_httppolicyclass.h @@ -0,0 +1,59 @@ +/** + * @file _httppolicyclass.h + * @brief Declarations for internal class defining policy class options. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_CLASS_H_ +#define _LLCORE_HTTP_POLICY_CLASS_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyClass +{ +public: + HttpPolicyClass(); + ~HttpPolicyClass(); + + HttpPolicyClass & operator=(const HttpPolicyClass &); + HttpPolicyClass(const HttpPolicyClass &); // Not defined + +public: + HttpStatus set(HttpRequest::EClassPolicy opt, long value); + HttpStatus get(HttpRequest::EClassPolicy opt, long * value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + long mPerHostConnectionLimit; + long mPipelining; +}; // end class HttpPolicyClass + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_CLASS_H_ diff --git a/indra/llcorehttp/_httppolicyglobal.cpp b/indra/llcorehttp/_httppolicyglobal.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72f409d3b1dbad98b143406381d10d310df9f504 --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.cpp @@ -0,0 +1,175 @@ +/** + * @file _httppolicyglobal.cpp + * @brief Definitions for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httppolicyglobal.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpPolicyGlobal::HttpPolicyGlobal() + : mSetMask(0UL), + mConnectionLimit(HTTP_CONNECTION_LIMIT_DEFAULT), + mTrace(HTTP_TRACE_OFF), + mUseLLProxy(0) +{} + + +HttpPolicyGlobal::~HttpPolicyGlobal() +{} + + +HttpPolicyGlobal & HttpPolicyGlobal::operator=(const HttpPolicyGlobal & other) +{ + if (this != &other) + { + mSetMask = other.mSetMask; + mConnectionLimit = other.mConnectionLimit; + mCAPath = other.mCAPath; + mCAFile = other.mCAFile; + mHttpProxy = other.mHttpProxy; + mTrace = other.mTrace; + mUseLLProxy = other.mUseLLProxy; + } + return *this; +} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, long value) +{ + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + mConnectionLimit = llclamp(value, long(HTTP_CONNECTION_LIMIT_MIN), long(HTTP_CONNECTION_LIMIT_MAX)); + break; + + case HttpRequest::GP_TRACE: + mTrace = llclamp(value, long(HTTP_TRACE_MIN), long(HTTP_TRACE_MAX)); + break; + + case HttpRequest::GP_LLPROXY: + mUseLLProxy = llclamp(value, 0L, 1L); + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::set(HttpRequest::EGlobalPolicy opt, const std::string & value) +{ + switch (opt) + { + case HttpRequest::GP_CA_PATH: + mCAPath = value; + break; + + case HttpRequest::GP_CA_FILE: + mCAFile = value; + break; + + case HttpRequest::GP_HTTP_PROXY: + mCAFile = value; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + mSetMask |= 1UL << int(opt); + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, long * value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + long * src(NULL); + + switch (opt) + { + case HttpRequest::GP_CONNECTION_LIMIT: + src = &mConnectionLimit; + break; + + case HttpRequest::GP_TRACE: + src = &mTrace; + break; + + case HttpRequest::GP_LLPROXY: + src = &mUseLLProxy; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = *src; + return HttpStatus(); +} + + +HttpStatus HttpPolicyGlobal::get(HttpRequest::EGlobalPolicy opt, const std::string ** value) +{ + static const HttpStatus not_set(HttpStatus::LLCORE, HE_OPT_NOT_SET); + const std::string * src(NULL); + + switch (opt) + { + case HttpRequest::GP_CA_PATH: + src = &mCAPath; + break; + + case HttpRequest::GP_CA_FILE: + src = &mCAFile; + break; + + case HttpRequest::GP_HTTP_PROXY: + src = &mHttpProxy; + break; + + default: + return HttpStatus(HttpStatus::LLCORE, HE_INVALID_ARG); + } + + if (! (mSetMask & (1UL << int(opt)))) + return not_set; + + *value = src; + return HttpStatus(); +} + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httppolicyglobal.h b/indra/llcorehttp/_httppolicyglobal.h new file mode 100644 index 0000000000000000000000000000000000000000..a50d0e41887c4a47f44bc2fcce78fd008484e447 --- /dev/null +++ b/indra/llcorehttp/_httppolicyglobal.h @@ -0,0 +1,66 @@ +/** + * @file _httppolicyglobal.h + * @brief Declarations for internal class defining global policy option. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_POLICY_GLOBAL_H_ +#define _LLCORE_HTTP_POLICY_GLOBAL_H_ + + +#include "httprequest.h" + + +namespace LLCore +{ + +class HttpPolicyGlobal +{ +public: + HttpPolicyGlobal(); + ~HttpPolicyGlobal(); + + HttpPolicyGlobal & operator=(const HttpPolicyGlobal &); + +private: + HttpPolicyGlobal(const HttpPolicyGlobal &); // Not defined + +public: + HttpStatus set(HttpRequest::EGlobalPolicy opt, long value); + HttpStatus set(HttpRequest::EGlobalPolicy opt, const std::string & value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, long * value); + HttpStatus get(HttpRequest::EGlobalPolicy opt, const std::string ** value); + +public: + unsigned long mSetMask; + long mConnectionLimit; + std::string mCAPath; + std::string mCAFile; + std::string mHttpProxy; + long mTrace; + long mUseLLProxy; +}; // end class HttpPolicyGlobal + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_POLICY_GLOBAL_H_ diff --git a/indra/llcorehttp/_httpreadyqueue.h b/indra/llcorehttp/_httpreadyqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..5f19a9c5f9ded8408a164257871ffa94c3e1d668 --- /dev/null +++ b/indra/llcorehttp/_httpreadyqueue.h @@ -0,0 +1,124 @@ +/** + * @file _httpreadyqueue.h + * @brief Internal declaration for the operation ready queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_READY_QUEUE_H_ +#define _LLCORE_HTTP_READY_QUEUE_H_ + + +#include <queue> + +#include "_httpinternal.h" +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpReadyQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// If LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY tests true, the class +/// implements a std::priority_queue interface but on std::deque +/// behavior to eliminate sensitivity to priority. In the future, +/// this will likely become the only behavior or it may become +/// a run-time election. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + +typedef std::deque<HttpOpRequest *> HttpReadyQueueBase; + +#else + +typedef std::priority_queue<HttpOpRequest *, + std::deque<HttpOpRequest *>, + LLCore::HttpOpRequestCompare> HttpReadyQueueBase; + +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + +class HttpReadyQueue : public HttpReadyQueueBase +{ +public: + HttpReadyQueue() + : HttpReadyQueueBase() + {} + + ~HttpReadyQueue() + {} + +protected: + HttpReadyQueue(const HttpReadyQueue &); // Not defined + void operator=(const HttpReadyQueue &); // Not defined + +public: + +#if LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + // Types and methods needed to make a std::deque look + // more like a std::priority_queue, at least for our + // purposes. + typedef HttpReadyQueueBase container_type; + + const_reference top() const + { + return front(); + } + + void pop() + { + pop_front(); + } + + void push(const value_type & v) + { + push_back(v); + } + +#endif // LLCORE_HTTP_READY_QUEUE_IGNORES_PRIORITY + + const container_type & get_container() const + { + return *this; + } + + container_type & get_container() + { + return *this; + } + +}; // end class HttpReadyQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_READY_QUEUE_H_ diff --git a/indra/llcorehttp/_httpreplyqueue.cpp b/indra/llcorehttp/_httpreplyqueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..558b7bdee9d4eb36f5a0333f57c6e2b42fa77e08 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.cpp @@ -0,0 +1,107 @@ +/** + * @file _httpreplyqueue.cpp + * @brief Internal definitions for the operation reply queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpreplyqueue.h" + + +#include "_mutex.h" +#include "_thread.h" +#include "_httpoperation.h" + +using namespace LLCoreInt; + + +namespace LLCore +{ + + +HttpReplyQueue::HttpReplyQueue() + : RefCounted(true) +{ +} + + +HttpReplyQueue::~HttpReplyQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpReplyQueue::addOp(HttpOperation * op) +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueue.push_back(op); + } + mQueueCV.notify_all(); +} + + +HttpOperation * HttpReplyQueue::fetchOp() +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + if (mQueue.empty()) + return NULL; + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpReplyQueue::fetchAll(OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + if (! mQueue.empty()) + { + mQueue.swap(ops); + } + } + + // Caller also acquires the reference counts on each op. + return; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpreplyqueue.h b/indra/llcorehttp/_httpreplyqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..4220a09a3b02420c35c06b80ff60b38b12cf4719 --- /dev/null +++ b/indra/llcorehttp/_httpreplyqueue.h @@ -0,0 +1,108 @@ +/** + * @file _httpreplyqueue.h + * @brief Internal declarations for the operation reply queue. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REPLY_QUEUE_H_ +#define _LLCORE_HTTP_REPLY_QUEUE_H_ + + +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Almost identical to the HttpRequestQueue class but +/// whereas that class is a singleton and is known to the +/// HttpService object, this queue is 1:1 with HttpRequest +/// instances and isn't explicitly referenced by the +/// service object. Instead, HttpOperation objects that +/// want to generate replies back to their creators also +/// keep references to the corresponding HttpReplyQueue. +/// The HttpService plumbing then simply delivers replies +/// to the requested reply queue. +/// +/// One result of that is that the fetch operations do +/// not have a wait forever option. The service object +/// doesn't keep handles on everything it would need to +/// notify so it can't wake up sleepers should it need to +/// shutdown. So only non-blocking or timed-blocking modes +/// are anticipated. These are how most application consumers +/// will be coded anyway so it shouldn't be too much of a +/// burden. + +class HttpReplyQueue : public LLCoreInt::RefCounted +{ +public: + /// Caller acquires a Refcount on construction + HttpReplyQueue(); + +protected: + virtual ~HttpReplyQueue(); // Use release() + +private: + HttpReplyQueue(const HttpReplyQueue &); // Not defined + void operator=(const HttpReplyQueue &); // Not defined + +public: + typedef std::vector<HttpOperation *> OpContainer; + + /// Insert an object at the back of the reply queue. + /// + /// Library also takes possession of one reference count to pass + /// through the queue. + /// + /// Threading: callable by any thread. + void addOp(HttpOperation * op); + + /// Fetch an operation from the head of the queue. Returns + /// NULL if none exists. + /// + /// Caller acquires reference count on returned operation. + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(); + + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(OpContainer & ops); + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + +}; // end class HttpReplyQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REPLY_QUEUE_H_ diff --git a/indra/llcorehttp/_httprequestqueue.cpp b/indra/llcorehttp/_httprequestqueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c16966d078f294c833ff5d172ec56ba1f9ef7e41 --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.cpp @@ -0,0 +1,161 @@ +/** + * @file _httprequestqueue.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httprequestqueue.h" + +#include "_httpoperation.h" +#include "_mutex.h" + + +using namespace LLCoreInt; + +namespace LLCore +{ + +HttpRequestQueue * HttpRequestQueue::sInstance(NULL); + + +HttpRequestQueue::HttpRequestQueue() + : RefCounted(true), + mQueueStopped(false) +{ +} + + +HttpRequestQueue::~HttpRequestQueue() +{ + while (! mQueue.empty()) + { + HttpOperation * op = mQueue.back(); + mQueue.pop_back(); + op->release(); + } +} + + +void HttpRequestQueue::init() +{ + llassert_always(! sInstance); + sInstance = new HttpRequestQueue(); +} + + +void HttpRequestQueue::term() +{ + if (sInstance) + { + sInstance->release(); + sInstance = NULL; + } +} + + +HttpStatus HttpRequestQueue::addOp(HttpOperation * op) +{ + bool wake(false); + { + HttpScopedLock lock(mQueueMutex); + + if (mQueueStopped) + { + // Return op and error to caller + return HttpStatus(HttpStatus::LLCORE, HE_SHUTTING_DOWN); + } + wake = mQueue.empty(); + mQueue.push_back(op); + } + if (wake) + { + mQueueCV.notify_all(); + } + return HttpStatus(); +} + + +HttpOperation * HttpRequestQueue::fetchOp(bool wait) +{ + HttpOperation * result(NULL); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait || mQueueStopped) + return NULL; + mQueueCV.wait(lock); + } + + result = mQueue.front(); + mQueue.erase(mQueue.begin()); + } + + // Caller also acquires the reference count + return result; +} + + +void HttpRequestQueue::fetchAll(bool wait, OpContainer & ops) +{ + // Not valid putting something back on the queue... + llassert_always(ops.empty()); + + { + HttpScopedLock lock(mQueueMutex); + + while (mQueue.empty()) + { + if (! wait || mQueueStopped) + return; + mQueueCV.wait(lock); + } + + mQueue.swap(ops); + } + + // Caller also acquires the reference counts on each op. + return; +} + + +void HttpRequestQueue::wakeAll() +{ + mQueueCV.notify_all(); +} + + +void HttpRequestQueue::stopQueue() +{ + { + HttpScopedLock lock(mQueueMutex); + + mQueueStopped = true; + wakeAll(); + } +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httprequestqueue.h b/indra/llcorehttp/_httprequestqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..c9c52b723391800569ce5f9b45b744c0261b292d --- /dev/null +++ b/indra/llcorehttp/_httprequestqueue.h @@ -0,0 +1,141 @@ +/** + * @file _httprequestqueue.h + * @brief Internal declaration for the operation request queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_QUEUE_H_ +#define _LLCORE_HTTP_REQUEST_QUEUE_H_ + + +#include <vector> + +#include "httpcommon.h" +#include "_refcounted.h" +#include "_mutex.h" + + +namespace LLCore +{ + + +class HttpOperation; + + +/// Thread-safe queue of HttpOperation objects. Just +/// a simple queue that handles the transfer of operation +/// requests from all HttpRequest instances into the +/// singleton HttpService instance. + +class HttpRequestQueue : public LLCoreInt::RefCounted +{ +protected: + /// Caller acquires a Refcount on construction + HttpRequestQueue(); + +protected: + virtual ~HttpRequestQueue(); // Use release() + +private: + HttpRequestQueue(const HttpRequestQueue &); // Not defined + void operator=(const HttpRequestQueue &); // Not defined + +public: + static void init(); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpRequestQueue * instanceOf() + { + return sInstance; + } + +public: + typedef std::vector<HttpOperation *> OpContainer; + + /// Insert an object at the back of the request queue. + /// + /// Caller must provide one refcount to the queue which takes + /// possession of the count on success. + /// + /// @return Standard status. On failure, caller + /// must dispose of the operation with + /// an explicit release() call. + /// + /// Threading: callable by any thread. + HttpStatus addOp(HttpOperation * op); + + /// Return the operation on the front of the queue. If + /// the queue is empty and @wait is false, call returns + /// immediately and a NULL pointer is returned. If true, + /// caller will sleep until explicitly woken. Wakeups + /// can be spurious and callers must expect NULL pointers + /// even if waiting is indicated. + /// + /// Caller acquires reference count any returned operation + /// + /// Threading: callable by any thread. + HttpOperation * fetchOp(bool wait); + + /// Return all queued requests to caller. The @ops argument + /// should be empty when called and will be swap()'d with + /// current contents. Handling of the @wait argument is + /// identical to @fetchOp. + /// + /// Caller acquires reference count on each returned operation + /// + /// Threading: callable by any thread. + void fetchAll(bool wait, OpContainer & ops); + + /// Wake any sleeping threads. Normal queuing operations + /// won't require this but it may be necessary for termination + /// requests. + /// + /// Threading: callable by any thread. + void wakeAll(); + + /// Disallow further request queuing. Callers to @addOp will + /// get a failure status (LLCORE, HE_SHUTTING_DOWN). Callers + /// to @fetchAll or @fetchOp will get requests that are on the + /// queue but the calls will no longer wait. Instead they'll + /// return immediately. Also wakes up all sleepers to send + /// them on their way. + /// + /// Threading: callable by any thread. + void stopQueue(); + +protected: + static HttpRequestQueue * sInstance; + +protected: + OpContainer mQueue; + LLCoreInt::HttpMutex mQueueMutex; + LLCoreInt::HttpConditionVariable mQueueCV; + bool mQueueStopped; + +}; // end class HttpRequestQueue + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_REQUEST_QUEUE_H_ diff --git a/indra/llcorehttp/_httpretryqueue.h b/indra/llcorehttp/_httpretryqueue.h new file mode 100644 index 0000000000000000000000000000000000000000..745adec09dbd76b95093f7af154be3953ef3526c --- /dev/null +++ b/indra/llcorehttp/_httpretryqueue.h @@ -0,0 +1,94 @@ +/** + * @file _httpretryqueue.h + * @brief Internal declaration for the operation retry queue + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RETRY_QUEUE_H_ +#define _LLCORE_HTTP_RETRY_QUEUE_H_ + + +#include <queue> + +#include "_httpoprequest.h" + + +namespace LLCore +{ + +/// HttpRetryQueue provides a simple priority queue for HttpOpRequest objects. +/// +/// This uses the priority_queue adaptor class to provide the queue +/// as well as the ordering scheme while allowing us access to the +/// raw container if we follow a few simple rules. One of the more +/// important of those rules is that any iterator becomes invalid +/// on element erasure. So pay attention. +/// +/// Threading: not thread-safe. Expected to be used entirely by +/// a single thread, typically a worker thread of some sort. + +struct HttpOpRetryCompare +{ + bool operator()(const HttpOpRequest * lhs, const HttpOpRequest * rhs) + { + return lhs->mPolicyRetryAt < rhs->mPolicyRetryAt; + } +}; + + +typedef std::priority_queue<HttpOpRequest *, + std::deque<HttpOpRequest *>, + LLCore::HttpOpRetryCompare> HttpRetryQueueBase; + +class HttpRetryQueue : public HttpRetryQueueBase +{ +public: + HttpRetryQueue() + : HttpRetryQueueBase() + {} + + ~HttpRetryQueue() + {} + +protected: + HttpRetryQueue(const HttpRetryQueue &); // Not defined + void operator=(const HttpRetryQueue &); // Not defined + +public: + const container_type & get_container() const + { + return c; + } + + container_type & get_container() + { + return c; + } + +}; // end class HttpRetryQueue + + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_RETRY_QUEUE_H_ diff --git a/indra/llcorehttp/_httpservice.cpp b/indra/llcorehttp/_httpservice.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0825888d0f5352f050e5ac88a9cc0a4ff7d1ee56 --- /dev/null +++ b/indra/llcorehttp/_httpservice.cpp @@ -0,0 +1,348 @@ +/** + * @file _httpservice.cpp + * @brief Internal definitions of the Http service thread + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_httpservice.h" + +#include <boost/bind.hpp> +#include <boost/function.hpp> + +#include "_httpoperation.h" +#include "_httprequestqueue.h" +#include "_httppolicy.h" +#include "_httplibcurl.h" +#include "_thread.h" +#include "_httpinternal.h" + +#include "lltimer.h" +#include "llthread.h" + + +namespace LLCore +{ + +HttpService * HttpService::sInstance(NULL); +volatile HttpService::EState HttpService::sState(NOT_INITIALIZED); + +HttpService::HttpService() + : mRequestQueue(NULL), + mExitRequested(0U), + mThread(NULL), + mPolicy(NULL), + mTransport(NULL) +{ + // Create the default policy class + HttpPolicyClass pol_class; + pol_class.set(HttpRequest::CP_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); + pol_class.set(HttpRequest::CP_PER_HOST_CONNECTION_LIMIT, HTTP_CONNECTION_LIMIT_DEFAULT); + pol_class.set(HttpRequest::CP_ENABLE_PIPELINING, 0L); + mPolicyClasses.push_back(pol_class); +} + + +HttpService::~HttpService() +{ + mExitRequested = 1U; + if (RUNNING == sState) + { + // Trying to kill the service object with a running thread + // is a bit tricky. + if (mRequestQueue) + { + mRequestQueue->stopQueue(); + } + + if (mThread) + { + if (! mThread->timedJoin(250)) + { + // Failed to join, expect problems ahead so do a hard termination. + mThread->cancel(); + + LL_WARNS("CoreHttp") << "Destroying HttpService with running thread. Expect problems." + << LL_ENDL; + } + } + } + + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + delete mTransport; + mTransport = NULL; + + delete mPolicy; + mPolicy = NULL; + + if (mThread) + { + mThread->release(); + mThread = NULL; + } +} + + +void HttpService::init(HttpRequestQueue * queue) +{ + llassert_always(! sInstance); + llassert_always(NOT_INITIALIZED == sState); + sInstance = new HttpService(); + + queue->addRef(); + sInstance->mRequestQueue = queue; + sInstance->mPolicy = new HttpPolicy(sInstance); + sInstance->mTransport = new HttpLibcurl(sInstance); + sState = INITIALIZED; +} + + +void HttpService::term() +{ + if (sInstance) + { + if (RUNNING == sState && sInstance->mThread) + { + // Unclean termination. Thread appears to be running. We'll + // try to give the worker thread a chance to cancel using the + // exit flag... + sInstance->mExitRequested = 1U; + sInstance->mRequestQueue->stopQueue(); + + // And a little sleep + for (int i(0); i < 10 && RUNNING == sState; ++i) + { + ms_sleep(100); + } + } + + delete sInstance; + sInstance = NULL; + } + sState = NOT_INITIALIZED; +} + + +HttpRequest::policy_t HttpService::createPolicyClass() +{ + const HttpRequest::policy_t policy_class(mPolicyClasses.size()); + if (policy_class >= HTTP_POLICY_CLASS_LIMIT) + { + return 0; + } + mPolicyClasses.push_back(HttpPolicyClass()); + return policy_class; +} + + +bool HttpService::isStopped() +{ + // What is really wanted here is something like: + // + // HttpService * service = instanceOf(); + // return STOPPED == sState && (! service || ! service->mThread || ! service->mThread->joinable()); + // + // But boost::thread is not giving me a consistent story on joinability + // of a thread after it returns. Debug and non-debug builds are showing + // different behavior on Linux/Etch so we do a weaker test that may + // not be globally correct (i.e. thread *is* stopping, may not have + // stopped but will very soon): + + return STOPPED == sState; +} + + +/// Threading: callable by consumer thread *once*. +void HttpService::startThread() +{ + llassert_always(! mThread || STOPPED == sState); + llassert_always(INITIALIZED == sState || STOPPED == sState); + + if (mThread) + { + mThread->release(); + } + + // Push current policy definitions, enable policy & transport components + mPolicy->start(mPolicyGlobal, mPolicyClasses); + mTransport->start(mPolicyClasses.size()); + + mThread = new LLCoreInt::HttpThread(boost::bind(&HttpService::threadRun, this, _1)); + sState = RUNNING; +} + + +/// Threading: callable by worker thread. +void HttpService::stopRequested() +{ + mExitRequested = 1U; +} + + +/// Threading: callable by worker thread. +bool HttpService::changePriority(HttpHandle handle, HttpRequest::priority_t priority) +{ + bool found(false); + + // Skip the request queue as we currently don't leave earlier + // requests sitting there. Start with the ready queue... + found = mPolicy->changePriority(handle, priority); + + // If not there, we could try the transport/active queue but priority + // doesn't really have much effect there so we don't waste cycles. + + return found; +} + + +/// Try to find the given request handle on any of the request +/// queues and cancel the operation. +/// +/// @return True if the request was canceled. +/// +/// Threading: callable by worker thread. +bool HttpService::cancel(HttpHandle handle) +{ + bool canceled(false); + + // Request can't be on request queue so skip that. + + // Check the policy component's queues first + canceled = mPolicy->cancel(handle); + + if (! canceled) + { + // If that didn't work, check transport's. + canceled = mTransport->cancel(handle); + } + + return canceled; +} + + +/// Threading: callable by worker thread. +void HttpService::shutdown() +{ + // Disallow future enqueue of requests + mRequestQueue->stopQueue(); + + // Cancel requests already on the request queue + HttpRequestQueue::OpContainer ops; + mRequestQueue->fetchAll(false, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + op->cancel(); + op->release(); + } + + // Shutdown transport canceling requests, freeing resources + mTransport->shutdown(); + + // And now policy + mPolicy->shutdown(); +} + + +// Working thread loop-forever method. Gives time to +// each of the request queue, policy layer and transport +// layer pieces and then either sleeps for a small time +// or waits for a request to come in. Repeats until +// requested to stop. +void HttpService::threadRun(LLCoreInt::HttpThread * thread) +{ + boost::this_thread::disable_interruption di; + + LLThread::registerThreadID(); + + ELoopSpeed loop(REQUEST_SLEEP); + while (! mExitRequested) + { + loop = processRequestQueue(loop); + + // Process ready queue issuing new requests as needed + ELoopSpeed new_loop = mPolicy->processReadyQueue(); + loop = (std::min)(loop, new_loop); + + // Give libcurl some cycles + new_loop = mTransport->processTransport(); + loop = (std::min)(loop, new_loop); + + // Determine whether to spin, sleep briefly or sleep for next request + if (REQUEST_SLEEP != loop) + { + ms_sleep(HTTP_SERVICE_LOOP_SLEEP_NORMAL_MS); + } + } + + shutdown(); + sState = STOPPED; +} + + +HttpService::ELoopSpeed HttpService::processRequestQueue(ELoopSpeed loop) +{ + HttpRequestQueue::OpContainer ops; + const bool wait_for_req(REQUEST_SLEEP == loop); + + mRequestQueue->fetchAll(wait_for_req, ops); + while (! ops.empty()) + { + HttpOperation * op(ops.front()); + ops.erase(ops.begin()); + + // Process operation + if (! mExitRequested) + { + // Setup for subsequent tracing + long tracing(HTTP_TRACE_OFF); + mPolicy->getGlobalOptions().get(HttpRequest::GP_TRACE, &tracing); + op->mTracing = (std::max)(op->mTracing, int(tracing)); + + if (op->mTracing > HTTP_TRACE_OFF) + { + LL_INFOS("CoreHttp") << "TRACE, FromRequestQueue, Handle: " + << static_cast<HttpHandle>(op) + << LL_ENDL; + } + + // Stage + op->stageFromRequest(this); + } + + // Done with operation + op->release(); + } + + // Queue emptied, allow polling loop to sleep + return REQUEST_SLEEP; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/_httpservice.h b/indra/llcorehttp/_httpservice.h new file mode 100644 index 0000000000000000000000000000000000000000..ffe0349d4d5432d77b48ba2ccdfadab28c9696a7 --- /dev/null +++ b/indra/llcorehttp/_httpservice.h @@ -0,0 +1,224 @@ +/** + * @file _httpservice.h + * @brief Declarations for internal class providing HTTP service. + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_SERVICE_H_ +#define _LLCORE_HTTP_SERVICE_H_ + + +#include <vector> + +#include "linden_common.h" +#include "llapr.h" +#include "httpcommon.h" +#include "httprequest.h" +#include "_httppolicyglobal.h" +#include "_httppolicyclass.h" + + +namespace LLCoreInt +{ + +class HttpThread; + +} + + +namespace LLCore +{ + + +class HttpRequestQueue; +class HttpPolicy; +class HttpLibcurl; + + +/// The HttpService class does the work behind the request queue. It +/// oversees the HTTP workflow carrying out a number of tasks: +/// - Pulling requests from the global request queue +/// - Executing 'immediate' requests directly +/// - Prioritizing and re-queuing on internal queues the slower requests +/// - Providing cpu cycles to the libcurl plumbing +/// - Overseeing retry operations +/// +/// Note that the service object doesn't have a pointer to any +/// reply queue. These are kept by HttpRequest and HttpOperation +/// only. +/// +/// Service, Policy and Transport +/// +/// HttpService could have been a monolithic class combining a request +/// queue servicer, request policy manager and network transport. +/// Instead, to prevent monolithic growth and allow for easier +/// replacement, it was developed as three separate classes: HttpService, +/// HttpPolicy and HttpLibcurl (transport). These always exist in a +/// 1:1:1 relationship with HttpService managing instances of the other +/// two. So, these classes do not use reference counting to refer +/// to one another, their lifecycles are always managed together. + +class HttpService +{ +protected: + HttpService(); + virtual ~HttpService(); + +private: + HttpService(const HttpService &); // Not defined + void operator=(const HttpService &); // Not defined + +public: + enum EState + { + NOT_INITIALIZED = -1, + INITIALIZED, ///< init() has been called + RUNNING, ///< thread created and running + STOPPED ///< thread has committed to exiting + }; + + // Ordered enumeration of idling strategies available to + // threadRun's loop. Ordered so that std::min on values + // produces the most conservative result of multiple + // requests. + enum ELoopSpeed + { + NORMAL, ///< continuous polling of request, ready, active queues + REQUEST_SLEEP ///< can sleep indefinitely waiting for request queue write + }; + + static void init(HttpRequestQueue *); + static void term(); + + /// Threading: callable by any thread once inited. + inline static HttpService * instanceOf() + { + return sInstance; + } + + /// Return the state of the worker thread. Note that the + /// transition from RUNNING to STOPPED is performed by the + /// worker thread itself. This has two weaknesses: + /// - race where the thread hasn't really stopped but will + /// - data ordering between threads where a non-worker thread + /// may see a stale RUNNING status. + /// + /// This transition is generally of interest only to unit tests + /// and these weaknesses shouldn't be any real burden. + /// + /// Threading: callable by any thread with above exceptions. + static EState getState() + { + return sState; + } + + /// Threading: callable by any thread but uses @see getState() and + /// acquires its weaknesses. + static bool isStopped(); + + /// Threading: callable by consumer thread *once*. + void startThread(); + + /// Threading: callable by worker thread. + void stopRequested(); + + /// Threading: callable by worker thread. + void shutdown(); + + /// Try to find the given request handle on any of the request + /// queues and reset the priority (and queue position) of the + /// request if found. + /// + /// @return True if the request was found somewhere. + /// + /// Threading: callable by worker thread. + bool changePriority(HttpHandle handle, HttpRequest::priority_t priority); + + /// Try to find the given request handle on any of the request + /// queues and cancel the operation. + /// + /// @return True if the request was found and canceled. + /// + /// Threading: callable by worker thread. + bool cancel(HttpHandle handle); + + /// Threading: callable by worker thread. + HttpPolicy & getPolicy() + { + return *mPolicy; + } + + /// Threading: callable by worker thread. + HttpLibcurl & getTransport() + { + return *mTransport; + } + + /// Threading: callable by worker thread. + HttpRequestQueue & getRequestQueue() + { + return *mRequestQueue; + } + + /// Threading: callable by consumer thread. + HttpPolicyGlobal & getGlobalOptions() + { + return mPolicyGlobal; + } + + /// Threading: callable by consumer thread. + HttpRequest::policy_t createPolicyClass(); + + /// Threading: callable by consumer thread. + HttpPolicyClass & getClassOptions(HttpRequest::policy_t policy_class) + { + llassert(policy_class >= 0 && policy_class < mPolicyClasses.size()); + return mPolicyClasses[policy_class]; + } + +protected: + void threadRun(LLCoreInt::HttpThread * thread); + + ELoopSpeed processRequestQueue(ELoopSpeed loop); + +protected: + static HttpService * sInstance; + + // === shared data === + static volatile EState sState; + HttpRequestQueue * mRequestQueue; // Refcounted + LLAtomicU32 mExitRequested; + LLCoreInt::HttpThread * mThread; + + // === consumer-thread-only data === + HttpPolicyGlobal mPolicyGlobal; + std::vector<HttpPolicyClass> mPolicyClasses; + + // === working-thread-only data === + HttpPolicy * mPolicy; // Simple pointer, has ownership + HttpLibcurl * mTransport; // Simple pointer, has ownership +}; // end class HttpService + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_SERVICE_H_ diff --git a/indra/llcorehttp/_mutex.h b/indra/llcorehttp/_mutex.h new file mode 100644 index 0000000000000000000000000000000000000000..4be4d016d4e65ad7dc1c8beeb44e0d297f058d41 --- /dev/null +++ b/indra/llcorehttp/_mutex.h @@ -0,0 +1,55 @@ +/** + * @file _mutex.hpp + * @brief mutex type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_MUTEX_H_ +#define LLCOREINT_MUTEX_H_ + + +#include <boost/thread.hpp> + + +namespace LLCoreInt +{ + +// MUTEX TYPES + +// unique mutex type +typedef boost::mutex HttpMutex; + +// CONDITION VARIABLES + +// standard condition variable +typedef boost::condition_variable HttpConditionVariable; + +// LOCKS AND FENCES + +// scoped unique lock +typedef boost::unique_lock<HttpMutex> HttpScopedLock; + +} + +#endif // LLCOREINT_MUTEX_H + diff --git a/indra/llcorehttp/_refcounted.cpp b/indra/llcorehttp/_refcounted.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e7d0b72741d00b59ab86f3a39243c121bd547c0f --- /dev/null +++ b/indra/llcorehttp/_refcounted.cpp @@ -0,0 +1,45 @@ +/** + * @file _refcounted.cpp + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "_refcounted.h" + + +namespace LLCoreInt +{ + +#if ! LL_WINDOWS + +const S32 RefCounted::NOT_REF_COUNTED; + +#endif // ! LL_WINDOWS + +RefCounted::~RefCounted() +{} + + +} // end namespace LLCoreInt + + diff --git a/indra/llcorehttp/_refcounted.h b/indra/llcorehttp/_refcounted.h new file mode 100644 index 0000000000000000000000000000000000000000..a96c65fb6b90a6b7c6de2e746f056eff323a2432 --- /dev/null +++ b/indra/llcorehttp/_refcounted.h @@ -0,0 +1,125 @@ +/** + * @file _refcounted.h + * @brief Atomic, thread-safe ref counting and destruction mixin class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT__REFCOUNTED_H_ +#define LLCOREINT__REFCOUNTED_H_ + + +#include "linden_common.h" + +#include <boost/thread.hpp> + +#include "llapr.h" + + +namespace LLCoreInt +{ + + +class RefCounted +{ +private: + RefCounted(); // Not defined - may not be default constructed + void operator=(const RefCounted &); // Not defined + +public: + explicit RefCounted(bool const implicit) + : mRefCount(implicit) + {} + + // ref-count interface + void addRef() const; + void release() const; + bool isLastRef() const; + S32 getRefCount() const; + void noRef() const; + + static const S32 NOT_REF_COUNTED = -1; + +protected: + virtual ~RefCounted(); + virtual void destroySelf(); + +private: + mutable LLAtomicS32 mRefCount; + +}; // end class RefCounted + + +inline void RefCounted::addRef() const +{ + S32 count(mRefCount++); + llassert_always(count >= 0); +} + + +inline void RefCounted::release() const +{ + S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count > 0); + count = mRefCount--; + + // clean ourselves up if that was the last reference + if (0 == count) + { + const_cast<RefCounted *>(this)->destroySelf(); + } +} + + +inline bool RefCounted::isLastRef() const +{ + const S32 count(mRefCount); + llassert_always(count != NOT_REF_COUNTED); + llassert_always(count >= 1); + return (1 == count); +} + + +inline S32 RefCounted::getRefCount() const +{ + const S32 result(mRefCount); + return result; +} + + +inline void RefCounted::noRef() const +{ + llassert_always(mRefCount <= 1); + mRefCount = NOT_REF_COUNTED; +} + + +inline void RefCounted::destroySelf() +{ + delete this; +} + +} // end namespace LLCoreInt + +#endif // LLCOREINT__REFCOUNTED_H_ + diff --git a/indra/llcorehttp/_thread.h b/indra/llcorehttp/_thread.h new file mode 100644 index 0000000000000000000000000000000000000000..e058d660e5654700debfe9de6a31f6bcac6f12ff --- /dev/null +++ b/indra/llcorehttp/_thread.h @@ -0,0 +1,123 @@ +/** + * @file _thread.h + * @brief thread type abstraction + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef LLCOREINT_THREAD_H_ +#define LLCOREINT_THREAD_H_ + +#include "linden_common.h" + +#include <boost/thread.hpp> +#include <boost/function.hpp> +#include <boost/date_time/posix_time/posix_time_types.hpp> + +#include "_refcounted.h" + +namespace LLCoreInt +{ + +class HttpThread : public RefCounted +{ +private: + HttpThread(); // Not defined + void operator=(const HttpThread &); // Not defined + + void at_exit() + { + // the thread function has exited so we need to release our reference + // to ourself so that we will be automagically cleaned up. + release(); + } + + void run() + { // THREAD CONTEXT + + // Take out additional reference for the at_exit handler + addRef(); + boost::this_thread::at_thread_exit(boost::bind(&HttpThread::at_exit, this)); + + // run the thread function + mThreadFunc(this); + + } // THREAD CONTEXT + +protected: + virtual ~HttpThread() + { + delete mThread; + } + +public: + /// Constructs a thread object for concurrent execution but does + /// not start running. Caller receives on refcount on the thread + /// instance. If the thread is started, another will be taken + /// out for the exit handler. + explicit HttpThread(boost::function<void (HttpThread *)> threadFunc) + : RefCounted(true), // implicit reference + mThreadFunc(threadFunc) + { + // this creates a boost thread that will call HttpThread::run on this instance + // and pass it the threadfunc callable... + boost::function<void()> f = boost::bind(&HttpThread::run, this); + + mThread = new boost::thread(f); + } + + inline void join() + { + mThread->join(); + } + + inline bool timedJoin(S32 millis) + { + return mThread->timed_join(boost::posix_time::milliseconds(millis)); + } + + inline bool joinable() const + { + return mThread->joinable(); + } + + // A very hostile method to force a thread to quit + inline void cancel() + { + boost::thread::native_handle_type thread(mThread->native_handle()); +#if LL_WINDOWS + TerminateThread(thread, 0); +#else + pthread_cancel(thread); +#endif + } + +private: + boost::function<void(HttpThread *)> mThreadFunc; + boost::thread * mThread; +}; // end class HttpThread + +} // end namespace LLCoreInt + +#endif // LLCOREINT_THREAD_H_ + + diff --git a/indra/llcorehttp/bufferarray.cpp b/indra/llcorehttp/bufferarray.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8eaaeed7102385850725a02029f765895f03abd0 --- /dev/null +++ b/indra/llcorehttp/bufferarray.cpp @@ -0,0 +1,352 @@ +/** + * @file bufferarray.cpp + * @brief Implements the BufferArray scatter/gather buffer + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferarray.h" + + +// BufferArray is a list of chunks, each a BufferArray::Block, of contiguous +// data presented as a single array. Chunks are at least BufferArray::BLOCK_ALLOC_SIZE +// in length and can be larger. Any chunk may be partially filled or even +// empty. +// +// The BufferArray itself is sharable as a RefCounted entity. As shared +// reads don't work with the concept of a current position/seek value, +// none is kept with the object. Instead, the read and write operations +// all take position arguments. Single write/shared read isn't supported +// directly and any such attempts have to be serialized outside of this +// implementation. + +namespace LLCore +{ + + +// ================================== +// BufferArray::Block Declaration +// ================================== + +class BufferArray::Block +{ +public: + ~Block(); + + void operator delete(void *); + void operator delete(void *, size_t len); + +protected: + Block(size_t len); + + Block(const Block &); // Not defined + void operator=(const Block &); // Not defined + + // Allocate the block with the additional space for the + // buffered data at the end of the object. + void * operator new(size_t len, size_t addl_len); + +public: + // Only public entry to get a block. + static Block * alloc(size_t len); + +public: + size_t mUsed; + size_t mAlloced; + + // *NOTE: Must be last member of the object. We'll + // overallocate as requested via operator new and index + // into the array at will. + char mData[1]; +}; + + +// ================================== +// BufferArray Definitions +// ================================== + + +#if ! LL_WINDOWS +const size_t BufferArray::BLOCK_ALLOC_SIZE; +#endif // ! LL_WINDOWS + +BufferArray::BufferArray() + : LLCoreInt::RefCounted(true), + mLen(0) +{} + + +BufferArray::~BufferArray() +{ + for (container_t::iterator it(mBlocks.begin()); + it != mBlocks.end(); + ++it) + { + delete *it; + *it = NULL; + } + mBlocks.clear(); +} + + +size_t BufferArray::append(const void * src, size_t len) +{ + const size_t ret(len); + const char * c_src(static_cast<const char *>(src)); + + // First, try to copy into the last block + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], c_src, copy_len); + last.mUsed += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + } + + // Then get new blocks as needed + while (len) + { + const size_t copy_len((std::min)(len, BLOCK_ALLOC_SIZE)); + + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc(BLOCK_ALLOC_SIZE); + memcpy(block->mData, c_src, copy_len); + block->mUsed = copy_len; + llassert_always(block->mUsed <= block->mAlloced); + mBlocks.push_back(block); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + return ret; +} + + +void * BufferArray::appendBufferAlloc(size_t len) +{ + // If someone asks for zero-length, we give them a valid pointer. + if (mBlocks.size() >= mBlocks.capacity()) + { + mBlocks.reserve(mBlocks.size() + 5); + } + Block * block = Block::alloc((std::max)(BLOCK_ALLOC_SIZE, len)); + block->mUsed = len; + mBlocks.push_back(block); + mLen += len; + return block->mData; +} + + +size_t BufferArray::read(size_t pos, void * dst, size_t len) +{ + char * c_dst(static_cast<char *>(dst)); + + if (pos >= mLen) + return 0; + size_t len_limit(mLen - pos); + len = (std::min)(len, len_limit); + if (0 == len) + return 0; + + size_t result(0), offset(0); + const int block_limit(mBlocks.size()); + int block_start(findBlock(pos, &offset)); + if (block_start < 0) + return 0; + + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mUsed - offset); + size_t block_len((std::min)(block_limit, len)); + + memcpy(c_dst, &block.mData[offset], block_len); + result += block_len; + len -= block_len; + c_dst += block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + + return result; +} + + +size_t BufferArray::write(size_t pos, const void * src, size_t len) +{ + const char * c_src(static_cast<const char *>(src)); + + if (pos > mLen || 0 == len) + return 0; + + size_t result(0), offset(0); + const int block_limit(mBlocks.size()); + int block_start(findBlock(pos, &offset)); + + if (block_start >= 0) + { + // Some or all of the write will be on top of + // existing data. + do + { + Block & block(*mBlocks[block_start]); + size_t block_limit(block.mUsed - offset); + size_t block_len((std::min)(block_limit, len)); + + memcpy(&block.mData[offset], c_src, block_len); + result += block_len; + c_src += block_len; + len -= block_len; + offset = 0; + ++block_start; + } + while (len && block_start < block_limit); + } + + // Something left, see if it will fit in the free + // space of the last block. + if (len && ! mBlocks.empty()) + { + Block & last(*mBlocks.back()); + if (last.mUsed < last.mAlloced) + { + // Some will fit... + const size_t copy_len((std::min)(len, (last.mAlloced - last.mUsed))); + + memcpy(&last.mData[last.mUsed], c_src, copy_len); + last.mUsed += copy_len; + result += copy_len; + llassert_always(last.mUsed <= last.mAlloced); + mLen += copy_len; + c_src += copy_len; + len -= copy_len; + } + } + + if (len) + { + // Some or all of the remaining write data will + // be an append. + result += append(c_src, len); + } + + return result; +} + + +int BufferArray::findBlock(size_t pos, size_t * ret_offset) +{ + *ret_offset = 0; + if (pos >= mLen) + return -1; // Doesn't exist + + const int block_limit(mBlocks.size()); + for (int i(0); i < block_limit; ++i) + { + if (pos < mBlocks[i]->mUsed) + { + *ret_offset = pos; + return i; + } + pos -= mBlocks[i]->mUsed; + } + + // Shouldn't get here but... + return -1; +} + + +bool BufferArray::getBlockStartEnd(int block, const char ** start, const char ** end) +{ + if (block < 0 || block >= mBlocks.size()) + { + return false; + } + + const Block & b(*mBlocks[block]); + *start = &b.mData[0]; + *end = &b.mData[b.mUsed]; + return true; +} + + +// ================================== +// BufferArray::Block Definitions +// ================================== + + +BufferArray::Block::Block(size_t len) + : mUsed(0), + mAlloced(len) +{ + memset(mData, 0, len); +} + + +BufferArray::Block::~Block() +{ + mUsed = 0; + mAlloced = 0; +} + + +void * BufferArray::Block::operator new(size_t len, size_t addl_len) +{ + void * mem = new char[len + addl_len + sizeof(void *)]; + return mem; +} + + +void BufferArray::Block::operator delete(void * mem) +{ + char * cmem = static_cast<char *>(mem); + delete [] cmem; +} + + +void BufferArray::Block::operator delete(void * mem, size_t) +{ + operator delete(mem); +} + + +BufferArray::Block * BufferArray::Block::alloc(size_t len) +{ + Block * block = new (len) Block(len); + return block; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/bufferarray.h b/indra/llcorehttp/bufferarray.h new file mode 100644 index 0000000000000000000000000000000000000000..1094a435b42246a01b4bd93d76211557ea0fd1f3 --- /dev/null +++ b/indra/llcorehttp/bufferarray.h @@ -0,0 +1,137 @@ +/** + * @file bufferarray.h + * @brief Public-facing declaration for the BufferArray scatter/gather class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_ARRAY_H_ +#define _LLCORE_BUFFER_ARRAY_H_ + + +#include <cstdlib> +#include <vector> + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArrayStreamBuf; + +/// A very simple scatter/gather type map for bulk data. The motivation +/// for this class is the writedata callback used by libcurl. Response +/// bodies are delivered to the caller in a sequence of sequential write +/// operations and this class captures them without having to reallocate +/// and move data. +/// +/// The interface looks a little like a unix file descriptor but only +/// just. There is a notion of a current position, starting from 0, +/// which is used as the position in the data when performing read and +/// write operations. The position also moves after various operations: +/// - seek(...) +/// - read(...) +/// - write(...) +/// - append(...) +/// - appendBufferAlloc(...) +/// The object also keeps a total length value which is updated after +/// write and append operations and beyond which the current position +/// cannot be set. +/// +/// Threading: not thread-safe +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a single refcount. +/// +class BufferArray : public LLCoreInt::RefCounted +{ +public: + // BufferArrayStreamBuf has intimate knowledge of this + // implementation to implement a buffer-free adapter. + // Changes here will likely need to be reflected there. + friend class BufferArrayStreamBuf; + + BufferArray(); + +protected: + virtual ~BufferArray(); // Use release() + +private: + BufferArray(const BufferArray &); // Not defined + void operator=(const BufferArray &); // Not defined + +public: + // Internal magic number, may be used by unit tests. + static const size_t BLOCK_ALLOC_SIZE = 65540; + + /// Appends the indicated data to the BufferArray + /// modifying current position and total size. New + /// position is one beyond the final byte of the buffer. + /// + /// @return Count of bytes copied to BufferArray + size_t append(const void * src, size_t len); + + /// Similar to @see append(), this call guarantees a + /// contiguous block of memory of requested size placed + /// at the current end of the BufferArray. On return, + /// the data in the memory is considered valid whether + /// the caller writes to it or not. + /// + /// @return Pointer to contiguous region at end + /// of BufferArray of 'len' size. + void * appendBufferAlloc(size_t len); + + /// Current count of bytes in BufferArray instance. + size_t size() const + { + return mLen; + } + + /// Copies data from the given position in the instance + /// to the caller's buffer. Will return a short count of + /// bytes copied if the 'len' extends beyond the data. + size_t read(size_t pos, void * dst, size_t len); + + /// Copies data from the caller's buffer to the instance + /// at the current position. May overwrite existing data, + /// append data when current position is equal to the + /// size of the instance or do a mix of both. + size_t write(size_t pos, const void * src, size_t len); + +protected: + int findBlock(size_t pos, size_t * ret_offset); + + bool getBlockStartEnd(int block, const char ** start, const char ** end); + +protected: + class Block; + typedef std::vector<Block *> container_t; + + container_t mBlocks; + size_t mLen; +}; // end class BufferArray + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/bufferstream.cpp b/indra/llcorehttp/bufferstream.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6553900eef524dd58ecdc5e890ba3039f34e909e --- /dev/null +++ b/indra/llcorehttp/bufferstream.cpp @@ -0,0 +1,285 @@ +/** + * @file bufferstream.cpp + * @brief Implements the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "bufferstream.h" + +#include "bufferarray.h" + + +namespace LLCore +{ + +BufferArrayStreamBuf::BufferArrayStreamBuf(BufferArray * array) + : mBufferArray(array), + mReadCurPos(0), + mReadCurBlock(-1), + mReadBegin(NULL), + mReadCur(NULL), + mReadEnd(NULL), + mWriteCurPos(0) +{ + if (array) + { + array->addRef(); + mWriteCurPos = array->mLen; + } +} + + +BufferArrayStreamBuf::~BufferArrayStreamBuf() +{ + if (mBufferArray) + { + mBufferArray->release(); + mBufferArray = NULL; + } +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::underflow() +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadEnd) + { + // Find the next block with actual data or leave + // mCurBlock/mCur/mEnd unchanged if we're at the end + // of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock + 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + ++new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = mReadCur = new_begin; + mReadEnd = new_end; + } + + return traits_type::to_int_type(*mReadCur); +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::uflow() +{ + const int_type ret(underflow()); + + if (traits_type::eof() != ret) + { + ++mReadCur; + ++mReadCurPos; + } + return ret; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::pbackfail(int_type ch) +{ + if (! mBufferArray) + { + return traits_type::eof(); + } + + if (mReadCur == mReadBegin) + { + // Find the previous block with actual data or leave + // mCurBlock/mBegin/mCur/mEnd unchanged if we're at the + // beginning of any block chain. + const char * new_begin(NULL), * new_end(NULL); + int new_cur_block(mReadCurBlock - 1); + + while (mBufferArray->getBlockStartEnd(new_cur_block, &new_begin, &new_end)) + { + if (new_begin != new_end) + { + break; + } + --new_cur_block; + } + if (new_begin == new_end) + { + return traits_type::eof(); + } + + mReadCurBlock = new_cur_block; + mReadBegin = new_begin; + mReadEnd = mReadCur = new_end; + } + + if (traits_type::eof() != ch && mReadCur[-1] != ch) + { + return traits_type::eof(); + } + --mReadCurPos; + return traits_type::to_int_type(*--mReadCur); +} + + +std::streamsize BufferArrayStreamBuf::showmanyc() +{ + if (! mBufferArray) + { + return -1; + } + return mBufferArray->mLen - mReadCurPos; +} + + +BufferArrayStreamBuf::int_type BufferArrayStreamBuf::overflow(int c) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return traits_type::eof(); + } + const size_t wrote(mBufferArray->write(mWriteCurPos, &c, 1)); + mWriteCurPos += wrote; + return wrote ? c : traits_type::eof(); +} + + +std::streamsize BufferArrayStreamBuf::xsputn(const char * src, std::streamsize count) +{ + if (! mBufferArray || mWriteCurPos > mBufferArray->mLen) + { + return 0; + } + const size_t wrote(mBufferArray->write(mWriteCurPos, src, count)); + mWriteCurPos += wrote; + return wrote; +} + + +std::streampos BufferArrayStreamBuf::seekoff(std::streamoff off, + std::ios_base::seekdir way, + std::ios_base::openmode which) +{ + std::streampos ret(-1); + + if (! mBufferArray) + { + return ret; + } + + if (std::ios_base::in == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mReadCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos >= mBufferArray->size()) + { + pos = (std::max)(size_t(0), mBufferArray->size() - 1); + } + size_t ba_offset(0); + int block(mBufferArray->findBlock(pos, &ba_offset)); + if (block < 0) + return ret; + const char * start(NULL), * end(NULL); + if (! mBufferArray->getBlockStartEnd(block, &start, &end)) + return ret; + mReadCurBlock = block; + mReadBegin = start; + mReadCur = start + ba_offset; + mReadEnd = end; + ret = mReadCurPos = pos; + } + else if (std::ios_base::out == which) + { + size_t pos(0); + + switch (way) + { + case std::ios_base::beg: + pos = off; + break; + + case std::ios_base::cur: + pos = mWriteCurPos += off; + break; + + case std::ios_base::end: + pos = mBufferArray->mLen - off; + break; + + default: + return ret; + } + + if (pos < 0) + return ret; + if (pos > mBufferArray->size()) + { + pos = mBufferArray->size(); + } + ret = mWriteCurPos = pos; + } + + return ret; +} + + +BufferArrayStream::BufferArrayStream(BufferArray * ba) + : std::iostream(&mStreamBuf), + mStreamBuf(ba) +{} + + +BufferArrayStream::~BufferArrayStream() +{} + + +} // end namespace LLCore + + diff --git a/indra/llcorehttp/bufferstream.h b/indra/llcorehttp/bufferstream.h new file mode 100644 index 0000000000000000000000000000000000000000..9327a798aa90cabf374c32e501525d7db1742570 --- /dev/null +++ b/indra/llcorehttp/bufferstream.h @@ -0,0 +1,153 @@ +/** + * @file bufferstream.h + * @brief Public-facing declaration for the BufferStream adapter class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_BUFFER_STREAM_H_ +#define _LLCORE_BUFFER_STREAM_H_ + + +#include <sstream> +#include <cstdlib> + +#include "bufferarray.h" + + +/// @file bufferstream.h +/// +/// std::streambuf and std::iostream adapters for BufferArray +/// objects. +/// +/// BufferArrayStreamBuf inherits std::streambuf and implements +/// an unbuffered interface for streambuf. This may or may not +/// be the most time efficient implementation and it is a little +/// challenging. +/// +/// BufferArrayStream inherits std::iostream and will be the +/// adapter object most callers will be interested in (though +/// it uses BufferArrayStreamBuf internally). Instances allow +/// for the usual streaming operators ('<<', '>>') and serialization +/// methods. +/// +/// Example of LLSD serialization to a BufferArray: +/// +/// BufferArray * ba = new BufferArray; +/// BufferArrayStream bas(ba); +/// LLSDSerialize::toXML(llsd, bas); +/// operationOnBufferArray(ba); +/// ba->release(); +/// ba = NULL; +/// // operationOnBufferArray and bas are each holding +/// // references to the ba instance at this point. +/// + +namespace LLCore +{ + + +// ===================================================== +// BufferArrayStreamBuf +// ===================================================== + +/// Adapter class to put a std::streambuf interface on a BufferArray +/// +/// Application developers will rarely be interested in anything +/// other than the constructor and even that will rarely be used +/// except indirectly via the @BufferArrayStream class. The +/// choice of interfaces implemented yields a bufferless adapter +/// that doesn't used either the input or output pointer triplets +/// of the more common buffered implementations. This may or may +/// not be faster and that question could stand to be looked at +/// sometime. +/// + +class BufferArrayStreamBuf : public std::streambuf +{ +public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. + BufferArrayStreamBuf(BufferArray * array); + virtual ~BufferArrayStreamBuf(); + +private: + BufferArrayStreamBuf(const BufferArrayStreamBuf &); // Not defined + void operator=(const BufferArrayStreamBuf &); // Not defined + +public: + // Input interfaces from std::streambuf + int_type underflow(); + int_type uflow(); + int_type pbackfail(int_type ch); + std::streamsize showmanyc(); + + // Output interfaces from std::streambuf + int_type overflow(int c); + std::streamsize xsputn(const char * src, std::streamsize count); + + // Common/misc interfaces from std::streambuf + std::streampos seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which); + +protected: + BufferArray * mBufferArray; // Ref counted + size_t mReadCurPos; + int mReadCurBlock; + const char * mReadBegin; + const char * mReadCur; + const char * mReadEnd; + size_t mWriteCurPos; + +}; // end class BufferArrayStreamBuf + + +// ===================================================== +// BufferArrayStream +// ===================================================== + +/// Adapter class that supplies streaming operators to BufferArray +/// +/// Provides a streaming adapter to an existing BufferArray +/// instance so that the convenient '<<' and '>>' conversions +/// can be applied to a BufferArray. Very convenient for LLSD +/// serialization and parsing as well. + +class BufferArrayStream : public std::iostream +{ +public: + /// Constructor increments the reference count on the + /// BufferArray argument and calls release() on destruction. + BufferArrayStream(BufferArray * ba); + ~BufferArrayStream(); + +protected: + BufferArrayStream(const BufferArrayStream &); + void operator=(const BufferArrayStream &); + +protected: + BufferArrayStreamBuf mStreamBuf; +}; // end class BufferArrayStream + + +} // end namespace LLCore + +#endif // _LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/examples/http_texture_load.cpp b/indra/llcorehttp/examples/http_texture_load.cpp new file mode 100644 index 0000000000000000000000000000000000000000..998dc9240b19ee34eed2ff53da0d516fedb306e8 --- /dev/null +++ b/indra/llcorehttp/examples/http_texture_load.cpp @@ -0,0 +1,943 @@ +/** + * @file http_texture_load.cpp + * @brief Texture download example for core-http library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include <iostream> +#include <cstdio> +#include <cstdlib> +#include <set> +#include <map> +#if !defined(WIN32) +#include <pthread.h> +#endif + +#include "linden_common.h" + +#include "httpcommon.h" +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "httpheaders.h" +#include "bufferarray.h" +#include "_mutex.h" + +#include <curl/curl.h> +#include <openssl/crypto.h> + +#include "lltimer.h" + + +void init_curl(); +void term_curl(); +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); +void usage(std::ostream & out); + +// Default command line settings +static int concurrency_limit(40); +static char url_format[1024] = "http://example.com/some/path?texture_id=%s.texture"; + +#if defined(WIN32) + +#define strncpy(_a, _b, _c) strncpy_s(_a, _b, _c) +#define strtok_r(_a, _b, _c) strtok_s(_a, _b, _c) + +int getopt(int argc, char * const argv[], const char *optstring); +char *optarg(NULL); +int optind(1); + +#endif + + +// Mostly just a container for the texture IDs and fetch +// parameters.... +class WorkingSet : public LLCore::HttpHandler +{ +public: + WorkingSet(); + ~WorkingSet(); + + bool reload(LLCore::HttpRequest *); + + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + void loadTextureUuids(FILE * in); + +public: + struct Spec + { + std::string mUuid; + int mOffset; + int mLength; + }; + typedef std::set<LLCore::HttpHandle> handle_set_t; + typedef std::vector<Spec> texture_list_t; + +public: + bool mVerbose; + bool mRandomRange; + int mMaxConcurrency; + handle_set_t mHandles; + int mRemaining; + int mLimit; + int mAt; + std::string mUrl; + texture_list_t mTextures; + int mErrorsApi; + int mErrorsHttp; + int mErrorsHttp404; + int mErrorsHttp416; + int mErrorsHttp500; + int mErrorsHttp503; + int mSuccesses; + long mByteCount; + LLCore::HttpHeaders * mHeaders; +}; + + +// Gather process information while we run. Process +// size, cpu consumed, wallclock time. + +class Metrics +{ +public: + class MetricsImpl; + +public: + Metrics(); + ~Metrics(); + + void init(); + void sample(); + void term(); + +protected: + MetricsImpl * mImpl; + +public: + U64 mMaxVSZ; + U64 mMinVSZ; + U64 mStartWallTime; + U64 mEndWallTime; + U64 mStartUTime; + U64 mEndUTime; + U64 mStartSTime; + U64 mEndSTime; +}; + + +// +// +// +int main(int argc, char** argv) +{ + bool do_random(false); + bool do_verbose(false); + + int option(-1); + while (-1 != (option = getopt(argc, argv, "u:c:h?Rv"))) + { + switch (option) + { + case 'u': + strncpy(url_format, optarg, sizeof(url_format)); + url_format[sizeof(url_format) - 1] = '\0'; + break; + + case 'c': + { + unsigned long value; + char * end; + + value = strtoul(optarg, &end, 10); + if (value < 1 || value > 100 || *end != '\0') + { + usage(std::cerr); + return 1; + } + concurrency_limit = value; + } + break; + + case 'R': + do_random = true; + break; + + case 'v': + do_verbose = true; + break; + + case 'h': + case '?': + usage(std::cout); + return 0; + } + } + + if ((optind + 1) != argc) + { + usage(std::cerr); + return 1; + } + + FILE * uuids(fopen(argv[optind], "r")); + if (! uuids) + { + const char * errstr(strerror(errno)); + + std::cerr << "Couldn't open UUID file '" << argv[optind] << "'. Reason: " + << errstr << std::endl; + return 1; + } + + // Initialization + init_curl(); + LLCore::HttpRequest::createService(); + LLCore::HttpRequest::startThread(); + + // Get service point + LLCore::HttpRequest * hr = new LLCore::HttpRequest(); + + // Get a handler/working set + WorkingSet ws; + + // Fill the working set with work + ws.mUrl = url_format; + ws.loadTextureUuids(uuids); + ws.mRandomRange = do_random; + ws.mVerbose = do_verbose; + ws.mMaxConcurrency = concurrency_limit; + + if (! ws.mTextures.size()) + { + std::cerr << "No UUIDs found in file '" << argv[optind] << "'." << std::endl; + return 1; + } + + // Setup metrics + Metrics metrics; + metrics.init(); + + // Run it + int passes(0); + while (! ws.reload(hr)) + { + hr->update(5000000); + ms_sleep(2); + if (0 == (++passes % 200)) + { + metrics.sample(); + } + } + metrics.sample(); + metrics.term(); + + // Report + std::cout << "HTTP errors: " << ws.mErrorsHttp << " API errors: " << ws.mErrorsApi + << " Successes: " << ws.mSuccesses << " Byte count: " << ws.mByteCount + << std::endl; + std::cout << "HTTP 404 errors: " << ws.mErrorsHttp404 << " HTTP 416 errors: " << ws.mErrorsHttp416 + << " HTTP 500 errors: " << ws.mErrorsHttp500 << " HTTP 503 errors: " << ws.mErrorsHttp503 + << std::endl; + std::cout << "User CPU: " << (metrics.mEndUTime - metrics.mStartUTime) + << " uS System CPU: " << (metrics.mEndSTime - metrics.mStartSTime) + << " uS Wall Time: " << (metrics.mEndWallTime - metrics.mStartWallTime) + << " uS Maximum VSZ: " << metrics.mMaxVSZ + << " Bytes Minimum VSZ: " << metrics.mMinVSZ << " Bytes" + << std::endl; + + // Clean up + hr->requestStopThread(NULL); + ms_sleep(1000); + delete hr; + LLCore::HttpRequest::destroyService(); + term_curl(); + + return 0; +} + + +void usage(std::ostream & out) +{ + out << "\n" + "usage:\thttp_texture_load [options] uuid_file\n" + "\n" + "This is a standalone program to drive the New Platform HTTP Library.\n" + "The program is supplied with a file of texture UUIDs, one per line\n" + "These are fetched sequentially using a pool of concurrent connection\n" + "until all are fetched. The default URL format is only useful from\n" + "within Linden Lab but this can be overriden with a printf-style\n" + "URL formatting string on the command line.\n" + "\n" + "Options:\n" + "\n" + " -u <url_format> printf-style format string for URL generation\n" + " Default: " << url_format << "\n" + " -R Issue GETs with random Range: headers\n" + " -c <limit> Maximum request concurrency. Range: [1..100]\n" + " Default: " << concurrency_limit << "\n" + " -v Verbose mode. Issue some chatter while running\n" + " -h print this help\n" + "\n" + << std::endl; +} + + +WorkingSet::WorkingSet() + : LLCore::HttpHandler(), + mVerbose(false), + mRandomRange(false), + mRemaining(200), + mLimit(200), + mAt(0), + mErrorsApi(0), + mErrorsHttp(0), + mErrorsHttp404(0), + mErrorsHttp416(0), + mErrorsHttp500(0), + mErrorsHttp503(0), + mSuccesses(0), + mByteCount(0L) +{ + mTextures.reserve(30000); + + mHeaders = new LLCore::HttpHeaders; + mHeaders->mHeaders.push_back("Accept: image/x-j2c"); +} + + +WorkingSet::~WorkingSet() +{ + if (mHeaders) + { + mHeaders->release(); + mHeaders = NULL; + } +} + + +bool WorkingSet::reload(LLCore::HttpRequest * hr) +{ + int to_do((std::min)(mRemaining, mMaxConcurrency - int(mHandles.size()))); + + for (int i(0); i < to_do; ++i) + { + char buffer[1024]; +#if defined(WIN32) + _snprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#else + snprintf(buffer, sizeof(buffer), mUrl.c_str(), mTextures[mAt].mUuid.c_str()); +#endif + int offset(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mOffset); + int length(mRandomRange ? ((unsigned long) rand()) % 1000000UL : mTextures[mAt].mLength); + + LLCore::HttpHandle handle; + if (offset || length) + { + handle = hr->requestGetByteRange(0, 0, buffer, offset, length, NULL, mHeaders, this); + } + else + { + handle = hr->requestGet(0, 0, buffer, NULL, mHeaders, this); + } + if (! handle) + { + // Fatal. Couldn't queue up something. + std::cerr << "Failed to queue work to HTTP Service. Reason: " + << hr->getStatus().toString() << std::endl; + exit(1); + } + else + { + mHandles.insert(handle); + } + mAt++; + mRemaining--; + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Queued " << count << std::endl; + } + } + + // Are we done? + return (! mRemaining) && mHandles.empty(); +} + + +void WorkingSet::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_set_t::iterator it(mHandles.find(handle)); + if (mHandles.end() == it) + { + // Wha? + std::cerr << "Failed to find handle in request list. Fatal." << std::endl; + exit(1); + } + else + { + LLCore::HttpStatus status(response->getStatus()); + if (status) + { + // More success + LLCore::BufferArray * data(response->getBody()); + mByteCount += data->size(); + ++mSuccesses; + } + else + { + // Something in this library or libcurl + if (status.isHttpStatus()) + { + static const LLCore::HttpStatus hs404(404); + static const LLCore::HttpStatus hs416(416); + static const LLCore::HttpStatus hs500(500); + static const LLCore::HttpStatus hs503(503); + + ++mErrorsHttp; + if (hs404 == status) + { + ++mErrorsHttp404; + } + else if (hs416 == status) + { + ++mErrorsHttp416; + } + else if (hs500 == status) + { + ++mErrorsHttp500; + } + else if (hs503 == status) + { + ++mErrorsHttp503; + } + } + else + { + ++mErrorsApi; + } + } + mHandles.erase(it); + } + + if (mVerbose) + { + static int count(0); + ++count; + if (0 == (count %5)) + std::cout << "Handled " << count << std::endl; + } +} + + +void WorkingSet::loadTextureUuids(FILE * in) +{ + char buffer[1024]; + + while (fgets(buffer, sizeof(buffer), in)) + { + WorkingSet::Spec texture; + char * state(NULL); + char * token = strtok_r(buffer, " \t\n,", &state); + if (token && 36 == strlen(token)) + { + // Close enough for this function + texture.mUuid = token; + texture.mOffset = 0; + texture.mLength = 0; + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int offset(atoi(token)); + token = strtok_r(buffer, " \t\n,", &state); + if (token) + { + int length(atoi(token)); + texture.mOffset = offset; + texture.mLength = length; + } + } + mTextures.push_back(texture); + } + } + mRemaining = mLimit = mTextures.size(); +} + + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } +} + + +void term_curl() +{ + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +#if defined(WIN32) + +// Very much a subset of posix functionality. Don't push +// it too hard... +int getopt(int argc, char * const argv[], const char *optstring) +{ + static int pos(0); + while (optind < argc) + { + if (pos == 0) + { + if (argv[optind][0] != '-') + return -1; + pos = 1; + } + if (! argv[optind][pos]) + { + ++optind; + pos = 0; + continue; + } + const char * thing(strchr(optstring, argv[optind][pos])); + if (! thing) + { + ++optind; + return -1; + } + if (thing[1] == ':') + { + optarg = argv[++optind]; + ++optind; + pos = 0; + } + else + { + optarg = NULL; + ++pos; + } + return *thing; + } + return -1; +} + +#endif + + + +#if LL_WINDOWS + +#define PSAPI_VERSION 1 +#include "windows.h" +#include "psapi.h" + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void init(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mStartSTime = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mStartUTime = uli.QuadPart / U64L(10); + metrics->mStartWallTime = totalTime(); + } + + void sample(Metrics * metrics) + { + PROCESS_MEMORY_COUNTERS_EX counters; + + GetProcessMemoryInfo(GetCurrentProcess(), + (PROCESS_MEMORY_COUNTERS *) &counters, + sizeof(counters)); + // Okay, PrivateUsage isn't truly VSZ but it will be + // a good tracker for leaks and fragmentation. Work on + // a better estimator later... + SIZE_T vsz(counters.PrivateUsage); + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, U64(vsz)); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, U64(vsz)); + } + + void term(Metrics * metrics) + { + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + metrics->mEndSTime = uli.QuadPart / U64L(10); + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + metrics->mEndUTime = uli.QuadPart / U64L(10); + metrics->mEndWallTime = totalTime(); + } + +protected: +}; + +#elif LL_DARWIN + +#include <sys/resource.h> +#include <mach/mach.h> + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + {} + + ~MetricsImpl() + {} + + void init(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + sample(metrics); + } + + void sample(Metrics * metrics) + { + U64 vsz; + + if (getVM(&vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + void term(Metrics * metrics) + { + U64 utime, stime; + + if (getTimes(&utime, &stime)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + } + +protected: + bool getVM(U64 * vsz) + { + task_basic_info task_info_block; + mach_msg_type_number_t task_info_count(TASK_BASIC_INFO_COUNT); + + if (KERN_SUCCESS != task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &task_info_block, + &task_info_count)) + { + return false; + } + * vsz = task_info_block.virtual_size; + return true; + } + + bool getTimes(U64 * utime, U64 * stime) + { + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + return false; + } + * utime = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + * stime = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + return true; + } + +}; + +#else + +class Metrics::MetricsImpl +{ +public: + MetricsImpl() + : mProcFS(NULL), + mUsecsPerTick(U64L(0)) + {} + + + ~MetricsImpl() + { + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + + void init(Metrics * metrics) + { + if (! mProcFS) + { + mProcFS = fopen("/proc/self/stat", "r"); + if (! mProcFS) + { + const int errnum(errno); + LL_ERRS("Main") << "Error opening proc fs: " << strerror(errnum) << LL_ENDL; + } + } + + long ticks_per_sec(sysconf(_SC_CLK_TCK)); + mUsecsPerTick = U64L(1000000) / ticks_per_sec; + U64 usecs_per_sec(mUsecsPerTick * ticks_per_sec); + if (900000 > usecs_per_sec || 1100000 < usecs_per_sec) + { + LL_ERRS("Main") << "Resolution problems using uSecs for ticks" << LL_ENDL; + } + + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mStartSTime = stime; + metrics->mStartUTime = utime; + } + metrics->mStartWallTime = totalTime(); + + sample(metrics); + } + + + void sample(Metrics * metrics) + { + U64 vsz; + if (scanProcFS(NULL, NULL, &vsz)) + { + metrics->mMaxVSZ = (std::max)(metrics->mMaxVSZ, vsz); + metrics->mMinVSZ = (std::min)(metrics->mMinVSZ, vsz); + } + } + + + void term(Metrics * metrics) + { + U64 utime, stime; + if (scanProcFS(&utime, &stime, NULL)) + { + metrics->mEndSTime = stime; + metrics->mEndUTime = utime; + } + metrics->mEndWallTime = totalTime(); + + sample(metrics); + + if (mProcFS) + { + fclose(mProcFS); + mProcFS = NULL; + } + } + +protected: + bool scanProcFS(U64 * utime, U64 * stime, U64 * vsz) + { + if (mProcFS) + { + int i_dummy; + unsigned int ui_dummy; + unsigned long ul_dummy, user_ticks, sys_ticks, vsize; + long l_dummy, rss; + unsigned long long ull_dummy; + char c_dummy; + + char buffer[256]; + + static const char * format("%d %*s %c %d %d %d %d %d %u %lu %lu %lu %lu %lu %lu %ld %ld %ld %ld %ld %ld %llu %lu %ld"); + + fseek(mProcFS, 0L, SEEK_SET); + size_t len = fread(buffer, 1, sizeof(buffer) - 1, mProcFS); + if (! len) + { + return false; + } + buffer[len] = '\0'; + if (23 == sscanf(buffer, format, + &i_dummy, // pid + // &s_dummy, // command name + &c_dummy, // state + &i_dummy, // ppid + &i_dummy, // pgrp + &i_dummy, // session + &i_dummy, // terminal + &i_dummy, // terminal group id + &ui_dummy, // flags + &ul_dummy, // minor faults + &ul_dummy, // minor faults in children + &ul_dummy, // major faults + &ul_dummy, // major faults in children + &user_ticks, + &sys_ticks, + &l_dummy, // cutime + &l_dummy, // cstime + &l_dummy, // process priority + &l_dummy, // nice value + &l_dummy, // thread count + &l_dummy, // time to SIGALRM + &ull_dummy, // start time + &vsize, + &rss)) + { + // Looks like we understand the line + if (utime) + { + *utime = user_ticks * mUsecsPerTick; + } + + if (stime) + { + *stime = sys_ticks * mUsecsPerTick; + } + + if (vsz) + { + *vsz = vsize; + } + return true; + } + } + return false; + } + +protected: + FILE * mProcFS; + U64 mUsecsPerTick; + +}; + + +#endif // LL_WINDOWS + +Metrics::Metrics() + : mMaxVSZ(U64(0)), + mMinVSZ(U64L(0xffffffffffffffff)), + mStartWallTime(U64(0)), + mEndWallTime(U64(0)), + mStartUTime(U64(0)), + mEndUTime(U64(0)), + mStartSTime(U64(0)), + mEndSTime(U64(0)) +{ + mImpl = new MetricsImpl(); +} + + +Metrics::~Metrics() +{ + delete mImpl; + mImpl = NULL; +} + + +void Metrics::init() +{ + mImpl->init(this); +} + + +void Metrics::sample() +{ + mImpl->sample(this); +} + + +void Metrics::term() +{ + mImpl->term(this); +} + + diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f2fcbf77a3b66f76c4dd472efead2184a18b2edd --- /dev/null +++ b/indra/llcorehttp/httpcommon.cpp @@ -0,0 +1,179 @@ +/** + * @file httpcommon.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpcommon.h" + +#include <curl/curl.h> +#include <string> +#include <sstream> + + +namespace LLCore +{ + +HttpStatus::type_enum_t EXT_CURL_EASY; +HttpStatus::type_enum_t EXT_CURL_MULTI; +HttpStatus::type_enum_t LLCORE; + +HttpStatus::operator unsigned long() const +{ + static const int shift(sizeof(unsigned long) * 4); + + unsigned long result(((unsigned long) mType) << shift | (unsigned long) (int) mStatus); + return result; +} + + +std::string HttpStatus::toHex() const +{ + std::ostringstream result; + result.width(8); + result.fill('0'); + result << std::hex << operator unsigned long(); + return result.str(); +} + + +std::string HttpStatus::toString() const +{ + static const char * llcore_errors[] = + { + "", + "HTTP error reply status", + "Services shutting down", + "Operation canceled", + "Invalid Content-Range header encountered", + "Request handle not found", + "Invalid datatype for argument or option", + "Option has not been explicitly set", + "Option is not dynamic and must be set early", + "Invalid HTTP status code received from server" + }; + static const int llcore_errors_count(sizeof(llcore_errors) / sizeof(llcore_errors[0])); + + static const struct + { + type_enum_t mCode; + const char * mText; + } + http_errors[] = + { + // Keep sorted by mCode, we binary search this list. + { 100, "Continue" }, + { 101, "Switching Protocols" }, + { 200, "OK" }, + { 201, "Created" }, + { 202, "Accepted" }, + { 203, "Non-Authoritative Information" }, + { 204, "No Content" }, + { 205, "Reset Content" }, + { 206, "Partial Content" }, + { 300, "Multiple Choices" }, + { 301, "Moved Permanently" }, + { 302, "Found" }, + { 303, "See Other" }, + { 304, "Not Modified" }, + { 305, "Use Proxy" }, + { 307, "Temporary Redirect" }, + { 400, "Bad Request" }, + { 401, "Unauthorized" }, + { 402, "Payment Required" }, + { 403, "Forbidden" }, + { 404, "Not Found" }, + { 405, "Method Not Allowed" }, + { 406, "Not Acceptable" }, + { 407, "Proxy Authentication Required" }, + { 408, "Request Time-out" }, + { 409, "Conflict" }, + { 410, "Gone" }, + { 411, "Length Required" }, + { 412, "Precondition Failed" }, + { 413, "Request Entity Too Large" }, + { 414, "Request-URI Too Large" }, + { 415, "Unsupported Media Type" }, + { 416, "Requested range not satisfiable" }, + { 417, "Expectation Failed" }, + { 500, "Internal Server Error" }, + { 501, "Not Implemented" }, + { 502, "Bad Gateway" }, + { 503, "Service Unavailable" }, + { 504, "Gateway Time-out" }, + { 505, "HTTP Version not supported" } + }; + static const int http_errors_count(sizeof(http_errors) / sizeof(http_errors[0])); + + if (*this) + { + return std::string(""); + } + switch (mType) + { + case EXT_CURL_EASY: + return std::string(curl_easy_strerror(CURLcode(mStatus))); + + case EXT_CURL_MULTI: + return std::string(curl_multi_strerror(CURLMcode(mStatus))); + + case LLCORE: + if (mStatus >= 0 && mStatus < llcore_errors_count) + { + return std::string(llcore_errors[mStatus]); + } + break; + + default: + if (isHttpStatus()) + { + // Binary search for the error code and string + int bottom(0), top(http_errors_count); + while (true) + { + int at((bottom + top) / 2); + if (mType == http_errors[at].mCode) + { + return std::string(http_errors[at].mText); + } + if (at == bottom) + { + break; + } + else if (mType < http_errors[at].mCode) + { + top = at; + } + else + { + bottom = at; + } + } + } + break; + } + return std::string("Unknown error"); +} + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h new file mode 100644 index 0000000000000000000000000000000000000000..c0d4ec5aada659b00a0af8e5f0f743cb0be695b4 --- /dev/null +++ b/indra/llcorehttp/httpcommon.h @@ -0,0 +1,311 @@ +/** + * @file httpcommon.h + * @brief Public-facing declarations and definitions of common types + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_COMMON_H_ +#define _LLCORE_HTTP_COMMON_H_ + +/// @package LLCore::HTTP +/// +/// This library implements a high-level, Indra-code-free client interface to +/// HTTP services based on actual patterns found in the viewer and simulator. +/// Interfaces are similar to those supplied by the legacy classes +/// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that +/// allows an application to specify connection behaviors: limits on +/// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. +/// +/// Features of the library include: +/// - Single, private working thread where all transport and processing occurs. +/// - Support for multiple consumers running in multiple threads. +/// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. +/// - Reference counting used for many object instance lifetimes. +/// - Minimal data sharing across threads for correctness and low latency. +/// +/// The public interface is declared in a few key header files: +/// - "llcorehttp/bufferarray.h" +/// - "llcorehttp/httpcommon.h" +/// - "llcorehttp/httphandler.h" +/// - "llcorehttp/httpheaders.h" +/// - "llcorehttp/httpoptions.h" +/// - "llcorehttp/httprequest.h" +/// - "llcorehttp/httpresponse.h" +/// +/// The library is still under early development and particular users +/// may need access to internal implementation details that are found +/// in the _*.h header files. But this is a crutch to be avoided if at +/// all possible and probably indicates some interface work is neeeded. +/// +/// Using the library is fairly easy. Global setup needs a few +/// steps: +/// +/// - libcurl initialization including thread-safely callbacks for SSL: +/// . curl_global_init(...) +/// . CRYPTO_set_locking_callback(...) +/// . CRYPTO_set_id_callback(...) +/// - HttpRequest::createService() called to instantiate singletons +/// and support objects. +/// +/// An HTTP consumer in an application, and an application may have many +/// consumers, does a few things: +/// +/// - Instantiate and retain an object based on HttpRequest. This +/// object becomes the portal into runtime services for the consumer. +/// - Derive or mixin the HttpHandler class if you want notification +/// when requests succeed or fail. This object's onCompleted() +/// method is invoked and an instance can be shared across +/// requests. +/// +/// Issuing a request is straightforward: +/// - Construct a suitable URL. +/// - Configure HTTP options for the request. (optional) +/// - Build a list of additional headers. (optional) +/// - Invoke one of the requestXXXX() methods (requestGetByteRange, +/// requestPost, etc.) on the HttpRequest instance supplying the +/// above along with a policy class, a priority and an optional +/// pointer to an HttpHandler instance. Work is then queued to +/// the worker thread and occurs asynchronously. +/// - Periodically invoke the update() method on the HttpRequest +/// instance which performs completion notification to HttpHandler +/// objects. +/// - Do completion processing in your onCompletion() method. +/// +/// Code fragments: +/// Rather than a poorly-maintained example in comments, look in the +/// example subdirectory which is a minimal yet functional tool to do +/// GET request performance testing. With four calls: +/// +/// init_curl(); +/// LLCore::HttpRequest::createService(); +/// LLCore::HttpRequest::startThread(); +/// LLCore::HttpRequest * hr = new LLCore::HttpRequest(); +/// +/// the program is basically ready to issue requests. +/// + + +#include "linden_common.h" // Modifies curl/curl.h interfaces + +#include <string> + + +namespace LLCore +{ + + +/// All queued requests are represented by an HttpHandle value. +/// The invalid value is returned when a request failed to queue. +/// The actual status for these failures is then fetched with +/// HttpRequest::getStatus(). +/// +/// The handle is valid only for the life of a request. On +/// return from any HttpHandler notification, the handle immediately +/// becomes invalid and may be recycled for other queued requests. + +typedef void * HttpHandle; +#define LLCORE_HTTP_HANDLE_INVALID (NULL) + +/// For internal scheduling and metrics, we use a microsecond +/// timebase compatible with the environment. +typedef U64 HttpTime; + +/// Error codes defined by the library itself as distinct from +/// libcurl (or any other transport provider). +enum HttpError +{ + // Successful value compatible with the libcurl codes. + HE_SUCCESS = 0, + + // Intended for HTTP reply codes 100-999, indicates that + // the reply should be considered an error by the application. + HE_REPLY_ERROR = 1, + + // Service is shutting down and requested operation will + // not be queued or performed. + HE_SHUTTING_DOWN = 2, + + // Operation was canceled by request. + HE_OP_CANCELED = 3, + + // Invalid content range header received. + HE_INV_CONTENT_RANGE_HDR = 4, + + // Request handle not found + HE_HANDLE_NOT_FOUND = 5, + + // Invalid datatype for option/setting + HE_INVALID_ARG = 6, + + // Option hasn't been explicitly set + HE_OPT_NOT_SET = 7, + + // Option not dynamic, must be set during init phase + HE_OPT_NOT_DYNAMIC = 8, + + // Invalid HTTP status code returned by server + HE_INVALID_HTTP_STATUS = 9 + +}; // end enum HttpError + + +/// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP +/// reply status codes and internal errors as well. The encapsulation +/// isn't expected to completely isolate the caller from libcurl but +/// basic operational tests (success or failure) are provided. +/// +/// Non-HTTP status are encoded as (type, status) with type being +/// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status +/// being the success/error code from that domain. HTTP status +/// is encoded as (status, error_flag). Status should be in the +/// range [100, 999] and error_flag is either HE_SUCCESS or +/// HE_REPLY_ERROR to indicate whether this should be treated as +/// a successful status or an error. The application is responsible +/// for making that determination and a range like [200, 299] isn't +/// automatically assumed to be definitive. +/// +/// Examples: +/// +/// 1. Construct a default, successful status code: +/// HttpStatus(); +/// +/// 2. Construct a successful, HTTP 200 status code: +/// HttpStatus(200); +/// +/// 3. Construct a failed, HTTP 404 not-found status code: +/// HttpStatus(404); +/// +/// 4. Construct a failed libcurl couldn't connect status code: +/// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); +/// +/// 5. Construct an HTTP 301 status code to be treated as success: +/// HttpStatus(301, HE_SUCCESS); +/// + +struct HttpStatus +{ + typedef unsigned short type_enum_t; + + HttpStatus() + : mType(LLCORE), + mStatus(HE_SUCCESS) + {} + + HttpStatus(type_enum_t type, short status) + : mType(type), + mStatus(status) + {} + + HttpStatus(int http_status) + : mType(http_status), + mStatus(http_status >= 200 && http_status <= 299 + ? HE_SUCCESS + : HE_REPLY_ERROR) + { + llassert(http_status >= 100 && http_status <= 999); + } + + HttpStatus(const HttpStatus & rhs) + : mType(rhs.mType), + mStatus(rhs.mStatus) + {} + + HttpStatus & operator=(const HttpStatus & rhs) + { + // Don't care if lhs & rhs are the same object + + mType = rhs.mType; + mStatus = rhs.mStatus; + return *this; + } + + static const type_enum_t EXT_CURL_EASY = 0; + static const type_enum_t EXT_CURL_MULTI = 1; + static const type_enum_t LLCORE = 2; + + type_enum_t mType; + short mStatus; + + /// Test for successful status in the code regardless + /// of error source (internal, libcurl). + /// + /// @return 'true' when status is successful. + /// + operator bool() const + { + return 0 == mStatus; + } + + /// Inverse of previous operator. + /// + /// @return 'true' on any error condition + bool operator !() const + { + return 0 != mStatus; + } + + /// Equality and inequality tests to bypass bool conversion + /// which will do the wrong thing in conditional expressions. + bool operator==(const HttpStatus & rhs) const + { + return mType == rhs.mType && mStatus == rhs.mStatus; + } + + bool operator!=(const HttpStatus & rhs) const + { + return ! operator==(rhs); + } + + /// Convert to single numeric representation. Mainly + /// for logging or other informal purposes. Also + /// creates an ambiguous second path to integer conversion + /// which tends to find programming errors such as formatting + /// the status to a stream (operator<<). + operator unsigned long() const; + unsigned long toULong() const + { + return operator unsigned long(); + } + + /// And to convert to a hex string. + std::string toHex() const; + + /// Convert status to a string representation. For + /// success, returns an empty string. For failure + /// statuses, a string as appropriate for the source of + /// the error code (libcurl easy, libcurl multi, or + /// LLCore itself). + std::string toString() const; + + /// Returns true if the status value represents an + /// HTTP response status (100 - 999). + bool isHttpStatus() const + { + return mType >= type_enum_t(100) && mType <= type_enum_t(999); + } + +}; // end struct HttpStatus + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_COMMON_H_ diff --git a/indra/llcorehttp/httphandler.h b/indra/llcorehttp/httphandler.h new file mode 100644 index 0000000000000000000000000000000000000000..9171e4e7b930252a6bb0e9e8d62adaa085c9d5f5 --- /dev/null +++ b/indra/llcorehttp/httphandler.h @@ -0,0 +1,88 @@ +/** + * @file httphandler.h + * @brief Public-facing declarations for the HttpHandler class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HANDLER_H_ +#define _LLCORE_HTTP_HANDLER_H_ + + +#include "httpcommon.h" + + +namespace LLCore +{ + +class HttpResponse; + + +/// HttpHandler defines an interface used by the library to +/// notify library callers of significant events, currently +/// request completion. Callers must derive or mixin this class +/// then provide an implementation of the @see onCompleted +/// method to receive such notifications. An instance may +/// be shared by any number of requests and across instances +/// of HttpRequest running in the same thread. +/// +/// Threading: HttpHandler itself is pure interface and is +/// tread-compatible. Most derivations, however, will have +/// different constraints. +/// +/// Allocation: Not refcounted, may be stack allocated though +/// that is rarely a good idea. Queued requests and replies keep +/// a naked pointer to the handler and this can result in a +/// dangling pointer if lifetimes aren't managed correctly. + +class HttpHandler +{ +public: + virtual ~HttpHandler() + {} + + /// Method invoked during calls to @see update(). Each invocation + /// represents the completion of some requested operation. Caller + /// can identify the request from the handle and interrogate the + /// response argument for success/failure, data and other information. + /// + /// @param handle Identifier of the request generating + /// the notification. + /// @param response Supplies detailed information about + /// the request including status codes + /// (both programming and HTTP), HTTP body + /// data and encodings, headers, etc. + /// The response object is refcounted and + /// the called code may retain the object + /// by invoking @see addRef() on it. The + /// library itself drops all references to + /// to object on return and never touches + /// it again. + /// + virtual void onCompleted(HttpHandle handle, HttpResponse * response) = 0; + +}; // end class HttpHandler + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_HANDLER_H_ diff --git a/indra/llcorehttp/httpheaders.cpp b/indra/llcorehttp/httpheaders.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2832696271440cb3681a4a5186670a31862d433d --- /dev/null +++ b/indra/llcorehttp/httpheaders.cpp @@ -0,0 +1,44 @@ +/** + * @file httpheaders.cpp + * @brief Implementation of the HTTPHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpHeaders::HttpHeaders() + : RefCounted(true) +{} + + +HttpHeaders::~HttpHeaders() +{} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httpheaders.h b/indra/llcorehttp/httpheaders.h new file mode 100644 index 0000000000000000000000000000000000000000..3449daa3a1311c859720cc1624c885420822a7c2 --- /dev/null +++ b/indra/llcorehttp/httpheaders.h @@ -0,0 +1,87 @@ +/** + * @file httpheaders.h + * @brief Public-facing declarations for the HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_HEADERS_H_ +#define _LLCORE_HTTP_HEADERS_H_ + + +#include <string> + +#include "_refcounted.h" + + +namespace LLCore +{ + +/// +/// Maintains an ordered list of name/value pairs representing +/// HTTP header lines. This is used both to provide additional +/// headers when making HTTP requests and in responses when the +/// caller has asked that headers be returned (not the default +/// option). +/// +/// @note +/// This is a minimally-functional placeholder at the moment +/// to fill out the class hierarchy. The final class will be +/// something else, probably more pair-oriented. It's also +/// an area where shared values are desirable so refcounting is +/// already specced and a copy-on-write scheme imagined. +/// Expect changes here. +/// +/// Threading: Not intrinsically thread-safe. It *is* expected +/// that callers will build these objects and then share them +/// via reference counting with the worker thread. The implication +/// is that once an HttpHeader instance is handed to a request, +/// the object must be treated as read-only. +/// +/// Allocation: Refcounted, heap only. Caller of the +/// constructor is given a refcount. +/// + +class HttpHeaders : public LLCoreInt::RefCounted +{ +public: + /// @post In addition to the instance, caller has a refcount + /// to the instance. A call to @see release() will destroy + /// the instance. + HttpHeaders(); + +protected: + virtual ~HttpHeaders(); // Use release() + + HttpHeaders(const HttpHeaders &); // Not defined + void operator=(const HttpHeaders &); // Not defined + +public: + typedef std::vector<std::string> container_t; + container_t mHeaders; + +}; // end class HttpHeaders + +} // end namespace LLCore + + +#endif // _LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/httpoptions.cpp b/indra/llcorehttp/httpoptions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1699d19f8dced9496eb2162efe4a60a5b8c8d4c8 --- /dev/null +++ b/indra/llcorehttp/httpoptions.cpp @@ -0,0 +1,73 @@ +/** + * @file httpoptions.cpp + * @brief Implementation of the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpoptions.h" + +#include "_httpinternal.h" + + +namespace LLCore +{ + + +HttpOptions::HttpOptions() + : RefCounted(true), + mWantHeaders(false), + mTracing(HTTP_TRACE_OFF), + mTimeout(HTTP_REQUEST_TIMEOUT_DEFAULT), + mRetries(HTTP_RETRY_COUNT_DEFAULT) +{} + + +HttpOptions::~HttpOptions() +{} + + +void HttpOptions::setWantHeaders(bool wanted) +{ + mWantHeaders = wanted; +} + + +void HttpOptions::setTrace(long level) +{ + mTracing = int(level); +} + + +void HttpOptions::setTimeout(unsigned int timeout) +{ + mTimeout = timeout; +} + + +void HttpOptions::setRetries(unsigned int retries) +{ + mRetries = retries; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpoptions.h b/indra/llcorehttp/httpoptions.h new file mode 100644 index 0000000000000000000000000000000000000000..97e46a8cd35641d72c23e25b08f10dff01bb96a5 --- /dev/null +++ b/indra/llcorehttp/httpoptions.h @@ -0,0 +1,106 @@ +/** + * @file httpoptions.h + * @brief Public-facing declarations for the HTTPOptions class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_OPTIONS_H_ +#define _LLCORE_HTTP_OPTIONS_H_ + + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + + +/// Really a struct in spirit, it provides options that +/// modify HTTP requests. +/// +/// Sharing instances across requests. It's intended that +/// these be shared across requests: caller can create one +/// of these, set it up as needed and then reference it +/// repeatedly in HTTP operations. But see the Threading +/// note about references. +/// +/// Threading: While this class does nothing to ensure thread +/// safety, it *is* intended to be shared between the application +/// thread and the worker thread. This means that once an instance +/// is delivered to the library in request operations, the +/// option data must not be written until all such requests +/// complete and relinquish their references. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpOptions : public LLCoreInt::RefCounted +{ +public: + HttpOptions(); + +protected: + virtual ~HttpOptions(); // Use release() + + HttpOptions(const HttpOptions &); // Not defined + void operator=(const HttpOptions &); // Not defined + +public: + void setWantHeaders(bool wanted); + bool getWantHeaders() const + { + return mWantHeaders; + } + + void setTrace(int long); + int getTrace() const + { + return mTracing; + } + + void setTimeout(unsigned int timeout); + unsigned int getTimeout() const + { + return mTimeout; + } + + void setRetries(unsigned int retries); + unsigned int getRetries() const + { + return mRetries; + } + +protected: + bool mWantHeaders; + int mTracing; + unsigned int mTimeout; + unsigned int mRetries; + +}; // end class HttpOptions + + +} // end namespace HttpOptions + +#endif // _LLCORE_HTTP_OPTIONS_H_ diff --git a/indra/llcorehttp/httprequest.cpp b/indra/llcorehttp/httprequest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9b739a8825a9e54ae027263d17bd467ee7937f9c --- /dev/null +++ b/indra/llcorehttp/httprequest.cpp @@ -0,0 +1,504 @@ +/** + * @file httprequest.cpp + * @brief Implementation of the HTTPRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httprequest.h" + +#include "_httprequestqueue.h" +#include "_httpreplyqueue.h" +#include "_httpservice.h" +#include "_httppolicy.h" +#include "_httpoperation.h" +#include "_httpoprequest.h" +#include "_httpopsetpriority.h" +#include "_httpopcancel.h" +#include "_httpopsetget.h" + +#include "lltimer.h" + + +namespace +{ + +bool has_inited(false); + +} + +namespace LLCore +{ + +// ==================================== +// HttpRequest Implementation +// ==================================== + + +HttpRequest::policy_t HttpRequest::sNextPolicyID(1); + + +HttpRequest::HttpRequest() + : //HttpHandler(), + mReplyQueue(NULL), + mRequestQueue(NULL) +{ + mRequestQueue = HttpRequestQueue::instanceOf(); + mRequestQueue->addRef(); + + mReplyQueue = new HttpReplyQueue(); +} + + +HttpRequest::~HttpRequest() +{ + if (mRequestQueue) + { + mRequestQueue->release(); + mRequestQueue = NULL; + } + + if (mReplyQueue) + { + mReplyQueue->release(); + mReplyQueue = NULL; + } +} + + +// ==================================== +// Policy Methods +// ==================================== + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, long value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +} + + +HttpStatus HttpRequest::setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getGlobalOptions().set(opt, value); +} + + +HttpRequest::policy_t HttpRequest::createPolicyClass() +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return 0; + } + return HttpService::instanceOf()->createPolicyClass(); +} + + +HttpStatus HttpRequest::setPolicyClassOption(policy_t policy_id, + EClassPolicy opt, + long value) +{ + if (HttpService::RUNNING == HttpService::instanceOf()->getState()) + { + return HttpStatus(HttpStatus::LLCORE, HE_OPT_NOT_DYNAMIC); + } + return HttpService::instanceOf()->getClassOptions(policy_id).set(opt, value); +} + + +// ==================================== +// Request Methods +// ==================================== + + +HttpStatus HttpRequest::getStatus() const +{ + return mLastReqStatus; +} + + +HttpHandle HttpRequest::requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGet(policy_id, priority, url, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpHandle HttpRequest::requestGetByteRange(policy_t policy_id, + priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupGetByteRange(policy_id, priority, url, offset, len, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpHandle HttpRequest::requestPost(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPost(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpHandle HttpRequest::requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpRequest * op = new HttpOpRequest(); + if (! (status = op->setupPut(policy_id, priority, url, body, options, headers))) + { + op->release(); + mLastReqStatus = status; + return handle; + } + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpHandle HttpRequest::requestNoOp(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpNull * op = new HttpOpNull(); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpStatus HttpRequest::update(long usecs) +{ + HttpOperation * op(NULL); + + if (usecs) + { + const HttpTime limit(totalTime() + HttpTime(usecs)); + while (limit >= totalTime() && (op = mReplyQueue->fetchOp())) + { + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } + else + { + // Same as above, just no time limit + HttpReplyQueue::OpContainer replies; + mReplyQueue->fetchAll(replies); + if (! replies.empty()) + { + for (HttpReplyQueue::OpContainer::iterator iter(replies.begin()); + replies.end() != iter; + ++iter) + { + // Swap op pointer for NULL; + op = *iter; *iter = NULL; + + // Process operation + op->visitNotifier(this); + + // We're done with the operation + op->release(); + } + } + } + + return HttpStatus(); +} + + + + +// ==================================== +// Request Management Methods +// ==================================== + +HttpHandle HttpRequest::requestCancel(HttpHandle request, HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpCancel * op = new HttpOpCancel(request); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } + + mLastReqStatus = status; + ret_handle = static_cast<HttpHandle>(op); + + return ret_handle; +} + + +HttpHandle HttpRequest::requestSetPriority(HttpHandle request, priority_t priority, + HttpHandler * handler) +{ + HttpStatus status; + HttpHandle ret_handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetPriority * op = new HttpOpSetPriority(request, priority); + op->setReplyPath(mReplyQueue, handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return ret_handle; + } + + mLastReqStatus = status; + ret_handle = static_cast<HttpHandle>(op); + + return ret_handle; +} + + +// ==================================== +// Utility Methods +// ==================================== + +HttpStatus HttpRequest::createService() +{ + HttpStatus status; + + if (! has_inited) + { + HttpRequestQueue::init(); + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + HttpService::init(rq); + has_inited = true; + } + + return status; +} + + +HttpStatus HttpRequest::destroyService() +{ + HttpStatus status; + + if (has_inited) + { + HttpService::term(); + HttpRequestQueue::term(); + has_inited = false; + } + + return status; +} + + +HttpStatus HttpRequest::startThread() +{ + HttpStatus status; + + HttpService::instanceOf()->startThread(); + + return status; +} + + +HttpHandle HttpRequest::requestStopThread(HttpHandler * user_handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpStop * op = new HttpOpStop(); + op->setReplyPath(mReplyQueue, user_handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +HttpHandle HttpRequest::requestSpin(int mode) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSpin * op = new HttpOpSpin(mode); + op->setReplyPath(mReplyQueue, NULL); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + +// ==================================== +// Dynamic Policy Methods +// ==================================== + +HttpHandle HttpRequest::requestSetHttpProxy(const std::string & proxy, HttpHandler * handler) +{ + HttpStatus status; + HttpHandle handle(LLCORE_HTTP_HANDLE_INVALID); + + HttpOpSetGet * op = new HttpOpSetGet(); + op->setupSet(GP_HTTP_PROXY, proxy); + op->setReplyPath(mReplyQueue, handler); + if (! (status = mRequestQueue->addOp(op))) // transfers refcount + { + op->release(); + mLastReqStatus = status; + return handle; + } + + mLastReqStatus = status; + handle = static_cast<HttpHandle>(op); + + return handle; +} + + +} // end namespace LLCore + diff --git a/indra/llcorehttp/httprequest.h b/indra/llcorehttp/httprequest.h new file mode 100644 index 0000000000000000000000000000000000000000..ab2f302d34be0d6525d0a8a47bb91971692899f9 --- /dev/null +++ b/indra/llcorehttp/httprequest.h @@ -0,0 +1,535 @@ +/** + * @file httprequest.h + * @brief Public-facing declarations for HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_REQUEST_H_ +#define _LLCORE_HTTP_REQUEST_H_ + + +#include "httpcommon.h" +#include "httphandler.h" + + +namespace LLCore +{ + +class HttpRequestQueue; +class HttpReplyQueue; +class HttpService; +class HttpOptions; +class HttpHeaders; +class HttpOperation; +class BufferArray; + +/// HttpRequest supplies the entry into the HTTP transport +/// services in the LLCore libraries. Services provided include: +/// +/// - Some, but not all, global initialization of libcurl. +/// - Starting asynchronous, threaded HTTP requests. +/// - Definition of policy classes affect request handling. +/// - Utilities to control request options and headers +/// +/// Requests +/// +/// The class supports the current HTTP request operations: +/// +/// - requestGetByteRange: GET with Range header for a single range of bytes +/// +/// Policy Classes +/// +/// <TBD> +/// +/// Usage +/// +/// <TBD> +/// +/// Threading: An instance may only be used by one application/ +/// consumer thread. But a thread may have as many instances of +/// this as it likes. +/// +/// Allocation: Not refcounted, may be stack allocated though that +/// hasn't been tested. Queued requests can still run and any +/// queued replies will keep refcounts to the reply queue leading +/// to memory leaks. +/// +/// @pre Before using this class (static or instances), some global +/// initialization is required. See @see httpcommon.h for more information. +/// +/// @nosubgrouping +/// + +class HttpRequest +{ +public: + HttpRequest(); + virtual ~HttpRequest(); + +private: + HttpRequest(const HttpRequest &); // Disallowed + void operator=(const HttpRequest &); // Disallowed + +public: + typedef unsigned int policy_t; + typedef unsigned int priority_t; + +public: + /// @name PolicyMethods + /// @{ + + /// Represents a default, catch-all policy class that guarantees + /// eventual service for any HTTP request. + static const int DEFAULT_POLICY_ID = 0; + + enum EGlobalPolicy + { + /// Maximum number of connections the library will use to + /// perform operations. This is somewhat soft as the underlying + /// transport will cache some connections (up to 5). + + /// A long value setting the maximum number of connections + /// allowed over all policy classes. Note that this will be + /// a somewhat soft value. There may be an additional five + /// connections per policy class depending upon runtime + /// behavior. + GP_CONNECTION_LIMIT, + + /// String containing a system-appropriate directory name + /// where SSL certs are stored. + GP_CA_PATH, + + /// String giving a full path to a file containing SSL certs. + GP_CA_FILE, + + /// String of host/port to use as simple HTTP proxy. This is + /// going to change in the future into something more elaborate + /// that may support richer schemes. + GP_HTTP_PROXY, + + /// Long value that if non-zero enables the use of the + /// traditional LLProxy code for http/socks5 support. If + /// enabled, has priority over GP_HTTP_PROXY. + GP_LLPROXY, + + /// Long value setting the logging trace level for the + /// library. Possible values are: + /// 0 - No tracing (default) + /// 1 - Basic tracing of request start, stop and major events. + /// 2 - Connection, header and payload size information from + /// HTTP transactions. + /// 3 - Partial logging of payload itself. + /// + /// These values are also used in the trace modes for + /// individual requests in HttpOptions. Also be aware that + /// tracing tends to impact performance of the viewer. + GP_TRACE + }; + + /// Set a parameter on a global policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value); + static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value); + + /// Create a new policy class into which requests can be made. + /// + /// @return If positive, the policy_id used to reference + /// the class in other methods. If 0, an error + /// occurred and @see getStatus() may provide more + /// detail on the reason. + static policy_t createPolicyClass(); + + enum EClassPolicy + { + /// Limits the number of connections used for the class. + CP_CONNECTION_LIMIT, + + /// Limits the number of connections used for a single + /// literal address/port pair within the class. + CP_PER_HOST_CONNECTION_LIMIT, + + /// Suitable requests are allowed to pipeline on their + /// connections when they ask for it. + CP_ENABLE_PIPELINING + }; + + /// Set a parameter on a class-based policy option. Calls + /// made after the start of the servicing thread are + /// not honored and return an error status. + /// + /// @param policy_id ID of class as returned by @see createPolicyClass(). + /// @param opt Enum of option to be set. + /// @param value Desired value of option. + /// @return Standard status code. + static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value); + + /// @} + + /// @name RequestMethods + /// + /// @{ + + /// Some calls expect to succeed as the normal part of operation and so + /// return a useful value rather than a status. When they do fail, the + /// status is saved and can be fetched with this method. + /// + /// @return Status of the failing method invocation. If the + /// preceding call succeeded or other HttpStatus + /// returning calls immediately preceded this method, + /// the returned value may not be reliable. + /// + HttpStatus getStatus() const; + + /// Queue a full HTTP GET request to be issued for entire entity. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: <stuff> + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Range: + /// - Transfer-Encoding: + /// - Referer: + /// + /// @param policy_id Default or user-defined policy class under + /// which this request is to be serviced. + /// @param priority Standard priority scheme inherited from + /// Indra code base (U32-type scheme). + /// @param url URL with any encoded query parameters to + /// be accessed. + /// @param options Optional instance of an HttpOptions object + /// to provide additional controls over the request + /// function for this request only. Any such + /// object then becomes shared-read across threads + /// and no code should modify the HttpOptions + /// instance. + /// @param headers Optional instance of an HttpHeaders object + /// to provide additional and/or overridden + /// headers for the request. As with options, + /// the instance becomes shared-read across threads + /// and no code should modify the HttpHeaders + /// instance. + /// @param handler Optional pointer to an HttpHandler instance + /// whose onCompleted() method will be invoked + /// during calls to update(). This is a non- + /// reference-counted object which would be a + /// problem for shutdown and other edge cases but + /// the pointer is only dereferenced during + /// calls to update(). + /// + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// + HttpHandle requestGet(policy_t policy_id, + priority_t priority, + const std::string & url, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP GET request to be issued with a 'Range' header. + /// The request is queued and serviced by the working thread and + /// notification of completion delivered to the optional HttpHandler + /// argument during @see update() calls. + /// + /// With a valid handle returned, it can be used to reference the + /// request in other requests (like cancellation) and will be an + /// argument when any HttpHandler object is invoked. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-alive: 300 + /// - Host: <stuff> + /// - Range: <stuff> (will be omitted if offset == 0 and len == 0) + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-control: + /// - Transfer-Encoding: + /// - Referer: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param offset Offset of first byte into resource to be returned. + /// @param len Count of bytes to be returned + /// @param options @see requestGet() + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestGetByteRange(policy_t policy_id, + priority_t priority, + const std::string & url, + size_t offset, + size_t len, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP POST. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: <stuff> + /// - Content-Length: <digits> + /// - Content-Type: application/x-www-form-urlencoded + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestPost(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a full HTTP PUT. Query arguments and body may + /// be provided. Caller is responsible for escaping and + /// encoding and communicating the content types. + /// + /// Headers supplied by default: + /// - Connection: keep-alive + /// - Accept: */* + /// - Accept-Encoding: deflate, gzip + /// - Keep-Alive: 300 + /// - Host: <stuff> + /// - Content-Length: <digits> + /// + /// Some headers excluded by default: + /// - Pragma: + /// - Cache-Control: + /// - Transfer-Encoding: ... chunked ... + /// - Referer: + /// - Content-Encoding: + /// - Expect: + /// - Content-Type: + /// + /// @param policy_id @see requestGet() + /// @param priority " + /// @param url " + /// @param body Byte stream to be sent as the body. No + /// further encoding or escaping will be done + /// to the content. + /// @param options @see requestGet()K(optional) + /// @param headers " + /// @param handler " + /// @return " + /// + HttpHandle requestPut(policy_t policy_id, + priority_t priority, + const std::string & url, + BufferArray * body, + HttpOptions * options, + HttpHeaders * headers, + HttpHandler * handler); + + + /// Queue a NoOp request. + /// The request is queued and serviced by the working thread which + /// immediately processes it and returns the request to the reply + /// queue. + /// + /// @param handler @see requestGet() + /// @return " + /// + HttpHandle requestNoOp(HttpHandler * handler); + + /// While all the heavy work is done by the worker thread, notifications + /// must be performed in the context of the application thread. These + /// are done synchronously during calls to this method which gives the + /// library control so notification can be performed. Application handlers + /// are expected to return 'quickly' and do any significant processing + /// outside of the notification callback to onCompleted(). + /// + /// @param usecs Maximum number of wallclock microseconds to + /// spend in the call. As hinted at above, this + /// is partly a function of application code so it's + /// a soft limit. A '0' value will run without + /// time limit until everything queued has been + /// delivered. + /// + /// @return Standard status code. + HttpStatus update(long usecs); + + /// @} + + /// @name RequestMgmtMethods + /// + /// @{ + + HttpHandle requestCancel(HttpHandle request, HttpHandler *); + + /// Request that a previously-issued request be reprioritized. + /// The status of whether the change itself succeeded arrives + /// via notification. + /// + /// @param request Handle of previously-issued request to + /// be changed. + /// @param priority New priority value. + /// @param handler @see requestGet() + /// @return " + /// + HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler); + + /// @} + + /// @name UtilityMethods + /// + /// @{ + + /// Initialization method that needs to be called before queueing any + /// requests. Doesn't start the worker thread and may be called befoer + /// or after policy setup. + static HttpStatus createService(); + + /// Mostly clean shutdown of services prior to exit. Caller is expected + /// to have stopped a running worker thread before calling this. + static HttpStatus destroyService(); + + /// Called once after @see createService() to start the worker thread. + /// Stopping the thread is achieved by requesting it via @see requestStopThread(). + /// May be called before or after requests are issued. + static HttpStatus startThread(); + + /// Queues a request to the worker thread to have it stop processing + /// and exit (without exiting the program). When the operation is + /// picked up by the worker thread, it immediately processes it and + /// begins detaching from refcounted resources like request and + /// reply queues and then returns to the host OS. It *does* queue a + /// reply to give the calling application thread a notification that + /// the operation has been performed. + /// + /// @param handler (optional) + /// @return The handle of the request if successfully + /// queued or LLCORE_HTTP_HANDLE_INVALID if the + /// request could not be queued. In the latter + /// case, @see getStatus() will return more info. + /// As the request cannot be cancelled, the handle + /// is generally not useful. + /// + HttpHandle requestStopThread(HttpHandler * handler); + + /// Queue a Spin request. + /// DEBUG/TESTING ONLY. This puts the worker into a CPU spin for + /// test purposes. + /// + /// @param mode 0 for hard spin, 1 for soft spin + /// @return Standard handle return cases. + /// + HttpHandle requestSpin(int mode); + + /// @} + + /// @name DynamicPolicyMethods + /// + /// @{ + + /// Request that a running transport pick up a new proxy setting. + /// An empty string will indicate no proxy is to be used. + HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler); + + /// @} + +protected: + void generateNotification(HttpOperation * op); + +private: + /// @name InstanceData + /// + /// @{ + HttpStatus mLastReqStatus; + HttpReplyQueue * mReplyQueue; + HttpRequestQueue * mRequestQueue; + + /// @} + + // ==================================== + /// @name GlobalState + /// + /// @{ + /// + /// Must be established before any threading is allowed to + /// start. + /// + static policy_t sNextPolicyID; + + /// @} + // End Global State + // ==================================== + +}; // end class HttpRequest + + +} // end namespace LLCore + + + +#endif // _LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/httpresponse.cpp b/indra/llcorehttp/httpresponse.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a552e48a1bac51f5f78377fadf1252ec339a1634 --- /dev/null +++ b/indra/llcorehttp/httpresponse.cpp @@ -0,0 +1,91 @@ +/** + * @file httpresponse.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "httpresponse.h" +#include "bufferarray.h" +#include "httpheaders.h" + + +namespace LLCore +{ + + +HttpResponse::HttpResponse() + : LLCoreInt::RefCounted(true), + mReplyOffset(0U), + mReplyLength(0U), + mReplyFullLength(0U), + mBufferArray(NULL), + mHeaders(NULL) +{} + + +HttpResponse::~HttpResponse() +{ + setBody(NULL); + setHeaders(NULL); +} + + +void HttpResponse::setBody(BufferArray * ba) +{ + if (mBufferArray == ba) + return; + + if (mBufferArray) + { + mBufferArray->release(); + } + + if (ba) + { + ba->addRef(); + } + + mBufferArray = ba; +} + + +void HttpResponse::setHeaders(HttpHeaders * headers) +{ + if (mHeaders == headers) + return; + + if (mHeaders) + { + mHeaders->release(); + } + + if (headers) + { + headers->addRef(); + } + + mHeaders = headers; +} + + +} // end namespace LLCore diff --git a/indra/llcorehttp/httpresponse.h b/indra/llcorehttp/httpresponse.h new file mode 100644 index 0000000000000000000000000000000000000000..4a481db6ac5efe0c028ad868f509ed2c4a778c4f --- /dev/null +++ b/indra/llcorehttp/httpresponse.h @@ -0,0 +1,161 @@ +/** + * @file httpresponse.h + * @brief Public-facing declarations for the HttpResponse class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LLCORE_HTTP_RESPONSE_H_ +#define _LLCORE_HTTP_RESPONSE_H_ + + +#include <string> + +#include "httpcommon.h" + +#include "_refcounted.h" + + +namespace LLCore +{ + +class BufferArray; +class HttpHeaders; + +/// HttpResponse is instantiated by the library and handed to +/// the caller during callbacks to the handler. It supplies +/// all the status, header and HTTP body data the caller is +/// interested in. Methods provide simple getters to return +/// individual pieces of the response. +/// +/// Typical usage will have the caller interrogate the object +/// and return from the handler callback. Instances are refcounted +/// and callers can bump the count and retain the object as needed. +/// +/// Threading: Not intrinsically thread-safe. +/// +/// Allocation: Refcounted, heap only. Caller of the constructor +/// is given a refcount. +/// +class HttpResponse : public LLCoreInt::RefCounted +{ +public: + HttpResponse(); + +protected: + virtual ~HttpResponse(); // Use release() + + HttpResponse(const HttpResponse &); // Not defined + void operator=(const HttpResponse &); // Not defined + +public: + /// Returns the final status of the requested operation. + /// + HttpStatus getStatus() const + { + return mStatus; + } + + void setStatus(const HttpStatus & status) + { + mStatus = status; + } + + /// Simple getter for the response body returned as a scatter/gather + /// buffer. If the operation doesn't produce data (such as the Null + /// or StopThread operations), this may be NULL. + /// + /// Caller can hold onto the response by incrementing the reference + /// count of the returned object. + BufferArray * getBody() const + { + return mBufferArray; + } + + /// Set the response data in the instance. Will drop the reference + /// count to any existing data and increment the count of that passed + /// in. It is legal to set the data to NULL. + void setBody(BufferArray * ba); + + /// And a getter for the headers. And as with @see getResponse(), + /// if headers aren't available because the operation doesn't produce + /// any or delivery of headers wasn't requested in the options, this + /// will be NULL. + /// + /// Caller can hold onto the headers by incrementing the reference + /// count of the returned object. + HttpHeaders * getHeaders() const + { + return mHeaders; + } + + /// Behaves like @see setResponse() but for header data. + void setHeaders(HttpHeaders * headers); + + /// If a 'Range:' header was used, these methods are involved + /// in setting and returning data about the actual response. + /// If both @offset and @length are returned as 0, we probably + /// didn't get a Content-Range header in the response. This + /// occurs with various Capabilities-based services and the + /// caller is going to have to make assumptions on receipt of + /// a 206 status. The @full value may also be zero in cases of + /// parsing problems or a wild-carded length response. + void getRange(unsigned int * offset, unsigned int * length, unsigned int * full) const + { + *offset = mReplyOffset; + *length = mReplyLength; + *full = mReplyFullLength; + } + + void setRange(unsigned int offset, unsigned int length, unsigned int full_length) + { + mReplyOffset = offset; + mReplyLength = length; + mReplyFullLength = full_length; + } + + /// + const std::string & getContentType() const + { + return mContentType; + } + + void setContentType(const std::string & con_type) + { + mContentType = con_type; + } + +protected: + // Response data here + HttpStatus mStatus; + unsigned int mReplyOffset; + unsigned int mReplyLength; + unsigned int mReplyFullLength; + BufferArray * mBufferArray; + HttpHeaders * mHeaders; + std::string mContentType; +}; + + +} // end namespace LLCore + +#endif // _LLCORE_HTTP_RESPONSE_H_ diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e863ddd13ff75939d3cf4aa4988b065ae79983ed --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -0,0 +1,175 @@ +/** + * @file llcorehttp_test + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llcorehttp_test.h" + +#include <iostream> +#include <sstream> + +// These are not the right way in viewer for some reason: +// #include <tut/tut.hpp> +// #include <tut/tut_reporter.hpp> +// This works: +#include "../test/lltut.h" + +// Pull in each of the test sets +#include "test_bufferarray.hpp" +#include "test_bufferstream.hpp" +#include "test_httpstatus.hpp" +#include "test_refcounted.hpp" +#include "test_httpoperation.hpp" +#include "test_httprequest.hpp" +#include "test_httpheaders.hpp" +#include "test_httprequestqueue.hpp" + +#include "llproxy.h" + +unsigned long ssl_thread_id_callback(void); +void ssl_locking_callback(int mode, int type, const char * file, int line); + +#if 0 // lltut provides main and runner + +namespace tut +{ + test_runner_singleton runner; +} + +int main() +{ + curl_global_init(CURL_GLOBAL_ALL); + + // *FIXME: Need threaded/SSL curl setup here. + + tut::reporter reporter; + + tut::runner.get().set_callback(&reporter); + tut::runner.get().run_tests(); + return !reporter.all_ok(); + + curl_global_cleanup(); +} + +#endif // 0 + +int ssl_mutex_count(0); +LLCoreInt::HttpMutex ** ssl_mutex_list = NULL; + +void init_curl() +{ + curl_global_init(CURL_GLOBAL_ALL); + + ssl_mutex_count = CRYPTO_num_locks(); + if (ssl_mutex_count > 0) + { + ssl_mutex_list = new LLCoreInt::HttpMutex * [ssl_mutex_count]; + + for (int i(0); i < ssl_mutex_count; ++i) + { + ssl_mutex_list[i] = new LLCoreInt::HttpMutex; + } + + CRYPTO_set_locking_callback(ssl_locking_callback); + CRYPTO_set_id_callback(ssl_thread_id_callback); + } + + LLProxy::getInstance(); +} + + +void term_curl() +{ + LLProxy::cleanupClass(); + + CRYPTO_set_locking_callback(NULL); + for (int i(0); i < ssl_mutex_count; ++i) + { + delete ssl_mutex_list[i]; + } + delete [] ssl_mutex_list; +} + + +unsigned long ssl_thread_id_callback(void) +{ +#if defined(WIN32) + return (unsigned long) GetCurrentThread(); +#else + return (unsigned long) pthread_self(); +#endif +} + + +void ssl_locking_callback(int mode, int type, const char * /* file */, int /* line */) +{ + if (type >= 0 && type < ssl_mutex_count) + { + if (mode & CRYPTO_LOCK) + { + ssl_mutex_list[type]->lock(); + } + else + { + ssl_mutex_list[type]->unlock(); + } + } +} + + +std::string get_base_url() +{ + const char * env(getenv("LL_TEST_PORT")); + + if (! env) + { + std::cerr << "LL_TEST_PORT environment variable missing." << std::endl; + std::cerr << "Test expects to run in test_llcorehttp_peer.py script." << std::endl; + tut::ensure("LL_TEST_PORT set in environment", NULL != env); + } + + int port(atoi(env)); + std::ostringstream out; + out << "http://localhost:" << port << "/"; + return out.str(); +} + + +void stop_thread(LLCore::HttpRequest * req) +{ + if (req) + { + req->requestStopThread(NULL); + + int count = 0; + int limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + req->update(1000); + usleep(100000); + } + } +} + + diff --git a/indra/llcorehttp/tests/llcorehttp_test.h b/indra/llcorehttp/tests/llcorehttp_test.h new file mode 100644 index 0000000000000000000000000000000000000000..a9567435ce1a23a3a98a8149a3e48cbabb5a2d2f --- /dev/null +++ b/indra/llcorehttp/tests/llcorehttp_test.h @@ -0,0 +1,64 @@ +/** + * @file llcorehttp_test.h + * @brief Main test runner + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + + +#ifndef _LLCOREHTTP_TEST_H_ +#define _LLCOREHTTP_TEST_H_ + +#include "linden_common.h" // Modifies curl interfaces + +#include <curl/curl.h> +#include <openssl/crypto.h> +#include <string> + +#include "httprequest.h" + +// Initialization and cleanup for libcurl. Mainly provides +// a mutex callback for SSL and a thread ID hash for libcurl. +// If you don't use these (or equivalent) and do use libcurl, +// you'll see stalls and other anomalies when performing curl +// operations. +extern void init_curl(); +extern void term_curl(); +extern std::string get_base_url(); +extern void stop_thread(LLCore::HttpRequest * req); + +class ScopedCurlInit +{ +public: + ScopedCurlInit() + { + init_curl(); + } + + ~ScopedCurlInit() + { + term_curl(); + } +}; + + +#endif // _LLCOREHTTP_TEST_H_ diff --git a/indra/llcorehttp/tests/test_allocator.cpp b/indra/llcorehttp/tests/test_allocator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ea12dc58eb1bb19eecd2e624e505e55ede961de9 --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.cpp @@ -0,0 +1,184 @@ +/** + * @file test_allocator.cpp + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "test_allocator.h" + +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 +#include <libkern/OSAtomic.h> +#elif defined(_MSC_VER) +#include <Windows.h> +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 +// atomic extensions are built into GCC on posix platforms +#endif + +#include <cassert> +#include <cstdlib> +#include <cstring> +#include <vector> +#include <iostream> +#include <new> + +#include <boost/thread.hpp> + + +#if defined(WIN32) +#define THROW_BAD_ALLOC() _THROW1(std::bad_alloc) +#define THROW_NOTHING() _THROW0() +#else +#define THROW_BAD_ALLOC() throw(std::bad_alloc) +#define THROW_NOTHING() throw() +#endif + + +struct BlockHeader +{ + struct Block * next; + std::size_t size; + bool in_use; +}; + +struct Block +{ + BlockHeader hdr; + unsigned char data[1]; +}; + +#define TRACE_MSG(val) std::cout << __FUNCTION__ << "(" << val << ") [" << __FILE__ << ":" << __LINE__ << "]" << std::endl; + +static unsigned char MemBuf[ 4096 * 1024 ]; +Block * pNext = static_cast<Block *>(static_cast<void *>(MemBuf)); +volatile std::size_t MemTotal = 0; + +// cross-platform compare and swap operation +static bool CAS(void * volatile * ptr, void * expected, void * new_value) +{ +#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwapPtr( expected, new_value, ptr ); +#elif defined(_MSC_VER) + return expected == InterlockedCompareExchangePointer( ptr, new_value, expected ); +#elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ ) > 40100 + return __sync_bool_compare_and_swap( ptr, expected, new_value ); +#endif +} + +static void * GetMem(std::size_t size) +{ + // TRACE_MSG(size); + volatile Block * pBlock = NULL; + volatile Block * pNewNext = NULL; + + // do a lock-free update of the global next pointer + do + { + pBlock = pNext; + pNewNext = (volatile Block *)(pBlock->data + size); + + } while(! CAS((void * volatile *) &pNext, (void *) pBlock, (void *) pNewNext)); + + // if we get here, we safely carved out a block of memory in the + // memory pool... + + // initialize our block + pBlock->hdr.next = (Block *)(pBlock->data + size); + pBlock->hdr.size = size; + pBlock->hdr.in_use = true; + memset((void *) pBlock->data, 0, pBlock->hdr.size); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total + size; + + } while (! CAS((void * volatile *) &MemTotal, (void *) total, (void *) new_total)); + + return (void *) pBlock->data; +} + + +static void FreeMem(void * p) +{ + // get the pointer to the block record + Block * pBlock = (Block *)((unsigned char *) p - sizeof(BlockHeader)); + + // TRACE_MSG(pBlock->hdr.size); + bool * cur_in_use = &(pBlock->hdr.in_use); + volatile bool in_use = false; + bool new_in_use = false; + do + { + in_use = pBlock->hdr.in_use; + } while (! CAS((void * volatile *) cur_in_use, (void *) in_use, (void *) new_in_use)); + + // do a lock-free update of the global memory total + volatile size_t total = 0; + volatile size_t new_total = 0; + do + { + total = MemTotal; + new_total = total - pBlock->hdr.size; + } while (! CAS((void * volatile *)&MemTotal, (void *) total, (void *) new_total)); +} + + +std::size_t GetMemTotal() +{ + return MemTotal; +} + + +void * operator new(std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void * operator new[](std::size_t size) THROW_BAD_ALLOC() +{ + return GetMem( size ); +} + + +void operator delete(void * p) THROW_NOTHING() +{ + if (p) + { + FreeMem( p ); + } +} + + +void operator delete[](void * p) THROW_NOTHING() +{ + if (p) + { + FreeMem( p ); + } +} + + diff --git a/indra/llcorehttp/tests/test_allocator.h b/indra/llcorehttp/tests/test_allocator.h new file mode 100644 index 0000000000000000000000000000000000000000..3572bbc5c5f91b007bf14088244a2739a7cbd0ba --- /dev/null +++ b/indra/llcorehttp/tests/test_allocator.h @@ -0,0 +1,47 @@ +/** + * @file test_allocator.h + * @brief quick and dirty allocator for tracking memory allocations + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_ALLOCATOR_H +#define TEST_ALLOCATOR_H + +#include <cstdlib> +#include <new> + +size_t GetMemTotal(); +#if defined(WIN32) +void * operator new(std::size_t size) _THROW1(std::bad_alloc); +void * operator new[](std::size_t size) _THROW1(std::bad_alloc); +void operator delete(void * p) _THROW0(); +void operator delete[](void * p) _THROW0(); +#else +void * operator new(std::size_t size) throw (std::bad_alloc); +void * operator new[](std::size_t size) throw (std::bad_alloc); +void operator delete(void * p) throw (); +void operator delete[](void * p) throw (); +#endif + +#endif // TEST_ALLOCATOR_H + diff --git a/indra/llcorehttp/tests/test_bufferarray.hpp b/indra/llcorehttp/tests/test_bufferarray.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8a2a64d970c385ab3051d5516bb3c2845f463338 --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferarray.hpp @@ -0,0 +1,432 @@ +/** + * @file test_bufferarray.hpp + * @brief unit tests for the LLCore::BufferArray class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_ARRAY_H_ +#define TEST_LLCORE_BUFFER_ARRAY_H_ + +#include "bufferarray.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCore; + + + +namespace tut +{ + +struct BufferArrayTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group<BufferArrayTestData> BufferArrayTestGroupType; +typedef BufferArrayTestGroupType::object BufferArrayTestObjectType; +BufferArrayTestGroupType BufferArrayTestGroup("BufferArray Tests"); + +template <> template <> +void BufferArrayTestObjectType::test<1>() +{ + set_test_name("BufferArray construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + ensure("One ref on construction of BufferArray", ba->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in BA", 0 == ba->size()); + + // Try to read + char buffer[20]; + size_t read_len(ba->read(0, buffer, sizeof(buffer))); + ensure("Read returns empty", 0 == read_len); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<2>() +{ + set_test_name("BufferArray single write"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + char buffer[256]; + + size_t len = ba->write(0, str1, strlen(str1)); + ensure("Wrote length correct", strlen(str1) == len); + ensure("Recorded size correct", strlen(str1) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(2, buffer, 2); + ensure("Read length correct", 2 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read didn't overwrite", 'X' == buffer[2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferArrayTestObjectType::test<3>() +{ + set_test_name("BufferArray multiple writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read whole thing + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Read length correct", (2 * str1_len) == len); + ensure("Read content correct (3)", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct (4)", 0 == strncmp(&buffer[str1_len], str1, str1_len)); + ensure("Read didn't overwrite (5)", 'X' == buffer[2 * str1_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<4>() +{ + set_test_name("BufferArray overwriting"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJ"; + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->write(8, str2, 4); + ensure("Overwrite length correct", 4 == len); + + // Leave position and read verifying content (stale really from seek() days) + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(12, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'c' == buffer[0] && 'd' == buffer[1]); + ensure("Read content correct.2", 'e' == buffer[2] && 'f' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // reposition and check + len = ba->read(6, buffer, 8); + ensure("Read length correct.2", 8 == len); + ensure("Read content correct.3", 'g' == buffer[0] && 'h' == buffer[1]); + ensure("Read content correct.4", 'A' == buffer[2] && 'B' == buffer[3]); + ensure("Read content correct.5", 'C' == buffer[4] && 'D' == buffer[5]); + ensure("Read content correct.6", 'c' == buffer[6] && 'd' == buffer[7]); + ensure("Read didn't overwrite.7", 'X' == buffer[8]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<5>() +{ + set_test_name("BufferArray multiple writes - sequential reads"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // read some data back + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8, buffer, 4); + ensure("Read length correct", 4 == len); + ensure("Read content correct", 'i' == buffer[0] && 'j' == buffer[1]); + ensure("Read content correct.2", 'a' == buffer[2] && 'b' == buffer[3]); + ensure("Read didn't overwrite", 'X' == buffer[4]); + + // Read some more without repositioning + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(12, buffer, sizeof(buffer)); + ensure("Read length correct", (str1_len - 2) == len); + ensure("Read content correct.3", 0 == strncmp(buffer, str1+2, str1_len-2)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len-1]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<6>() +{ + set_test_name("BufferArray overwrite spanning blocks and appending"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + size_t len = ba->write(0, str1, str1_len); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", str1_len == ba->size()); + + // again... + len = ba->write(str1_len, str1, strlen(str1)); + ensure("Wrote length correct", str1_len == len); + ensure("Recorded size correct", (2 * str1_len) == ba->size()); + + // reposition and overwrite + len = ba->write(8, str2, str2_len); + ensure("Overwrite length correct", str2_len == len); + + // Leave position and read verifying content + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(8 + str2_len, buffer, 0); + ensure("Read length correct", 0 == len); + ensure("Read didn't overwrite", 'X' == buffer[0]); + + // reposition and check + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Read length correct.2", (str1_len + str2_len - 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, str1_len-2)); + ensure("Read content correct.2", 0 == strncmp(buffer+str1_len-2, str2, str2_len)); + ensure("Read didn't overwrite.2", 'X' == buffer[str1_len + str2_len - 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<7>() +{ + set_test_name("BufferArray overwrite spanning blocks and sequential writes"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); + + // reposition and overwrite + len = ba->write(6, str2, 2); + ensure("Overwrite length correct", 2 == len); + + len = ba->write(8, str2, 2); + ensure("Overwrite length correct.2", 2 == len); + + len = ba->write(10, str2, 2); + ensure("Overwrite length correct.3", 2 == len); + + // append some data + len = ba->append(str2, str2_len); + ensure("Append length correct", str2_len == len); + + // append some more + void * out_buf(ba->appendBufferAlloc(str1_len)); + memcpy(out_buf, str1, str1_len); + + // And some final writes + len = ba->write(3 * str1_len + str2_len, str2, 2); + ensure("Write length correct.2", 2 == len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Final buffer length correct", (3 * str1_len + str2_len + 2) == len); + ensure("Read content correct", 0 == strncmp(buffer, str1, 6)); + ensure("Read content correct.2", 0 == strncmp(buffer + 6, str2, 2)); + ensure("Read content correct.3", 0 == strncmp(buffer + 8, str2, 2)); + ensure("Read content correct.4", 0 == strncmp(buffer + 10, str2, 2)); + ensure("Read content correct.5", 0 == strncmp(buffer + str1_len + 2, str1 + 2, str1_len - 2)); + ensure("Read content correct.6", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read content correct.7", 0 == strncmp(buffer + str1_len + str1_len + str2_len, str1, str1_len)); + ensure("Read content correct.8", 0 == strncmp(buffer + str1_len + str1_len + str2_len + str1_len, str2, 2)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len + str1_len + 2]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +template <> template <> +void BufferArrayTestObjectType::test<8>() +{ + set_test_name("BufferArray zero-length appendBufferAlloc"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArray * ba = new BufferArray(); + + // write some data to the buffer + char str1[] = "abcdefghij"; + size_t str1_len(strlen(str1)); + char str2[] = "ABCDEFGHIJKLMNOPQRST"; + size_t str2_len(strlen(str2)); + char buffer[256]; + + // 2x str1 + size_t len = ba->write(0, str1, str1_len); + len = ba->write(str1_len, str1, str1_len); + + // zero-length allocate (we allow this with a valid pointer returned) + void * out_buf(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL", NULL != out_buf); + + // Do it again + void * out_buf2(ba->appendBufferAlloc(0)); + ensure("Buffer from zero-length appendBufferAlloc non-NULL.2", NULL != out_buf2); + ensure("Two zero-length appendBufferAlloc buffers distinct", out_buf != out_buf2); + + // And some final writes + len = ba->write(2 * str1_len, str2, str2_len); + + // Check contents + memset(buffer, 'X', sizeof(buffer)); + len = ba->read(0, buffer, sizeof(buffer)); + ensure("Final buffer length correct", (2 * str1_len + str2_len) == len); + ensure("Read content correct.1", 0 == strncmp(buffer, str1, str1_len)); + ensure("Read content correct.2", 0 == strncmp(buffer + str1_len, str1, str1_len)); + ensure("Read content correct.3", 0 == strncmp(buffer + str1_len + str1_len, str2, str2_len)); + ensure("Read didn't overwrite", 'X' == buffer[str1_len + str1_len + str2_len]); + + // release the implicit reference, causing the object to be released + ba->release(); + + // make sure we didn't leak any memory + ensure("All memory released", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_ARRAY_H_ diff --git a/indra/llcorehttp/tests/test_bufferstream.hpp b/indra/llcorehttp/tests/test_bufferstream.hpp new file mode 100644 index 0000000000000000000000000000000000000000..831c901b9d72f4e7ca24033f863bccfc0e05f2be --- /dev/null +++ b/indra/llcorehttp/tests/test_bufferstream.hpp @@ -0,0 +1,304 @@ +/** + * @file test_bufferstream.hpp + * @brief unit tests for the LLCore::BufferArrayStreamBuf/BufferArrayStream classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_BUFFER_STREAM_H_ +#define TEST_LLCORE_BUFFER_STREAM_H_ + +#include "bufferstream.h" + +#include <iostream> + +#include "test_allocator.h" +#include "llsd.h" +#include "llsdserialize.h" + + +using namespace LLCore; + + +namespace tut +{ + +struct BufferStreamTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group<BufferStreamTestData> BufferStreamTestGroupType; +typedef BufferStreamTestGroupType::object BufferStreamTestObjectType; +BufferStreamTestGroupType BufferStreamTestGroup("BufferStream Tests"); +typedef BufferArrayStreamBuf::traits_type tst_traits_t; + + +template <> template <> +void BufferStreamTestObjectType::test<1>() +{ + set_test_name("BufferArrayStreamBuf construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL + ensure("underflow() on NULL fails", tst_traits_t::eof() == bsb->underflow()); + ensure("uflow() on NULL fails", tst_traits_t::eof() == bsb->uflow()); + ensure("pbackfail() on NULL fails", tst_traits_t::eof() == bsb->pbackfail('c')); + ensure("showmanyc() on NULL fails", bsb->showmanyc() == -1); + ensure("overflow() on NULL fails", tst_traits_t::eof() == bsb->overflow('c')); + ensure("xsputn() on NULL fails", bsb->xsputn("blah", 4) == 0); + ensure("seekoff() on NULL fails", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(-1)); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<2>() +{ + set_test_name("BufferArrayStream construction with NULL BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + BufferArrayStream * bas = new BufferArrayStream(NULL); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Not much will work with a NULL here + ensure("eof() is false on NULL", ! bas->eof()); + ensure("fail() is false on NULL", ! bas->fail()); + ensure("good() on NULL", bas->good()); + + // release the implicit reference, causing the object to be released + delete bas; + bas = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<3>() +{ + set_test_name("BufferArrayStreamBuf construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<4>() +{ + set_test_name("BufferArrayStream construction with empty BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // create a new ref counted object with an implicit reference + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<5>() +{ + set_test_name("BufferArrayStreamBuf construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + const char * content("This is a string. A fragment."); + const size_t c_len(strlen(content)); + ba->append(content, c_len); + + // Creat an adapter for the BufferArray + BufferArrayStreamBuf * bsb = new BufferArrayStreamBuf(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // I can release my ref on the BA + ba->release(); + ba = NULL; + + // Various static state + ensure("underflow() returns 'T'", bsb->underflow() == 'T'); + ensure("underflow() returns 'T' again", bsb->underflow() == 'T'); + ensure("uflow() returns 'T'", bsb->uflow() == 'T'); + ensure("uflow() returns 'h'", bsb->uflow() == 'h'); + ensure("pbackfail('i') fails", tst_traits_t::eof() == bsb->pbackfail('i')); + ensure("pbackfail('T') fails", tst_traits_t::eof() == bsb->pbackfail('T')); + ensure("pbackfail('h') succeeds", bsb->pbackfail('h') == 'h'); + ensure("showmanyc() is everything but the 'T'", bsb->showmanyc() == (c_len - 1)); + ensure("overflow() appends", bsb->overflow('c') == 'c'); + ensure("showmanyc() reflects append", bsb->showmanyc() == (c_len - 1 + 1)); + ensure("xsputn() appends some more", bsb->xsputn("bla!", 4) == 4); + ensure("showmanyc() reflects 2nd append", bsb->showmanyc() == (c_len - 1 + 5)); + ensure("seekoff() succeeds", bsb->seekoff(0, std::ios_base::beg, std::ios_base::in) == std::streampos(0)); + ensure("seekoff() succeeds 2", bsb->seekoff(4, std::ios_base::cur, std::ios_base::in) == std::streampos(4)); + ensure("showmanyc() picks up seekoff", bsb->showmanyc() == (c_len + 5 - 4)); + ensure("seekoff() succeeds 3", bsb->seekoff(0, std::ios_base::end, std::ios_base::in) == std::streampos(c_len + 4)); + ensure("pbackfail('!') succeeds", tst_traits_t::eof() == bsb->pbackfail('!')); + + // release the implicit reference, causing the object to be released + delete bsb; + bsb = NULL; + + // make sure we didn't leak any memory + ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +template <> template <> +void BufferStreamTestObjectType::test<6>() +{ + set_test_name("BufferArrayStream construction with real BufferArray"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + //const char * content("This is a string. A fragment."); + //const size_t c_len(strlen(content)); + //ba->append(content, strlen(content)); + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Basic operations + bas << "Hello" << 27 << "."; + ensure("BA length 8", ba->size() == 8); + + std::string str; + bas >> str; + ensure("reads correctly", str == "Hello27."); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); + // static U64 mem = GetMemTotal(); +} + + +template <> template <> +void BufferStreamTestObjectType::test<7>() +{ + set_test_name("BufferArrayStream with LLSD serialization"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted BufferArray with implicit reference + BufferArray * ba = new BufferArray; + + { + // Creat an adapter for the BufferArray + BufferArrayStream bas(ba); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // LLSD + LLSD llsd = LLSD::emptyMap(); + + llsd["int"] = LLSD::Integer(3); + llsd["float"] = LLSD::Real(923289.28992); + llsd["string"] = LLSD::String("aksjdl;ajsdgfjgfal;sdgjakl;sdfjkl;ajsdfkl;ajsdfkl;jaskl;dfj"); + + LLSD llsd_map = LLSD::emptyMap(); + llsd_map["int"] = LLSD::Integer(-2889); + llsd_map["float"] = LLSD::Real(2.37829e32); + llsd_map["string"] = LLSD::String("OHIGODHSPDGHOSDHGOPSHDGP"); + + llsd["map"] = llsd_map; + + // Serialize it + LLSDSerialize::toXML(llsd, bas); + + std::string str; + bas >> str; + // std::cout << "SERIALIZED LLSD: " << str << std::endl; + ensure("Extracted string has reasonable length", str.size() > 60); + } + + // release the implicit reference, causing the object to be released + ba->release(); + ba = NULL; + + // make sure we didn't leak any memory + // ensure("Allocated memory returned", mMemTotal == GetMemTotal()); +} + + +} // end namespace tut + + +#endif // TEST_LLCORE_BUFFER_STREAM_H_ diff --git a/indra/llcorehttp/tests/test_httpheaders.hpp b/indra/llcorehttp/tests/test_httpheaders.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ce0d19b05842bc49c55b98fd1eaa807a7a6aa681 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpheaders.hpp @@ -0,0 +1,108 @@ +/** + * @file test_httpheaders.hpp + * @brief unit tests for the LLCore::HttpHeaders class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_HEADERS_H_ +#define TEST_LLCORE_HTTP_HEADERS_H_ + +#include "httpheaders.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpHeadersTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group<HttpHeadersTestData> HttpHeadersTestGroupType; +typedef HttpHeadersTestGroupType::object HttpHeadersTestObjectType; +HttpHeadersTestGroupType HttpHeadersTestGroup("HttpHeaders Tests"); + +template <> template <> +void HttpHeadersTestObjectType::test<1>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + ensure("One ref on construction of HttpHeaders", headers->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + ensure("Nothing in headers", 0 == headers->mHeaders.size()); + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpHeadersTestObjectType::test<2>() +{ + set_test_name("HttpHeaders construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpHeaders * headers = new HttpHeaders(); + + { + // Append a few strings + std::string str1("Pragma:"); + headers->mHeaders.push_back(str1); + std::string str2("Accept: application/json"); + headers->mHeaders.push_back(str2); + + ensure("Headers retained", 2 == headers->mHeaders.size()); + ensure("First is first", headers->mHeaders[0] == str1); + ensure("Second is second", headers->mHeaders[1] == str2); + } + + // release the implicit reference, causing the object to be released + headers->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_HEADERS_H_ diff --git a/indra/llcorehttp/tests/test_httpoperation.hpp b/indra/llcorehttp/tests/test_httpoperation.hpp new file mode 100644 index 0000000000000000000000000000000000000000..17b1a96878325e8240648d1f904cd65bf461cda7 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpoperation.hpp @@ -0,0 +1,125 @@ +/** + * @file test_httpoperation.hpp + * @brief unit tests for the LLCore::HttpOperation-derived classes + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_OPERATION_H_ +#define TEST_LLCORE_HTTP_OPERATION_H_ + +#include "_httpoperation.h" +#include "httphandler.h" + +#include <iostream> + +#include "test_allocator.h" + + +using namespace LLCoreInt; + + +namespace +{ + +class TestHandler : public LLCore::HttpHandler +{ +public: + virtual void onCompleted(HttpHandle, HttpResponse *) + { + std::cout << "TestHandler::onCompleted() invoked" << std::endl; + } + +}; + + +} // end namespace anonymous + + +namespace tut +{ + struct HttpOperationTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group<HttpOperationTestData> HttpOperationTestGroupType; + typedef HttpOperationTestGroupType::object HttpOperationTestObjectType; + HttpOperationTestGroupType HttpOperationTestGroup("HttpOperation Tests"); + + template <> template <> + void HttpOperationTestObjectType::test<1>() + { + set_test_name("HttpOpNull construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + ensure(op->getRefCount() == 1); + ensure(mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + op->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void HttpOperationTestObjectType::test<2>() + { + set_test_name("HttpOpNull construction with handlers"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // Get some handlers + TestHandler * h1 = new TestHandler(); + + // create a new ref counted object with an implicit reference + HttpOpNull * op = new HttpOpNull(); + + // Add the handlers + op->setReplyPath(NULL, h1); + + // Check ref count + ensure(op->getRefCount() == 1); + + // release the reference, releasing the operation but + // not the handlers. + op->release(); + op = NULL; + ensure(mMemTotal != GetMemTotal()); + + // release the handlers + delete h1; + h1 = NULL; + + ensure(mMemTotal == GetMemTotal()); + } + +} + +#endif // TEST_LLCORE_HTTP_OPERATION_H_ diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp new file mode 100644 index 0000000000000000000000000000000000000000..e5488cf941e7a8b7c0892965aa6664d596a44edf --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequest.hpp @@ -0,0 +1,2670 @@ +/** + * @file test_httprequest.hpp + * @brief unit tests for the LLCore::HttpRequest class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUEST_H_ +#define TEST_LLCORE_HTTP_REQUEST_H_ + +#include "httprequest.h" +#include "bufferarray.h" +#include "httphandler.h" +#include "httpheaders.h" +#include "httpresponse.h" +#include "httpoptions.h" +#include "_httpservice.h" +#include "_httprequestqueue.h" + +#include <curl/curl.h> +#include <boost/regex.hpp> +#include <sstream> + +#include "test_allocator.h" +#include "llcorehttp_test.h" + + +using namespace LLCoreInt; + + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec); + +#endif + +} + +namespace tut +{ + +struct HttpRequestTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + int mHandlerCalls; + HttpStatus mStatus; +}; + +class TestHandler2 : public LLCore::HttpHandler +{ +public: + TestHandler2(HttpRequestTestData * state, + const std::string & name) + : mState(state), + mName(name), + mExpectHandle(LLCORE_HTTP_HANDLE_INVALID) + {} + + virtual void onCompleted(HttpHandle handle, HttpResponse * response) + { + if (LLCORE_HTTP_HANDLE_INVALID != mExpectHandle) + { + ensure("Expected handle received in handler", mExpectHandle == handle); + } + ensure("Handler got a response", NULL != response); + if (response && mState) + { + const HttpStatus actual_status(response->getStatus()); + std::ostringstream test; + test << "Expected HttpStatus received in response. Wanted: " + << mState->mStatus.toHex() << " Received: " << actual_status.toHex(); + ensure(test.str().c_str(), actual_status == mState->mStatus); + } + if (mState) + { + mState->mHandlerCalls++; + } + if (! mHeadersRequired.empty() || ! mHeadersDisallowed.empty()) + { + ensure("Response required with header check", response != NULL); + HttpHeaders * header(response->getHeaders()); // Will not hold onto this + ensure("Some quantity of headers returned", header != NULL); + + if (! mHeadersRequired.empty()) + { + for (int i(0); i < mHeadersRequired.size(); ++i) + { + bool found = false; + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersRequired[i])) + { + found = true; + break; + } + } + std::ostringstream str; + str << "Required header # " << i << " found in response"; + ensure(str.str(), found); + } + } + + if (! mHeadersDisallowed.empty()) + { + for (int i(0); i < mHeadersDisallowed.size(); ++i) + { + for (HttpHeaders::container_t::const_iterator iter(header->mHeaders.begin()); + header->mHeaders.end() != iter; + ++iter) + { + if (boost::regex_match(*iter, mHeadersDisallowed[i])) + { + std::ostringstream str; + str << "Disallowed header # " << i << " not found in response"; + ensure(str.str(), false); + } + } + } + } + } + + if (! mCheckContentType.empty()) + { + ensure("Response required with content type check", response != NULL); + std::string con_type(response->getContentType()); + ensure("Content-Type as expected (" + mCheckContentType + ")", + mCheckContentType == con_type); + } + + // std::cout << "TestHandler2::onCompleted() invoked" << std::endl; + } + + HttpRequestTestData * mState; + std::string mName; + HttpHandle mExpectHandle; + std::string mCheckContentType; + std::vector<boost::regex> mHeadersRequired; + std::vector<boost::regex> mHeadersDisallowed; +}; + +typedef test_group<HttpRequestTestData> HttpRequestTestGroupType; +typedef HttpRequestTestGroupType::object HttpRequestTestObjectType; +HttpRequestTestGroupType HttpRequestTestGroup("HttpRequest Tests"); + +template <> template <> +void HttpRequestTestObjectType::test<1>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest construction"); + + HttpRequest * req = NULL; + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + try + { + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the request object + delete req; + req = NULL; + + HttpRequest::destroyService(); + + // make sure we didn't leak any memory + ensure("Memory returned", mMemTotal == GetMemTotal()); + } + catch (...) + { + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<2>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest and Null Op queued"); + + HttpRequest * req = NULL; + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + try + { + // Get singletons created + HttpRequest::createService(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(NULL); + ensure("Request issued", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release the request object + delete req; + req = NULL; + + // We're still holding onto the operation which is + // sitting, unserviced, on the request queue so... + ensure("Memory being used 2", mMemTotal < GetMemTotal()); + + // Request queue should have two references: global singleton & service object + ensure("Two references to request queue", 2 == HttpRequestQueue::instanceOf()->getRefCount()); + + // Okay, tear it down + HttpRequest::destroyService(); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory returned", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<3>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest NoOp + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a NoOp + HttpHandle handle = req->requestNoOp(&handler); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<4>() +{ + ScopedCurlInit ready; + + set_test_name("2 HttpRequest instances, one thread"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + TestHandler2 handler1(this, "handler1"); + TestHandler2 handler2(this, "handler2"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req1 = NULL; + HttpRequest * req2 = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req1 = new HttpRequest(); + req2 = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue some NoOps + HttpHandle handle = req1->requestNoOp(&handler1); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler1.mExpectHandle = handle; + + handle = req2->requestNoOp(&handler2); + ensure("Valid handle returned for first request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump. + int count(0); + int limit(20); + while (count++ < limit && mHandlerCalls < 2) + { + req1->update(1000000); + req2->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + // Okay, request a shutdown of the servicing thread + handle = req2->requestStopThread(&handler2); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + handler2.mExpectHandle = handle; + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 3) + { + req1->update(1000000); + req2->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req1; + req1 = NULL; + delete req2; + req2 = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 3 == mHandlerCalls); + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + } + catch (...) + { + stop_thread(req1); + delete req1; + delete req2; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<5>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin (soft) + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(1); + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("NoOp notification received", mHandlerCalls == 1); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test should work but could give problems as it + // relies on the worker thread picking up a friendly request + // to shutdown. Doing so, it drops references to things and + // we should go back to where we started. If it gives you + // problems, look into the code before commenting things out. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<6>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest Spin + NoOp + hard termination"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a Spin + HttpHandle handle = req->requestSpin(0); // Hard spin + ensure("Valid handle returned for spin request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Issue a NoOp + handle = req->requestNoOp(&handler); + ensure("Valid handle returned for no-op request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("No notifications received", mHandlerCalls == 0); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + // Check memory usage + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + // ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); + // This memory test won't work because we're killing the thread + // hard with the hard spinner. There's no opportunity to join + // nicely so many things leak or get destroyed unilaterally. + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<7>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET to dead port + Stop execution"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(1); // Don't try for too long - default retries take about 18S + + // Issue a GET that can't connect + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + "http://127.0.0.1:2/nothing/here", + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<8>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<9>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Range: header to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<10>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest PUT to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<11>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest POST to real service"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + BufferArray * body = new BufferArray; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + static const char * body_text("Now is the time for all good men..."); + body->append(body_text, strlen(body_text)); + mStatus = HttpStatus(200); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + body, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // Lose the request body + body->release(); + body = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + if (body) + { + body->release(); + } + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + +template <> template <> +void HttpRequestTestObjectType::test<12>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with some tracing"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if 0 // defined(WIN32) + // Can't do this on any platform anymore, the LL logging system holds + // on to memory and produces what looks like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<13>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with returned headers"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + handler.mHeadersRequired.reserve(20); // Avoid memory leak test failure + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Enable tracing + HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, 2); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setWantHeaders(true); + + // Issue a GET that succeeds + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("\\W*X-LL-Special:.*", boost::regex::icase)); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // release options + opts->release(); + opts = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +template <> template <> +void HttpRequestTestObjectType::test<14>() +{ + ScopedCurlInit ready; + + set_test_name("HttpRequest GET timeout"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * opts = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + opts = new HttpOptions(); + opts->setRetries(0); // Don't retry + opts->setTimeout(2); + + // Issue a GET that sleeps + mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT); + HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + 0, + 0, + opts, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(50); // With one retry, should fail quickish + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 100; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options + opts->release(); + opts = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + if (opts) + { + opts->release(); + opts = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + +// Test retrieval of Content-Type/Content-Encoding headers +template <> template <> +void HttpRequestTestObjectType::test<15>() +{ + ScopedCurlInit ready; + + std::string url_base(get_base_url()); + // std::cerr << "Base: " << url_base << std::endl; + + set_test_name("HttpRequest GET with Content-Type"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // Load and clear the string setting to preload std::string object + // for memory return tests. + handler.mCheckContentType = "application/llsd+xml"; + handler.mCheckContentType.clear(); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + ensure("Memory allocated on construction", mMemTotal < GetMemTotal()); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mCheckContentType = "application/llsd+xml"; + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base, + NULL, + NULL, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mCheckContentType.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + + ensure("Two handler calls on the way out", 2 == mHandlerCalls); + +#if defined(WIN32) + // Can only do this memory test on Windows. On other platforms, + // the LL logging system holds on to memory and produces what looks + // like memory leaks... + + // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal()); + ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal()); +#endif + } + catch (...) + { + stop_thread(req); + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on GET requests +template <> template <> +void HttpRequestTestObjectType::test<16>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // Issue a GET that *can* connect + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Do a texture-style fetch + headers = new HttpHeaders; + headers->mHeaders.push_back("Accept: image/x-j2c"); + + mStatus = HttpStatus(200); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*image/x-j2c", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\W*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + 0, + 47, + options, + headers, + &handler); + ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 2); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 3) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 3); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on POST requests +template <> template <> +void HttpRequestTestObjectType::test<17>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests +template <> template <> +void HttpRequestTestObjectType::test<18>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // And a buffer array + const char * msg("It was the best of times, it was the worst of times."); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:.*", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + NULL, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on GET requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<19>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest GET with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("Keep-Alive: 120"); + headers->mHeaders.push_back("Accept-encoding: deflate"); + headers->mHeaders.push_back("Accept: text/plain"); + + // Issue a GET with modified headers + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/plain", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*deflate", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-type:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + HttpHandle handle = req->requestGet(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on POST requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<20>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest POST with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders(); + headers->mHeaders.push_back("keep-Alive: 120"); + headers->mHeaders.push_back("Accept: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + headers->mHeaders.push_back("cache-control: no-store"); + + // And a buffer array + const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>"); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default POST + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*text/html", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*120", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("\\s*X-Reflect-cache-control:\\s*no-store", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*application/x-www-form-urlencoded", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-keep-alive:\\s*300", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + HttpHandle handle = req->requestPost(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +// Test header generation on PUT requests with overrides +template <> template <> +void HttpRequestTestObjectType::test<21>() +{ + ScopedCurlInit ready; + + // Warmup boost::regex to pre-alloc memory for memory size tests + boost::regex warmup("askldjflasdj;f", boost::regex::icase); + boost::regex_match("akl;sjflajfk;ajsk", warmup); + + std::string url_base(get_base_url()); + + set_test_name("Header generation for HttpRequest PUT with header overrides"); + + // Handler can be stack-allocated *if* there are no dangling + // references to it after completion of this method. + // Create before memory record as the string copy will bump numbers. + TestHandler2 handler(this, "handler"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + mHandlerCalls = 0; + + HttpRequest * req = NULL; + HttpOptions * options = NULL; + HttpHeaders * headers = NULL; + BufferArray * ba = NULL; + + try + { + // Get singletons created + HttpRequest::createService(); + + // Start threading early so that thread memory is invariant + // over the test. + HttpRequest::startThread(); + + // create a new ref counted object with an implicit reference + req = new HttpRequest(); + + // options set + options = new HttpOptions(); + options->setWantHeaders(true); + + // headers + headers = new HttpHeaders; + headers->mHeaders.push_back("content-type: text/plain"); + headers->mHeaders.push_back("content-type: text/html"); + headers->mHeaders.push_back("content-type: application/llsd+xml"); + + // And a buffer array + const char * msg("<xml><llsd><string>It was the best of times, it was the worst of times.</string></llsd></xml>"); + ba = new BufferArray; + ba->append(msg, strlen(msg)); + + // Issue a default PUT + mStatus = HttpStatus(200); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-connection:\\s*keep-alive", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept:\\s*\\*/\\*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-accept-encoding:\\s*((gzip|deflate),\\s*)+(gzip|deflate)", boost::regex::icase)); // close enough + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-keep-alive:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-host:\\s*.*", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-length:\\s*\\d+", boost::regex::icase)); + handler.mHeadersRequired.push_back(boost::regex("X-Reflect-content-type:\\s*application/llsd\\+xml", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-cache-control:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-pragma:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-range:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-referer:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-content-encoding:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-expect:.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("\\s*X-Reflect-transfer-encoding:\\s*.*chunked.*", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/plain", boost::regex::icase)); + handler.mHeadersDisallowed.push_back(boost::regex("X-Reflect-content-type:\\s*text/html", boost::regex::icase)); + HttpHandle handle = req->requestPut(HttpRequest::DEFAULT_POLICY_ID, + 0U, + url_base + "reflect/", + ba, + options, + headers, + &handler); + ensure("Valid handle returned for get request", handle != LLCORE_HTTP_HANDLE_INVALID); + ba->release(); + ba = NULL; + + // Run the notification pump. + int count(0); + int limit(10); + while (count++ < limit && mHandlerCalls < 1) + { + req->update(1000000); + usleep(100000); + } + ensure("Request executed in reasonable time", count < limit); + ensure("One handler invocation for request", mHandlerCalls == 1); + + + // Okay, request a shutdown of the servicing thread + mStatus = HttpStatus(); + handler.mHeadersRequired.clear(); + handler.mHeadersDisallowed.clear(); + handle = req->requestStopThread(&handler); + ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID); + + // Run the notification pump again + count = 0; + limit = 10; + while (count++ < limit && mHandlerCalls < 2) + { + req->update(1000000); + usleep(100000); + } + ensure("Second request executed in reasonable time", count < limit); + ensure("Second handler invocation", mHandlerCalls == 2); + + // See that we actually shutdown the thread + count = 0; + limit = 10; + while (count++ < limit && ! HttpService::isStopped()) + { + usleep(100000); + } + ensure("Thread actually stopped running", HttpService::isStopped()); + + // release options & headers + if (options) + { + options->release(); + } + options = NULL; + + if (headers) + { + headers->release(); + } + headers = NULL; + + // release the request object + delete req; + req = NULL; + + // Shut down service + HttpRequest::destroyService(); + } + catch (...) + { + stop_thread(req); + if (ba) + { + ba->release(); + ba = NULL; + } + if (options) + { + options->release(); + options = NULL; + } + if (headers) + { + headers->release(); + headers = NULL; + } + delete req; + HttpRequest::destroyService(); + throw; + } +} + + +} // end namespace tut + +namespace +{ + +#if defined(WIN32) + +void usleep(unsigned long usec) +{ + Sleep((DWORD) (usec / 1000UL)); +} + +#endif + +} + +#endif // TEST_LLCORE_HTTP_REQUEST_H_ diff --git a/indra/llcorehttp/tests/test_httprequestqueue.hpp b/indra/llcorehttp/tests/test_httprequestqueue.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1de2d8f9ab042c187f963f7272eb44c4f6800075 --- /dev/null +++ b/indra/llcorehttp/tests/test_httprequestqueue.hpp @@ -0,0 +1,186 @@ +/** + * @file test_httprequestqueue.hpp + * @brief unit tests for the LLCore::HttpRequestQueue class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCORE_HTTP_REQUESTQUEUE_H_ +#define TEST_LLCORE_HTTP_REQUESTQUEUE_H_ + +#include "_httprequestqueue.h" + +#include <iostream> + +#include "test_allocator.h" +#include "_httpoperation.h" + + +using namespace LLCoreInt; + + + +namespace tut +{ + +struct HttpRequestqueueTestData +{ + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; +}; + +typedef test_group<HttpRequestqueueTestData> HttpRequestqueueTestGroupType; +typedef HttpRequestqueueTestGroupType::object HttpRequestqueueTestObjectType; +HttpRequestqueueTestGroupType HttpRequestqueueTestGroup("HttpRequestqueue Tests"); + +template <> template <> +void HttpRequestqueueTestObjectType::test<1>() +{ + set_test_name("HttpRequestQueue construction"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + ensure("One ref on construction of HttpRequestQueue", HttpRequestQueue::instanceOf()->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // release the implicit reference, causing the object to be released + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<2>() +{ + set_test_name("HttpRequestQueue refcount works"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + rq->addRef(); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + ensure("One ref after term() called", rq->getRefCount() == 1); + ensure("Memory being used", mMemTotal < GetMemTotal()); + + // Drop ref + rq->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<3>() +{ + set_test_name("HttpRequestQueue addOp/fetchOp work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + + rq->addOp(op); // transfer my refcount + + op = rq->fetchOp(true); // Potentially hangs the test on failure + ensure("One goes in, one comes out", NULL != op); + op->release(); + + op = rq->fetchOp(false); + ensure("Better not be two of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); +} + +template <> template <> +void HttpRequestqueueTestObjectType::test<4>() +{ + set_test_name("HttpRequestQueue addOp/fetchAll work"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + HttpRequestQueue::init(); + + HttpRequestQueue * rq = HttpRequestQueue::instanceOf(); + + HttpOperation * op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + op = new HttpOpNull(); + rq->addOp(op); // transfer my refcount + + { + HttpRequestQueue::OpContainer ops; + rq->fetchAll(true, ops); // Potentially hangs the test on failure + ensure("Three go in, three come out", 3 == ops.size()); + + op = rq->fetchOp(false); + ensure("Better not be any more of them", NULL == op); + + // release the singleton, hold on to the object + HttpRequestQueue::term(); + + // We're still holding onto the ops. + ensure(mMemTotal < GetMemTotal()); + + // Release them + while (! ops.empty()) + { + HttpOperation * op = ops.front(); + ops.erase(ops.begin()); + op->release(); + } + } + + // Should be clean + ensure("All memory returned", mMemTotal == GetMemTotal()); +} + +} // end namespace tut + + +#endif // TEST_LLCORE_HTTP_REQUESTQUEUE_H_ diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f7b542d3b5e110b709e3b4c0dcdfd29a5d3e1838 --- /dev/null +++ b/indra/llcorehttp/tests/test_httpstatus.hpp @@ -0,0 +1,265 @@ +/** + * @file test_llrefcounted + * @brief unit tests for HttpStatus struct + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef TEST_HTTP_STATUS_H_ +#define TEST_HTTP_STATUS_H_ + +#include "httpcommon.h" + +#include <curl/curl.h> +#include <curl/multi.h> + +using namespace LLCore; + +namespace tut +{ + +struct HttpStatusTestData +{ + HttpStatusTestData() + {} +}; + +typedef test_group<HttpStatusTestData> HttpStatusTestGroupType; +typedef HttpStatusTestGroupType::object HttpStatusTestObjectType; + +HttpStatusTestGroupType HttpStatusTestGroup("HttpStatus Tests"); + +template <> template <> +void HttpStatusTestObjectType::test<1>() +{ + set_test_name("HttpStatus construction"); + + // auto allocation fine for this + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = 0; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SUCCESS; + + ensure(bool(status)); + ensure(false == !(status)); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -1; + + ensure(false == bool(status)); + ensure(!(status)); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_DOWNLOAD_RESUME; + + ensure(false == bool(status)); + ensure(!(status)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<2>() +{ + set_test_name("HttpStatus memory structure"); + + // Require that an HttpStatus object can be trivially + // returned as a function return value in registers. + // One should fit in an int on all platforms. + + ensure(sizeof(HttpStatus) <= sizeof(int)); +} + + +template <> template <> +void HttpStatusTestObjectType::test<3>() +{ + set_test_name("HttpStatus valid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 0; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(msg.empty()); + + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = CURLE_BAD_FUNCTION_ARGUMENT; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = CURLM_OUT_OF_MEMORY; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = HE_SHUTTING_DOWN; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + + +template <> template <> +void HttpStatusTestObjectType::test<4>() +{ + set_test_name("HttpStatus invalid error string conversion"); + + HttpStatus status; + status.mType = HttpStatus::EXT_CURL_EASY; + status.mStatus = 32726; + std::string msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::EXT_CURL_MULTI; + status.mStatus = -470; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); + + status.mType = HttpStatus::LLCORE; + status.mStatus = 923; + msg = status.toString(); + // std::cout << "Result: " << msg << std::endl; + ensure(! msg.empty()); +} + +template <> template <> +void HttpStatusTestObjectType::test<5>() +{ + set_test_name("HttpStatus equality/inequality testing"); + + // Make certain equality/inequality tests do not pass + // through the bool conversion. Distinct successful + // and error statuses should compare unequal. + + HttpStatus status1(HttpStatus::LLCORE, HE_SUCCESS); + HttpStatus status2(HttpStatus::EXT_CURL_EASY, HE_SUCCESS); + ensure(status1 != status2); + + status1.mType = HttpStatus::LLCORE; + status1.mStatus = HE_REPLY_ERROR; + status2.mType = HttpStatus::LLCORE; + status2.mStatus= HE_SHUTTING_DOWN; + ensure(status1 != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<6>() +{ + set_test_name("HttpStatus basic HTTP status encoding"); + + HttpStatus status; + status.mType = 200; + status.mStatus = HE_SUCCESS; + std::string msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Normally a success but application says error + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(! msg.empty()); + ensure(! bool(status)); + ensure(status.toULong() > 1UL); // Biggish number, not a bool-to-ulong + + // Same statuses with distinct success/fail are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + HttpStatus status2(200, HE_REPLY_ERROR); + ensure(status != status2); + + // Normally an error but application says okay + status.mType = 406; + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); + ensure(bool(status)); + + // Different statuses but both successful are distinct + status.mType = 200; + status.mStatus = HE_SUCCESS; + status2.mType = 201; + status2.mStatus = HE_SUCCESS; + ensure(status != status2); + + // Different statuses but both failed are distinct + status.mType = 200; + status.mStatus = HE_REPLY_ERROR; + status2.mType = 201; + status2.mStatus = HE_REPLY_ERROR; + ensure(status != status2); +} + +template <> template <> +void HttpStatusTestObjectType::test<7>() +{ + set_test_name("HttpStatus HTTP error text strings"); + + HttpStatus status(100, HE_REPLY_ERROR); + std::string msg(status.toString()); + ensure(! msg.empty()); // Should be something + ensure(msg == "Continue"); + + status.mStatus = HE_SUCCESS; + msg = status.toString(); + ensure(msg.empty()); // Success is empty + + status.mType = 199; + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 505; // Last defined string + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "HTTP Version not supported"); + + status.mType = 506; // One beyond + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); + + status.mType = 999; // Last HTTP status + status.mStatus = HE_REPLY_ERROR; + msg = status.toString(); + ensure(msg == "Unknown error"); +} + +} // end namespace tut + +#endif // TEST_HTTP_STATUS_H + diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py new file mode 100644 index 0000000000000000000000000000000000000000..75a3c39ef2f047f3880159ee288806d523336fd9 --- /dev/null +++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python +"""\ +@file test_llsdmessage_peer.py +@author Nat Goodspeed +@date 2008-10-09 +@brief This script asynchronously runs the executable (with args) specified on + the command line, returning its result code. While that executable is + running, we provide dummy local services for use by C++ tests. + +$LicenseInfo:firstyear=2008&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2012, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +import os +import sys +import time +import select +import getopt +from threading import Thread +from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler +from SocketServer import ThreadingMixIn + +mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/ +sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python")) +from indra.util.fastest_elementtree import parse as xml_parse +from indra.base import llsd +from testrunner import freeport, run, debug, VERBOSE + +class TestHTTPRequestHandler(BaseHTTPRequestHandler): + """This subclass of BaseHTTPRequestHandler is to receive and echo + LLSD-flavored messages sent by the C++ LLHTTPClient. + """ + def read(self): + # The following logic is adapted from the library module + # SimpleXMLRPCServer.py. + # Get arguments by reading body of request. + # We read this in chunks to avoid straining + # socket.read(); around the 10 or 15Mb mark, some platforms + # begin to have problems (bug #792570). + try: + size_remaining = int(self.headers["content-length"]) + except (KeyError, ValueError): + return "" + max_chunk_size = 10*1024*1024 + L = [] + while size_remaining: + chunk_size = min(size_remaining, max_chunk_size) + chunk = self.rfile.read(chunk_size) + L.append(chunk) + size_remaining -= len(chunk) + return ''.join(L) + # end of swiped read() logic + + def read_xml(self): + # This approach reads the entire POST data into memory first + return llsd.parse(self.read()) +## # This approach attempts to stream in the LLSD XML from self.rfile, +## # assuming that the underlying XML parser reads its input file +## # incrementally. Unfortunately I haven't been able to make it work. +## tree = xml_parse(self.rfile) +## debug("Finished raw parse") +## debug("parsed XML tree %s", tree) +## debug("parsed root node %s", tree.getroot()) +## debug("root node tag %s", tree.getroot().tag) +## return llsd.to_python(tree.getroot()) + + def do_HEAD(self): + self.do_GET(withdata=False) + + def do_GET(self, withdata=True): + # Of course, don't attempt to read data. + self.answer(dict(reply="success", status=200, + reason="Your GET operation worked")) + + def do_POST(self): + # Read the provided POST data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def do_PUT(self): + # Read the provided PUT data. + # self.answer(self.read()) + self.answer(dict(reply="success", status=200, + reason=self.read())) + + def answer(self, data, withdata=True): + debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path) + if "/sleep/" in self.path: + time.sleep(30) + + if "fail" not in self.path: + data = data.copy() # we're going to modify + # Ensure there's a "reply" key in data, even if there wasn't before + data["reply"] = data.get("reply", llsd.LLSD("success")) + response = llsd.format_xml(data) + debug("success: %s", response) + self.send_response(200) + if "/reflect/" in self.path: + self.reflect_headers() + self.send_header("Content-type", "application/llsd+xml") + self.send_header("Content-Length", str(len(response))) + self.send_header("X-LL-Special", "Mememememe"); + self.end_headers() + if withdata: + self.wfile.write(response) + else: # fail requested + status = data.get("status", 500) + # self.responses maps an int status to a (short, long) pair of + # strings. We want the longer string. That's why we pass a string + # pair to get(): the [1] will select the second string, whether it + # came from self.responses or from our default pair. + reason = data.get("reason", + self.responses.get(status, + ("fail requested", + "Your request specified failure status %s " + "without providing a reason" % status))[1]) + debug("fail requested: %s: %r", status, reason) + self.send_error(status, reason) + if "/reflect/" in self.path: + self.reflect_headers() + self.end_headers() + + def reflect_headers(self): + for name in self.headers.keys(): + # print "Header: %s: %s" % (name, self.headers[name]) + self.send_header("X-Reflect-" + name, self.headers[name]) + + if not VERBOSE: + # When VERBOSE is set, skip both these overrides because they exist to + # suppress output. + + def log_request(self, code, size=None): + # For present purposes, we don't want the request splattered onto + # stderr, as it would upset devs watching the test run + pass + + def log_error(self, format, *args): + # Suppress error output as well + pass + +class Server(ThreadingMixIn, HTTPServer): + # This pernicious flag is on by default in HTTPServer. But proper + # operation of freeport() absolutely depends on it being off. + allow_reuse_address = False + +if __name__ == "__main__": + do_valgrind = False + path_search = False + options, args = getopt.getopt(sys.argv[1:], "V", ["valgrind"]) + for option, value in options: + if option == "-V" or option == "--valgrind": + do_valgrind = True + + # Instantiate a Server(TestHTTPRequestHandler) on the first free port + # in the specified port range. Doing this inline is better than in a + # daemon thread: if it blows up here, we'll get a traceback. If it blew up + # in some other thread, the traceback would get eaten and we'd run the + # subject test program anyway. + httpd, port = freeport(xrange(8000, 8020), + lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)) + + # Pass the selected port number to the subject test program via the + # environment. We don't want to impose requirements on the test program's + # command-line parsing -- and anyway, for C++ integration tests, that's + # performed in TUT code rather than our own. + os.environ["LL_TEST_PORT"] = str(port) + debug("$LL_TEST_PORT = %s", port) + if do_valgrind: + args = ["valgrind", "--log-file=./valgrind.log"] + args + path_search = True + sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args)) diff --git a/indra/llcorehttp/tests/test_refcounted.hpp b/indra/llcorehttp/tests/test_refcounted.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cb4b50287af38a0a90f3da7b0af285f09293b7ee --- /dev/null +++ b/indra/llcorehttp/tests/test_refcounted.hpp @@ -0,0 +1,156 @@ +/** + * @file test_refcounted.hpp + * @brief unit tests for the LLCoreInt::RefCounted class + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ +#ifndef TEST_LLCOREINT_REF_COUNTED_H_ +#define TEST_LLCOREINT_REF_COUNTED_H_ + +#include "_refcounted.h" + +#include "test_allocator.h" + +using namespace LLCoreInt; + +namespace tut +{ + struct RefCountedTestData + { + // the test objects inherit from this so the member functions and variables + // can be referenced directly inside of the test functions. + size_t mMemTotal; + }; + + typedef test_group<RefCountedTestData> RefCountedTestGroupType; + typedef RefCountedTestGroupType::object RefCountedTestObjectType; + RefCountedTestGroupType RefCountedTestGroup("RefCounted Tests"); + + template <> template <> + void RefCountedTestObjectType::test<1>() + { + set_test_name("RefCounted construction with implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(true); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<2>() + { + set_test_name("RefCounted construction without implicit count"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + // create a new ref counted object with an implicit reference + RefCounted * rc = new RefCounted(false); + ensure(rc->getRefCount() == 0); + + // add a reference + rc->addRef(); + ensure(rc->getRefCount() == 1); + + // release the implicit reference, causing the object to be released + rc->release(); + + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<3>() + { + set_test_name("RefCounted addRef and release"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + for (int i = 0; i < 1024; ++i) + { + rc->addRef(); + } + + ensure(rc->getRefCount() == 1024); + + for (int i = 0; i < 1024; ++i) + { + rc->release(); + } + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<4>() + { + set_test_name("RefCounted isLastRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(true); + + // with only one reference, isLastRef should be true + ensure(rc->isLastRef()); + + // release it to clean up memory + rc->release(); + + // make sure we didn't leak any memory + ensure(mMemTotal == GetMemTotal()); + } + + template <> template <> + void RefCountedTestObjectType::test<5>() + { + set_test_name("RefCounted noRef check"); + + // record the total amount of dynamically allocated memory + mMemTotal = GetMemTotal(); + + RefCounted * rc = new RefCounted(false); + + // set the noRef + rc->noRef(); + + // with only one reference, isLastRef should be true + ensure(rc->getRefCount() == RefCounted::NOT_REF_COUNTED); + + // allow this memory leak, but check that we're leaking a known amount + ensure(mMemTotal == (GetMemTotal() - sizeof(RefCounted))); + } +} + +#endif // TEST_LLCOREINT_REF_COUNTED_H_ diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py new file mode 100644 index 0000000000000000000000000000000000000000..9a2de711425359cb4d0053139cde7d0dc5200981 --- /dev/null +++ b/indra/llcorehttp/tests/testrunner.py @@ -0,0 +1,265 @@ +#!/usr/bin/env python +"""\ +@file testrunner.py +@author Nat Goodspeed +@date 2009-03-20 +@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests + +$LicenseInfo:firstyear=2009&license=viewerlgpl$ +Second Life Viewer Source Code +Copyright (C) 2010, Linden Research, Inc. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; +version 2.1 of the License only. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA +$/LicenseInfo$ +""" + +from __future__ import with_statement + +import os +import sys +import re +import errno +import socket + +VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet +# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if +# that construct actually turns on verbosity... +VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE) + +if VERBOSE: + def debug(fmt, *args): + print fmt % args + sys.stdout.flush() +else: + debug = lambda *args: None + +def freeport(portlist, expr): + """ + Find a free server port to use. Specifically, evaluate 'expr' (a + callable(port)) until it stops raising EADDRINUSE exception. + + Pass: + + portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the + range, freeport() lets the socket.error exception propagate. If you want + unbounded, you could pass itertools.count(baseport), though of course in + practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain + the range much more sharply: if we're iterating an absurd number of times, + probably something else is wrong. + + expr: a callable accepting a port number, specifically one of the items + from portlist. If calling that callable raises socket.error with + EADDRINUSE, freeport() retrieves the next item from portlist and retries. + + Returns: (expr(port), port) + + port: the value from portlist for which expr(port) succeeded + + Raises: + + Any exception raised by expr(port) other than EADDRINUSE. + + socket.error if, for every item from portlist, expr(port) raises + socket.error. The exception you see is the one from the last item in + portlist. + + StopIteration if portlist is completely empty. + + Example: + + class Server(HTTPServer): + # If you use BaseHTTPServer.HTTPServer, turning off this flag is + # essential for proper operation of freeport()! + allow_reuse_address = False + # ... + server, port = freeport(xrange(8000, 8010), + lambda port: Server(("localhost", port), + MyRequestHandler)) + # pass 'port' to client code + # call server.serve_forever() + """ + try: + # If portlist is completely empty, let StopIteration propagate: that's an + # error because we can't return meaningful values. We have no 'port', + # therefore no 'expr(port)'. + portiter = iter(portlist) + port = portiter.next() + + while True: + try: + # If this value of port works, return as promised. + value = expr(port) + + except socket.error, err: + # Anything other than 'Address already in use', propagate + if err.args[0] != errno.EADDRINUSE: + raise + + # Here we want the next port from portiter. But on StopIteration, + # we want to raise the original exception rather than + # StopIteration. So save the original exc_info(). + type, value, tb = sys.exc_info() + try: + try: + port = portiter.next() + except StopIteration: + raise type, value, tb + finally: + # Clean up local traceback, see docs for sys.exc_info() + del tb + + else: + debug("freeport() returning %s on port %s", value, port) + return value, port + + # Recap of the control flow above: + # If expr(port) doesn't raise, return as promised. + # If expr(port) raises anything but EADDRINUSE, propagate that + # exception. + # If portiter.next() raises StopIteration -- that is, if the port + # value we just passed to expr(port) was the last available -- reraise + # the EADDRINUSE exception. + # If we've actually arrived at this point, portiter.next() delivered a + # new port value. Loop back to pass that to expr(port). + + except Exception, err: + debug("*** freeport() raising %s: %s", err.__class__.__name__, err) + raise + +def run(*args, **kwds): + """All positional arguments collectively form a command line, executed as + a synchronous child process. + In addition, pass server=new_thread_instance as an explicit keyword (to + differentiate it from an additional command-line argument). + new_thread_instance should be an instantiated but not yet started Thread + subclass instance, e.g.: + run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd")) + """ + # If there's no server= keyword arg, don't start a server thread: simply + # run a child process. + try: + thread = kwds.pop("server") + except KeyError: + pass + else: + # Start server thread. Note that this and all other comm server + # threads should be daemon threads: we'll let them run "forever," + # confident that the whole process will terminate when the main thread + # terminates, which will be when the child process terminates. + thread.setDaemon(True) + thread.start() + # choice of os.spawnv(): + # - [v vs. l] pass a list of args vs. individual arguments, + # - [no p] don't use the PATH because we specifically want to invoke the + # executable passed as our first arg, + # - [no e] child should inherit this process's environment. + debug("Running %s...", " ".join(args)) + if kwds.get("use_path", False): + rc = os.spawnvp(os.P_WAIT, args[0], args) + else: + rc = os.spawnv(os.P_WAIT, args[0], args) + debug("%s returned %s", args[0], rc) + return rc + +# **************************************************************************** +# test code -- manual at this point, see SWAT-564 +# **************************************************************************** +def test_freeport(): + # ------------------------------- Helpers -------------------------------- + from contextlib import contextmanager + # helper Context Manager for expecting an exception + # with exc(SomeError): + # raise SomeError() + # raises AssertionError otherwise. + @contextmanager + def exc(exception_class, *args): + try: + yield + except exception_class, err: + for i, expected_arg in enumerate(args): + assert expected_arg == err.args[i], \ + "Raised %s, but args[%s] is %r instead of %r" % \ + (err.__class__.__name__, i, err.args[i], expected_arg) + print "Caught expected exception %s(%s)" % \ + (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args)) + else: + assert False, "Failed to raise " + exception_class.__class__.__name__ + + # helper to raise specified exception + def raiser(exception): + raise exception + + # the usual + def assert_equals(a, b): + assert a == b, "%r != %r" % (a, b) + + # ------------------------ Sanity check the above ------------------------ + class SomeError(Exception): pass + # Without extra args, accept any err.args value + with exc(SomeError): + raiser(SomeError("abc")) + # With extra args, accept only the specified value + with exc(SomeError, "abc"): + raiser(SomeError("abc")) + with exc(AssertionError): + with exc(SomeError, "abc"): + raiser(SomeError("def")) + with exc(AssertionError): + with exc(socket.error, errno.EADDRINUSE): + raiser(socket.error(errno.ECONNREFUSED, 'Connection refused')) + + # ----------- freeport() without engaging socket functionality ----------- + # If portlist is empty, freeport() raises StopIteration. + with exc(StopIteration): + freeport([], None) + + assert_equals(freeport([17], str), ("17", 17)) + + # This is the magic exception that should prompt us to retry + inuse = socket.error(errno.EADDRINUSE, 'Address already in use') + # Get the iterator to our ports list so we can check later if we've used all + ports = iter(xrange(5)) + with exc(socket.error, errno.EADDRINUSE): + freeport(ports, lambda port: raiser(inuse)) + # did we entirely exhaust 'ports'? + with exc(StopIteration): + ports.next() + + ports = iter(xrange(2)) + # Any exception but EADDRINUSE should quit immediately + with exc(SomeError): + freeport(ports, lambda port: raiser(SomeError())) + assert_equals(ports.next(), 1) + + # ----------- freeport() with platform-dependent socket stuff ------------ + # This is what we should've had unit tests to begin with (see CHOP-661). + def newbind(port): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('127.0.0.1', port)) + return sock + + bound0, port0 = freeport(xrange(7777, 7780), newbind) + assert_equals(port0, 7777) + bound1, port1 = freeport(xrange(7777, 7780), newbind) + assert_equals(port1, 7778) + bound2, port2 = freeport(xrange(7777, 7780), newbind) + assert_equals(port2, 7779) + with exc(socket.error, errno.EADDRINUSE): + bound3, port3 = freeport(xrange(7777, 7780), newbind) + +if __name__ == "__main__": + test_freeport() diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index 0d01dd0e3e72874dbd8171898ddbaa0afe364ff0..8ffa8e4271120ba59a81efe9be0066547d0c1575 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -294,6 +294,8 @@ LLCurl::Easy* LLCurl::Easy::getEasy() // multi handles cache if they are added to one. CURLcode result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_DNS_CACHE_TIMEOUT, 0); check_curl_code(result); + result = curl_easy_setopt(easy->mCurlEasyHandle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + check_curl_code(result); ++gCurlEasyCount; return easy; @@ -482,7 +484,8 @@ void LLCurl::Easy::prepRequest(const std::string& url, //setopt(CURLOPT_VERBOSE, 1); // useful for debugging setopt(CURLOPT_NOSIGNAL, 1); - + setopt(CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + // Set the CURL options for either Socks or HTTP proxy LLProxy::getInstance()->applyProxySettings(this); diff --git a/indra/llmessage/llhttpassetstorage.cpp b/indra/llmessage/llhttpassetstorage.cpp index 612d765969efb92fa21695cac11faadc3e28bde3..d6ed08055e7764052586b9181ec1072649217883 100644 --- a/indra/llmessage/llhttpassetstorage.cpp +++ b/indra/llmessage/llhttpassetstorage.cpp @@ -749,7 +749,7 @@ LLAssetRequest* LLHTTPAssetStorage::findNextRequest(LLAssetStorage::request_list request_list_t::iterator pending_iter = pending.begin(); request_list_t::iterator pending_end = pending.end(); // Loop over all pending requests until we miss finding it in the running list. - for (; pending_iter != pending.end(); ++pending_iter) + for (; pending_iter != pending_end; ++pending_iter) { LLAssetRequest* req = *pending_iter; // Look for this pending request in the running list. diff --git a/indra/llrender/llgl.h b/indra/llrender/llgl.h index 964495a3abce301eae301d196b1e3d917d0820db..d70e764769a12e7854939dc36bf3d1071a5b34c4 100644 --- a/indra/llrender/llgl.h +++ b/indra/llrender/llgl.h @@ -424,6 +424,10 @@ const U32 FENCE_WAIT_TIME_NANOSECONDS = 1000; //1 ms class LLGLFence { public: + virtual ~LLGLFence() + { + } + virtual void placeFence() = 0; virtual bool isCompleted() = 0; virtual void wait() = 0; diff --git a/indra/llrender/llrendertarget.cpp b/indra/llrender/llrendertarget.cpp index cc5c2323809b40c43b79f843fe19fbd0997558e2..846311a8d045cf459ede3a0e9c22ea7596c68671 100644 --- a/indra/llrender/llrendertarget.cpp +++ b/indra/llrender/llrendertarget.cpp @@ -163,10 +163,19 @@ bool LLRenderTarget::addColorAttachment(U32 color_fmt) } U32 offset = mTex.size(); - if (offset >= 4 || - (offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers))) + + if( offset >= 4 ) { - llerrs << "Too many color attachments!" << llendl; + llwarns << "Too many color attachments" << llendl; + llassert( offset < 4 ); + return false; + } + if( offset > 0 && (mFBO == 0 || !gGLManager.mHasDrawBuffers) ) + { + llwarns << "FBO not used or no drawbuffers available; mFBO=" << (U32)mFBO << " gGLManager.mHasDrawBuffers=" << (U32)gGLManager.mHasDrawBuffers << llendl; + llassert( mFBO != 0 ); + llassert( gGLManager.mHasDrawBuffers ); + return false; } U32 tex; diff --git a/indra/llrender/llvertexbuffer.cpp b/indra/llrender/llvertexbuffer.cpp index eadef93c890f871634b8ee1061d560f2d19c2f7e..2fe0aa0b725491fdcd62896bd456e2cad4895ec1 100644 --- a/indra/llrender/llvertexbuffer.cpp +++ b/indra/llrender/llvertexbuffer.cpp @@ -555,8 +555,21 @@ void LLVertexBuffer::drawArrays(U32 mode, const std::vector<LLVector3>& pos, con gGL.syncMatrices(); U32 count = pos.size(); - llassert_always(norm.size() >= pos.size()); - llassert_always(count > 0); + + llassert(norm.size() >= pos.size()); + llassert(count > 0); + + if( count == 0 ) + { + llwarns << "Called drawArrays with 0 vertices" << llendl; + return; + } + + if( norm.size() < pos.size() ) + { + llwarns << "Called drawArrays with #" << norm.size() << " normals and #" << pos.size() << " vertices" << llendl; + return; + } unbind(); diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 4a0b98527da426c81e46850e01b5e49959455774..2e9ab134266792cc29fb3b5762f4689881f64e32 100755 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,6 +18,7 @@ include(JsonCpp) include(LLAudio) include(LLCharacter) include(LLCommon) +include(LLCoreHttp) include(LLImage) include(LLImageJ2COJ) include(LLInventory) @@ -57,6 +58,7 @@ include_directories( ${LLAUDIO_INCLUDE_DIRS} ${LLCHARACTER_INCLUDE_DIRS} ${LLCOMMON_INCLUDE_DIRS} + ${LLCOREHTTP_INCLUDE_DIRS} ${LLPHYSICS_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLIMAGE_INCLUDE_DIRS} @@ -97,6 +99,7 @@ set(viewer_SOURCE_FILES llagentwearables.cpp llagentwearablesfetch.cpp llanimstatelabels.cpp + llappcorehttp.cpp llappearancemgr.cpp llappviewer.cpp llappviewerlistener.cpp @@ -672,6 +675,7 @@ set(viewer_HEADER_FILES llagentwearables.h llagentwearablesfetch.h llanimstatelabels.h + llappcorehttp.h llappearance.h llappearancemgr.h llappviewer.h @@ -1812,6 +1816,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} + ${LLCOREHTTP_LIBRARIES} ${LLCOMMON_LIBRARIES} ${NDOF_LIBRARY} ${HUNSPELL_LIBRARY} diff --git a/indra/newview/app_settings/logcontrol.xml b/indra/newview/app_settings/logcontrol.xml old mode 100755 new mode 100644 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 5e42fc29f79769d94397f78347d81edada7e912f..190dd5e2d54f3497d5707855478047f6c337d6fa 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -10789,6 +10789,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>TextureFetchConcurrency</key> + <map> + <key>Comment</key> + <string>Maximum number of HTTP connections used for texture fetches</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>U32</string> + <key>Value</key> + <integer>0</integer> + </map> <key>TextureFetchDebuggerEnabled</key> <map> <key>Comment</key> diff --git a/indra/newview/llagent.cpp b/indra/newview/llagent.cpp index 4782a71ba88d77603fdd2a6e62ab00ef737f4795..b6fd7bc9c20d9b02a55a9379e9c616fb4999878a 100755 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -4042,6 +4042,12 @@ void LLAgent::teleportViaLocation(const LLVector3d& pos_global) void LLAgent::doTeleportViaLocation(const LLVector3d& pos_global) { LLViewerRegion* regionp = getRegion(); + + if (!regionp) + { + return; + } + U64 handle = to_region_handle(pos_global); LLSimInfo* info = LLWorldMap::getInstance()->simInfoFromHandle(handle); if(regionp && info) diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h old mode 100755 new mode 100644 diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0d7d41304d0e69fb9e934c69d8b6472d3e9acecf --- /dev/null +++ b/indra/newview/llappcorehttp.cpp @@ -0,0 +1,192 @@ +/** + * @file llappcorehttp.cpp + * @brief + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappcorehttp.h" + +#include "llviewercontrol.h" + + +const F64 LLAppCoreHttp::MAX_THREAD_WAIT_TIME(10.0); + +LLAppCoreHttp::LLAppCoreHttp() + : mRequest(NULL), + mStopHandle(LLCORE_HTTP_HANDLE_INVALID), + mStopRequested(0.0), + mStopped(false), + mPolicyDefault(-1) +{} + + +LLAppCoreHttp::~LLAppCoreHttp() +{ + delete mRequest; + mRequest = NULL; +} + + +void LLAppCoreHttp::init() +{ + LLCore::HttpStatus status = LLCore::HttpRequest::createService(); + if (! status) + { + LL_ERRS("Init") << "Failed to initialize HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Point to our certs or SSH/https: will fail on connect + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_CA_FILE, + gDirUtilp->getCAFile()); + if (! status) + { + LL_ERRS("Init") << "Failed to set CA File for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Establish HTTP Proxy. "LLProxy" is a special string which directs + // the code to use LLProxy::applyProxySettings() to establish any + // HTTP or SOCKS proxy for http operations. + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_LLPROXY, 1); + if (! status) + { + LL_ERRS("Init") << "Failed to set HTTP proxy for HTTP services. Reason: " + << status.toString() + << LL_ENDL; + } + + // Tracing levels for library & libcurl (note that 2 & 3 are beyond spammy): + // 0 - None + // 1 - Basic start, stop simple transitions + // 2 - libcurl CURLOPT_VERBOSE mode with brief lines + // 3 - with partial data content + static const std::string http_trace("QAModeHttpTrace"); + if (gSavedSettings.controlExists(http_trace)) + { + long trace_level(0L); + trace_level = long(gSavedSettings.getU32(http_trace)); + status = LLCore::HttpRequest::setPolicyGlobalOption(LLCore::HttpRequest::GP_TRACE, trace_level); + } + + // Setup default policy and constrain if directed to + mPolicyDefault = LLCore::HttpRequest::DEFAULT_POLICY_ID; + static const std::string texture_concur("TextureFetchConcurrency"); + if (gSavedSettings.controlExists(texture_concur)) + { + U32 concur(llmin(gSavedSettings.getU32(texture_concur), U32(12))); + + if (concur > 0) + { + LLCore::HttpStatus status; + status = LLCore::HttpRequest::setPolicyClassOption(mPolicyDefault, + LLCore::HttpRequest::CP_CONNECTION_LIMIT, + concur); + if (! status) + { + LL_WARNS("Init") << "Unable to set texture fetch concurrency. Reason: " + << status.toString() + << LL_ENDL; + } + else + { + LL_INFOS("Init") << "Application settings overriding default texture fetch concurrency. New value: " + << concur + << LL_ENDL; + } + } + } + + // Kick the thread + status = LLCore::HttpRequest::startThread(); + if (! status) + { + LL_ERRS("Init") << "Failed to start HTTP servicing thread. Reason: " + << status.toString() + << LL_ENDL; + } + + mRequest = new LLCore::HttpRequest; +} + + +void LLAppCoreHttp::requestStop() +{ + llassert_always(mRequest); + + mStopHandle = mRequest->requestStopThread(this); + if (LLCORE_HTTP_HANDLE_INVALID != mStopHandle) + { + mStopRequested = LLTimer::getTotalSeconds(); + } +} + + +void LLAppCoreHttp::cleanup() +{ + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + // Should have been started already... + requestStop(); + } + + if (LLCORE_HTTP_HANDLE_INVALID == mStopHandle) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services without thread shutdown" + << LL_ENDL; + } + else + { + while (! mStopped && LLTimer::getTotalSeconds() < (mStopRequested + MAX_THREAD_WAIT_TIME)) + { + mRequest->update(200000); + ms_sleep(50); + } + if (! mStopped) + { + LL_WARNS("Cleanup") << "Attempting to cleanup HTTP services with thread shutdown incomplete" + << LL_ENDL; + } + } + + delete mRequest; + mRequest = NULL; + + LLCore::HttpStatus status = LLCore::HttpRequest::destroyService(); + if (! status) + { + LL_WARNS("Cleanup") << "Failed to shutdown HTTP services, continuing. Reason: " + << status.toString() + << LL_ENDL; + } +} + + +void LLAppCoreHttp::onCompleted(LLCore::HttpHandle, LLCore::HttpResponse *) +{ + mStopped = true; +} diff --git a/indra/newview/llappcorehttp.h b/indra/newview/llappcorehttp.h new file mode 100644 index 0000000000000000000000000000000000000000..241d73ad52737d1ee78b2a22e490c918db72a100 --- /dev/null +++ b/indra/newview/llappcorehttp.h @@ -0,0 +1,86 @@ +/** + * @file llappcorehttp.h + * @brief Singleton initialization/shutdown class for llcorehttp library + * + * $LicenseInfo:firstyear=2012&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2012, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#ifndef _LL_APP_COREHTTP_H_ +#define _LL_APP_COREHTTP_H_ + + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" + + +// This class manages the lifecyle of the core http library. +// Slightly different style than traditional code but reflects +// the use of handler classes and light-weight interface +// object instances of the new libraries. To be used +// as a singleton and static construction is fine. +class LLAppCoreHttp : public LLCore::HttpHandler +{ +public: + LLAppCoreHttp(); + ~LLAppCoreHttp(); + + // Initialize the LLCore::HTTP library creating service classes + // and starting the servicing thread. Caller is expected to do + // other initializations (SSL mutex, thread hash function) appropriate + // for the application. + void init(); + + // Request that the servicing thread stop servicing requests, + // release resource references and stop. Request is asynchronous + // and @see cleanup() will perform a limited wait loop for this + // request to stop the thread. + void requestStop(); + + // Terminate LLCore::HTTP library services. Caller is expected + // to have made a best-effort to shutdown the servicing thread + // by issuing a requestThreadStop() and waiting for completion + // notification that the stop has completed. + void cleanup(); + + // Notification when the stop request is complete. + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); + + // Retrieve the policy class for default operations. + int getPolicyDefault() const + { + return mPolicyDefault; + } + +private: + static const F64 MAX_THREAD_WAIT_TIME; + +private: + LLCore::HttpRequest * mRequest; + LLCore::HttpHandle mStopHandle; + F64 mStopRequested; + bool mStopped; + int mPolicyDefault; +}; + + +#endif // _LL_APP_COREHTTP_H_ diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp old mode 100755 new mode 100644 index 6d67e098a615d48556304671f1777eb6687c867c..06a9892c7e2f09eed17cea5e7bd91c0b15eba8aa --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -52,7 +52,8 @@ std::string self_av_string() { - return gAgentAvatarp->avString(); + // On logout gAgentAvatarp can already be invalid + return isAgentAvatarValid() ? gAgentAvatarp->avString() : ""; } // RAII thingy to guarantee that a variable gets reset when the Setter diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 0a9d6229ef85d11e90f84f633f0a75276d921a06..39df2d08c33f5f9d88de36a763b04cbb4de031bb 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -219,6 +219,7 @@ #include "llmachineid.h" #include "llmainlooprepeater.h" + // *FIX: These extern globals should be cleaned up. // The globals either represent state/config/resource-storage of either // this app, or another 'component' of the viewer. App globals should be @@ -734,6 +735,10 @@ bool LLAppViewer::init() LLViewerStatsRecorder::initClass(); #endif + // Initialize the non-LLCurl libcurl library. Should be called + // before consumers (LLTextureFetch). + mAppCoreHttp.init(); + // *NOTE:Mani - LLCurl::initClass is not thread safe. // Called before threads are created. LLCurl::initClass(gSavedSettings.getF32("CurlRequestTimeOut"), @@ -1884,6 +1889,7 @@ bool LLAppViewer::cleanup() // Delete workers first // shotdown all worker threads before deleting them in case of co-dependencies + mAppCoreHttp.requestStop(); sTextureFetch->shutdown(); sTextureCache->shutdown(); sImageDecodeThread->shutdown(); @@ -1899,6 +1905,9 @@ bool LLAppViewer::cleanup() LLCurl::cleanupClass(); LL_CHECK_MEMORY + // Non-LLCurl libcurl library + mAppCoreHttp.cleanup(); + LLFilePickerThread::cleanupClass(); //MUST happen AFTER LLCurl::cleanupClass diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index ae3c795d1e2d9ced65bc77cfd79072a2b33adbab..0d9c420ff8ab1527dcf189e114d2dcb3d816ae96 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -31,6 +31,7 @@ #include "llcontrol.h" #include "llsys.h" // for LLOSInfo #include "lltimer.h" +#include "llappcorehttp.h" class LLCommandLineParser; class LLFrameTimer; @@ -173,6 +174,9 @@ public: // Metrics policy helper statics. static void metricsUpdateRegion(U64 region_handle); static void metricsSend(bool enable_reporting); + + // llcorehttp init/shutdown/config information. + LLAppCoreHttp & getAppCoreHttp() { return mAppCoreHttp; } protected: virtual bool initWindow(); // Initialize the viewer's window. @@ -271,6 +275,9 @@ private: boost::scoped_ptr<LLUpdaterService> mUpdater; + // llcorehttp library init/shutdown helper + LLAppCoreHttp mAppCoreHttp; + //--------------------------------------------- //*NOTE: Mani - legacy updater stuff // Still useable? diff --git a/indra/newview/llattachmentsmgr.cpp b/indra/newview/llattachmentsmgr.cpp index c9543988a68b212751730df554946ac92bf3074b..ea0b8f00a4342f63e68b7f104e6beee32625e188 100644 --- a/indra/newview/llattachmentsmgr.cpp +++ b/indra/newview/llattachmentsmgr.cpp @@ -62,6 +62,12 @@ void LLAttachmentsMgr::onIdle(void *) void LLAttachmentsMgr::onIdle() { + // Make sure we got a region before trying anything else + if( !gAgent.getRegion() ) + { + return; + } + S32 obj_count = mPendingAttachments.size(); if (obj_count == 0) { diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index caf15fe1cbb0616719f98e98b7989980ad2c9f38..b5faff7968215f467aabb38a2e2da8a3b674ca6f 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -192,14 +192,18 @@ void LLDrawPoolWLSky::renderStars(void) const bool error; LLColor4 star_alpha(LLColor4::black); star_alpha.mV[3] = LLWLParamManager::getInstance()->mCurParams.getFloat("star_brightness", error) / 2.f; - llassert_always(!error); + + // If start_brightness is not set, exit + if( error ) + { + llwarns << "star_brightness missing in mCurParams" << llendl; + return; + } gGL.getTexUnit(0)->bind(gSky.mVOSkyp->getBloomTex()); gGL.pushMatrix(); gGL.rotatef(gFrameTimeSeconds*0.01f, 0.f, 0.f, 1.f); - // gl_FragColor.rgb = gl_Color.rgb; - // gl_FragColor.a = gl_Color.a * star_alpha.a; if (LLGLSLShader::sNoFixedFunction) { gCustomAlphaProgram.bind(); diff --git a/indra/newview/llface.cpp b/indra/newview/llface.cpp index 188f943f136d53d2c8dbfb6e1688ed3f55905da6..605cb81c103b99d221d785c90356b9cd10b15f42 100755 --- a/indra/newview/llface.cpp +++ b/indra/newview/llface.cpp @@ -1062,7 +1062,11 @@ bool LLFace::canRenderAsMask() } const LLTextureEntry* te = getTextureEntry(); - + if( !te || !getViewerObject() || !getTexture() ) + { + return false; + } + if ((te->getColor().mV[3] == 1.0f) && // can't treat as mask if we have face alpha (te->getGlow() == 0.f) && // glowing masks are hard to implement - don't mask getTexture()->getIsAlphaMask()) // texture actually qualifies for masking (lazily recalculated but expensive) diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index fa0ad20fdb09a310f5170f68f0513f321cc4e83c..62848586cdbb1438589b502fc5312f18e2d0c8d4 100644 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -422,13 +422,14 @@ void LLFloaterBvhPreview::resetMotion() LLVOAvatar* avatarp = mAnimPreview->getDummyAvatar(); BOOL paused = avatarp->areAnimationsPaused(); - // *TODO: Fix awful casting hack - LLKeyframeMotion* motionp = (LLKeyframeMotion*)avatarp->findMotion(mMotionID); - - // Set emotion - std::string emote = getChild<LLUICtrl>("emote_combo")->getValue().asString(); - motionp->setEmote(mIDList[emote]); - + LLKeyframeMotion* motionp = dynamic_cast<LLKeyframeMotion*>(avatarp->findMotion(mMotionID)); + if( motionp ) + { + // Set emotion + std::string emote = getChild<LLUICtrl>("emote_combo")->getValue().asString(); + motionp->setEmote(mIDList[emote]); + } + LLUUID base_id = mIDList[getChild<LLUICtrl>("preview_base_anim")->getValue().asString()]; avatarp->deactivateAllMotions(); avatarp->startMotion(mMotionID, 0.0f); @@ -438,8 +439,12 @@ void LLFloaterBvhPreview::resetMotion() // Set pose std::string handpose = getChild<LLUICtrl>("hand_pose_combo")->getValue().asString(); avatarp->startMotion( ANIM_AGENT_HAND_MOTION, 0.0f ); - motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + if( motionp ) + { + motionp->setHandPose(LLHandMotion::getHandPose(handpose)); + } + if (paused) { mPauseRequest = avatarp->requestPause(); diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index fce0b7c9c92ebe0a4a6eb16e510d17fdeb596f62..14a228df1cb91a062c068ebb07567f29d60e8be9 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -2577,14 +2577,23 @@ void LLRightClickInventoryFetchDescendentsObserver::execute(bool clear_observer) LLInventoryModel::item_array_t* item_array; gInventory.getDirectDescendentsOf(*current_folder, cat_array, item_array); - S32 item_count = item_array->count(); - S32 cat_count = cat_array->count(); - + S32 item_count(0); + if( item_array ) + { + item_count = item_array->count(); + } + + S32 cat_count(0); + if( cat_array ) + { + cat_count = cat_array->count(); + } + // Move to next if current folder empty if ((item_count == 0) && (cat_count == 0)) - { + { continue; - } + } uuid_vec_t ids; LLRightClickInventoryFetchObserver* outfit = NULL; diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llmeshrepository.cpp b/indra/newview/llmeshrepository.cpp index bc7f522848e8423c61efe10037ef7eb79d982797..ba0a590910682168111842e8e9122a0c218b9161 100755 --- a/indra/newview/llmeshrepository.cpp +++ b/indra/newview/llmeshrepository.cpp @@ -1097,11 +1097,13 @@ bool LLMeshRepoThread::headerReceived(const LLVolumeParams& mesh_params, U8* dat mMeshHeader[mesh_id] = header; } + + LLMutexLock lock(mMutex); // make sure only one thread access mPendingLOD at the same time. + //check for pending requests pending_lod_map::iterator iter = mPendingLOD.find(mesh_params); if (iter != mPendingLOD.end()) { - LLMutexLock lock(mMutex); for (U32 i = 0; i < iter->second.size(); ++i) { LODRequest req(mesh_params, iter->second[i]); @@ -1796,7 +1798,12 @@ void LLMeshLODResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { - + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1851,6 +1858,12 @@ void LLMeshSkinInfoResponder::completedRaw(U32 status, const std::string& reason const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1905,6 +1918,11 @@ void LLMeshDecompositionResponder::completedRaw(U32 status, const std::string& r const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -1959,6 +1977,12 @@ void LLMeshPhysicsShapeResponder::completedRaw(U32 status, const std::string& re const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + S32 data_size = buffer->countAfter(channels.in(), NULL); if (status < 200 || status > 400) @@ -2013,6 +2037,12 @@ void LLMeshHeaderResponder::completedRaw(U32 status, const std::string& reason, const LLChannelDescriptors& channels, const LLIOPipe::buffer_ptr_t& buffer) { + // thread could have already be destroyed during logout + if( !gMeshRepo.mThread ) + { + return; + } + if (status < 200 || status > 400) { //llwarns diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index f792f53ac58d76c08bde206a55fa0a20283eaa51..3d8150eed39c20f48d62e116d2affc081b5ec9f1 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -97,6 +97,13 @@ bool LLNotificationManager::onNotification(const LLSD& notify) { LLSysHandler* handle = NULL; + // Don't bother if we're going down. + // Otherwise we might crash when trying to use handlers that are already dead. + if( LLApp::isExiting() ) + { + return false; + } + if (LLNotifications::destroyed()) return false; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index d58d6d536c0da5580cab4187b411af7989f0f6e2..d42056ef9d50736c3cd4c2f6582c2a011dc9c657 100644 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -835,11 +835,11 @@ BOOL LLPanelEditWearable::isDirty() const BOOL isDirty = FALSE; if (mWearablePtr) { - if (mWearablePtr->isDirty() || - mWearableItem->getName().compare(mNameEditor->getText()) != 0) - { - isDirty = TRUE; - } + if (mWearablePtr->isDirty() || + ( mWearableItem && mNameEditor && mWearableItem->getName().compare(mNameEditor->getText()) != 0 )) + { + isDirty = TRUE; + } } return isDirty; } diff --git a/indra/newview/llpreview.cpp b/indra/newview/llpreview.cpp index 18626e327307ba744c81029b0830233f590f11f2..04934b13f107569c6ce9f64e4cffef237998eb73 100644 --- a/indra/newview/llpreview.cpp +++ b/indra/newview/llpreview.cpp @@ -464,7 +464,9 @@ LLMultiPreview::LLMultiPreview() void LLMultiPreview::onOpen(const LLSD& key) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast<LLPreview*>(mTabContainer->getCurrentPanel()); + if (frontmost_preview && frontmost_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { frontmost_preview->loadAsset(); @@ -477,8 +479,13 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) { if(new_rect.getWidth() != getRect().getWidth() || new_rect.getHeight() != getRect().getHeight()) { - LLPreview* frontmost_preview = (LLPreview*)mTabContainer->getCurrentPanel(); - if (frontmost_preview) frontmost_preview->userResized(); + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* frontmost_preview = dynamic_cast<LLPreview*>(mTabContainer->getCurrentPanel()); + + if (frontmost_preview) + { + frontmost_preview->userResized(); + } } LLFloater::handleReshape(new_rect, by_user); } @@ -486,7 +493,9 @@ void LLMultiPreview::handleReshape(const LLRect& new_rect, bool by_user) void LLMultiPreview::tabOpen(LLFloater* opened_floater, bool from_click) { - LLPreview* opened_preview = (LLPreview*)opened_floater; + // Floater could be something else than LLPreview, eg LLFloaterProfile. + LLPreview* opened_preview = dynamic_cast<LLPreview*>(opened_floater); + if (opened_preview && opened_preview->getAssetStatus() == LLPreview::PREVIEW_ASSET_UNLOADED) { opened_preview->loadAsset(); diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index d340b304ca802bfdabc61484f19023436c7fe8cb..d2280ea0890fae7fb0b7259565ff7d69a921862f 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -571,9 +571,13 @@ void LLScreenChannel::showToastsBottom() LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector<ToastElem> vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -601,7 +605,7 @@ void LLScreenChannel::showToastsBottom() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = floater->getRect().getHeight(); @@ -624,7 +628,7 @@ void LLScreenChannel::showToastsBottom() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_top = toast->getRect().mTop + gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_top > getRect().mTop; @@ -632,7 +636,8 @@ void LLScreenChannel::showToastsBottom() } // at least one toast should be visible - if(it == mToastList.rbegin()) + + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -655,10 +660,11 @@ void LLScreenChannel::showToastsBottom() } // Dismiss toasts we don't have space for (STORM-391). - if(it != mToastList.rend()) + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) @@ -714,9 +720,13 @@ void LLScreenChannel::showToastsTop() LLDockableFloater* floater = dynamic_cast<LLDockableFloater*>(LLDockableFloater::getInstanceHandle().get()); - for(it = mToastList.rbegin(); it != mToastList.rend(); ++it) + // Use a local variable instead of mToastList. + // mToastList can be modified during recursive calls and then all iteratos will be invalidated. + std::vector<ToastElem> vToastList( mToastList ); + + for(it = vToastList.rbegin(); it != vToastList.rend(); ++it) { - if(it != mToastList.rbegin()) + if(it != vToastList.rbegin()) { LLToast* toast = (it-1)->getToast(); if (!toast) @@ -744,7 +754,7 @@ void LLScreenChannel::showToastsTop() if(floater && floater->overlapsScreenChannel()) { - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { // move first toast above docked floater S32 shift = -floater->getRect().getHeight(); @@ -767,7 +777,7 @@ void LLScreenChannel::showToastsTop() if(!stop_showing_toasts) { - if( it != mToastList.rend()-1) + if( it != vToastList.rend()-1) { S32 toast_bottom = toast->getRect().mBottom - gSavedSettings.getS32("ToastGap"); stop_showing_toasts = toast_bottom < channel_rect.mBottom; @@ -775,7 +785,7 @@ void LLScreenChannel::showToastsTop() } // at least one toast should be visible - if(it == mToastList.rbegin()) + if(it == vToastList.rbegin()) { stop_showing_toasts = false; } @@ -799,10 +809,12 @@ void LLScreenChannel::showToastsTop() // Dismiss toasts we don't have space for (STORM-391). std::vector<LLToast*> toasts_to_hide; - if(it != mToastList.rend()) + + if(it != vToastList.rend()) { mHiddenToastsNum = 0; - for(; it != mToastList.rend(); it++) + + for(; it != vToastList.rend(); it++) { LLToast* toast = it->getToast(); if (toast) diff --git a/indra/newview/lltexturefetch.cpp b/indra/newview/lltexturefetch.cpp index adffb2c7064577bc91a217d9ddc816e0edf7dc03..a2854dd6d8811f2b052dc90084389c2d4d2fe58e 100644 --- a/indra/newview/lltexturefetch.cpp +++ b/indra/newview/lltexturefetch.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,12 +28,12 @@ #include <iostream> #include <map> +#include <algorithm> #include "llstl.h" #include "lltexturefetch.h" -#include "llcurl.h" #include "lldir.h" #include "llhttpclient.h" #include "llhttpstatuscodes.h" @@ -54,28 +54,214 @@ #include "llworld.h" #include "llsdutil.h" #include "llstartup.h" -#include "llviewerstats.h" +#include "llsdserialize.h" + +#include "httprequest.h" +#include "httphandler.h" +#include "httpresponse.h" +#include "bufferarray.h" +#include "bufferstream.h" bool LLTextureFetchDebugger::sDebuggerEnabled = false ; LLStat LLTextureFetch::sCacheHitRate("texture_cache_hits", 128); LLStat LLTextureFetch::sCacheReadLatency("texture_cache_read_latency", 128); + ////////////////////////////////////////////////////////////////////////////// -class LLTextureFetchWorker : public LLWorkerClass +// +// Introduction +// +// This is an attempt to document what's going on in here after-the-fact. +// It's a sincere attempt to be accurate but there will be mistakes. +// +// +// Purpose +// +// What is this module trying to do? It accepts requests to load textures +// at a given priority and discard level and notifies the caller when done +// (successfully or not). Additional constraints are: +// +// * Support a local texture cache. Don't hit network when possible +// to avoid it. +// * Use UDP or HTTP as directed or as fallback. HTTP is tried when +// not disabled and a URL is available. UDP when a URL isn't +// available or HTTP attempts fail. +// * Asynchronous (using threads). Main thread is not to be blocked or +// burdened. +// * High concurrency. Many requests need to be in-flight and at various +// stages of completion. +// * Tolerate frequent re-prioritizations of requests. Priority is +// a reflection of a camera's viewpoint and as that viewpoint changes, +// objects and textures become more and less relevant and that is +// expressed at this level by priority changes and request cancelations. +// +// The caller interfaces that fall out of the above and shape the +// implementation are: +// * createRequest - Load j2c image via UDP or HTTP at given discard level and priority +// * deleteRequest - Request removal of prior request +// * getRequestFinished - Test if request is finished returning data to caller +// * updateRequestPriority - Change priority of existing request +// * getFetchState - Retrieve progress on existing request +// +// Everything else in here is mostly plumbing, metrics and debug. +// +// +// The Work Queue +// +// The two central classes are LLTextureFetch and LLTextureFetchWorker. +// LLTextureFetch combines threading with a priority queue of work +// requests. The priority queue is sorted by a U32 priority derived +// from the F32 priority in the APIs. The *only* work request that +// receives service time by this thread is the highest priority +// request. All others wait until it is complete or a dynamic priority +// change has re-ordered work. +// +// LLTextureFetchWorker implements the work request and is 1:1 with +// texture fetch requests. Embedded in each is a state machine that +// walks it through the cache, HTTP, UDP, image decode and retry +// steps of texture acquisition. +// +// +// Threads +// +// Several threads are actively invoking code in this module. They +// include: +// +// 1. Tmain Main thread of execution +// 2. Ttf LLTextureFetch's worker thread provided by LLQueuedThread +// 3. Tcurl LLCurl's worker thread (should disappear over time) +// 4. Ttc LLTextureCache's worker thread +// 5. Tid Image decoder's worker thread +// 6. Thl HTTP library's worker thread +// +// +// Mutexes/Condition Variables +// +// 1. Mt Mutex defined for LLThread's condition variable (base class of +// LLTextureFetch) +// 2. Ct Condition variable for LLThread and used by lock/unlockData(). +// 3. Mwtd Special LLWorkerThread mutex used for request deletion +// operations (base class of LLTextureFetch) +// 4. Mfq LLTextureFetch's mutex covering request and command queue +// data. +// 5. Mfnq LLTextureFetch's mutex covering udp and http request +// queue data. +// 6. Mwc Mutex covering LLWorkerClass's members (base class of +// LLTextureFetchWorker). One per request. +// 7. Mw LLTextureFetchWorker's mutex. One per request. +// +// +// Lock Ordering Rules +// +// Not an exhaustive list but shows the order of lock acquisition +// needed to prevent deadlocks. 'A < B' means acquire 'A' before +// acquiring 'B'. +// +// 1. Mw < Mfnq +// (there are many more...) +// +// +// Method and Member Definitions +// +// With the above, we'll try to document what threads can call what +// methods (using T* for any), what locks must be held on entry and +// are taken out during execution and what data is covered by which +// lock (if any). This latter category will be especially prone to +// error so be skeptical. +// +// A line like: "// Locks: M<xxx>" indicates a method that must +// be invoked by a caller holding the 'M<xxx>' lock. Similarly, +// "// Threads: T<xxx>" means that a caller should be running in +// the indicated thread. +// +// For data members, a trailing comment like "// M<xxx>" means that +// the data member is covered by the specified lock. Absence of a +// comment can mean the member is unlocked or that I didn't bother +// to do the archaeology. In the case of LLTextureFetchWorker, +// most data members added by the leaf class are actually covered +// by the Mw lock. You may also see "// T<xxx>" which means that +// the member's usage is restricted to one thread (except for +// perhaps construction and destruction) and so explicit locking +// isn't used. +// +// In code, a trailing comment like "// [-+]M<xxx>" indicates a +// lock acquision or release point. +// +// +// Worker Lifecycle +// +// The threading and responder model makes it very likely that +// other components are holding on to a pointer to a worker request. +// So, uncoordinated deletions of requests is a guarantee of memory +// corruption in a short time. So destroying a request involves +// invocations's of LLQueuedThread/LLWorkerThread's abort/stop +// logic that removes workers and puts them ona delete queue for +// 2-phase destruction. That second phase is deferrable by calls +// to deleteOK() which only allow final destruction (via dtor) +// once deleteOK has determined that the request is in a safe +// state. +// +// +// Worker State Machine +// +// (ASCII art needed) +// +// +// Priority Scheme +// +// [PRIORITY_LOW, PRIORITY_NORMAL) - for WAIT_HTTP_RESOURCE state +// and other wait states +// [PRIORITY_HIGH, PRIORITY_URGENT) - External event delivered, +// rapidly transitioning through states, +// no waiting allowed +// +// By itself, the above work queue model would fail the concurrency +// and liveness requirements of the interface. A high priority +// request could find itself on the head and stalled for external +// reasons (see VWR-28996). So a few additional constraints are +// required to keep things running: +// * Anything that can make forward progress must be kept at a +// higher priority than anything that can't. +// * On completion of external events, the associated request +// needs to be elevated beyond the normal range to handle +// any data delivery and release any external resource. +// +// This effort is made to keep higher-priority entities moving +// forward in their state machines at every possible step of +// processing. It's not entirely proven that this produces the +// experiencial benefits promised. +// + + +////////////////////////////////////////////////////////////////////////////// + +// Tuning/Parameterization Constants + +static const S32 HTTP_REQUESTS_IN_QUEUE_HIGH_WATER = 40; // Maximum requests to have active in HTTP +static const S32 HTTP_REQUESTS_IN_QUEUE_LOW_WATER = 20; // Active level at which to refill + + +////////////////////////////////////////////////////////////////////////////// + +class LLTextureFetchWorker : public LLWorkerClass, public LLCore::HttpHandler + { friend class LLTextureFetch; - friend class HTTPGetResponder; friend class LLTextureFetchDebugger; private: class CacheReadResponder : public LLTextureCache::ReadResponder { public: + + // Threads: Ttf CacheReadResponder(LLTextureFetch* fetcher, const LLUUID& id, LLImageFormatted* image) : mFetcher(fetcher), mID(id) { setImage(image); } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -92,10 +278,14 @@ private: class CacheWriteResponder : public LLTextureCache::WriteResponder { public: + + // Threads: Ttf CacheWriteResponder(LLTextureFetch* fetcher, const LLUUID& id) : mFetcher(fetcher), mID(id) { } + + // Threads: Ttc virtual void completed(bool success) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -112,10 +302,14 @@ private: class DecodeResponder : public LLImageDecodeThread::Responder { public: + + // Threads: Ttf DecodeResponder(LLTextureFetch* fetcher, const LLUUID& id, LLTextureFetchWorker* worker) : mFetcher(fetcher), mID(id), mWorker(worker) { } + + // Threads: Tid virtual void completed(bool success, LLImageRaw* raw, LLImageRaw* aux) { LLTextureFetchWorker* worker = mFetcher->getWorker(mID); @@ -148,22 +342,35 @@ private: }; public: + + // Threads: Ttf /*virtual*/ bool doWork(S32 param); // Called from LLWorkerThread::processRequest() + + // Threads: Ttf /*virtual*/ void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - /*virtual*/ bool deleteOK(); // called from update() (WORK THREAD) - + + // Threads: Tmain + /*virtual*/ bool deleteOK(); // called from update() + ~LLTextureFetchWorker(); - // void relese() { --mActiveCount; } - S32 callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); + // Threads: Ttf + // Locks: Mw + S32 callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success); + + // Threads: Ttc void callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); + + // Threads: Ttc void callbackCacheWrite(bool success); + + // Threads: Tid void callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux); - void setGetStatus(U32 status, const std::string& reason) + // Threads: T* + void setGetStatus(LLCore::HttpStatus status, const std::string& reason) { LLMutexLock lock(&mWorkMutex); @@ -175,35 +382,93 @@ public: bool getCanUseHTTP() const { return mCanUseHTTP; } LLTextureFetch & getFetcher() { return *mFetcher; } + + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); protected: LLTextureFetchWorker(LLTextureFetch* fetcher, const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 discard, S32 size); private: + + // Threads: Tmain /*virtual*/ void startWork(S32 param); // called from addWork() (MAIN THREAD) + + // Threads: Tmain /*virtual*/ void endWork(S32 param, bool aborted); // called from doWork() (MAIN THREAD) + // Locks: Mw void resetFormattedData(); + // Locks: Mw void setImagePriority(F32 priority); + + // Locks: Mw (ctor invokes without lock) void setDesiredDiscard(S32 discard, S32 size); + + // Threads: T* + // Locks: Mw bool insertPacket(S32 index, U8* data, S32 size); + + // Locks: Mw void clearPackets(); + + // Locks: Mw void setupPacketData(); + + // Locks: Mw (ctor invokes without lock) U32 calcWorkPriority(); + + // Locks: Mw void removeFromCache(); + + // Threads: Ttf + // Locks: Mw bool processSimulatorPackets(); + + // Threads: Ttf bool writeToCacheComplete(); - void removeFromHTTPQueue(); + // Threads: Ttf + void recordTextureStart(bool is_http); + + // Threads: Ttf + void recordTextureDone(bool is_http); void lockWorkMutex() { mWorkMutex.lock(); } void unlockWorkMutex() { mWorkMutex.unlock(); } + // Threads: Ttf + // Locks: Mw + bool acquireHttpSemaphore() + { + llassert(! mHttpHasResource); + if (mFetcher->mHttpSemaphore <= 0) + { + return false; + } + mHttpHasResource = true; + mFetcher->mHttpSemaphore--; + return true; + } + + // Threads: Ttf + // Locks: Mw + void releaseHttpSemaphore() + { + llassert(mHttpHasResource); + mHttpHasResource = false; + mFetcher->mHttpSemaphore++; + } + private: enum e_state // mState { + // *NOTE: Do not change the order/value of state variables, some code + // depends upon specific ordering/adjacency. + // NOTE: Affects LLTextureBar::draw in lltextureview.cpp (debug hack) INVALID = 0, INIT, @@ -211,8 +476,10 @@ private: CACHE_POST, LOAD_FROM_NETWORK, LOAD_FROM_SIMULATOR, - SEND_HTTP_REQ, - WAIT_HTTP_REQ, + WAIT_HTTP_RESOURCE, // Waiting for HTTP resources + WAIT_HTTP_RESOURCE2, // Waiting for HTTP resources + SEND_HTTP_REQ, // Commit to sending as HTTP + WAIT_HTTP_REQ, // Request sent, wait for completion DECODE_IMAGE, DECODE_IMAGE_UPDATE, WRITE_TO_CACHE, @@ -256,9 +523,8 @@ private: F32 mCacheReadTime; LLTextureCache::handle_t mCacheReadHandle; LLTextureCache::handle_t mCacheWriteHandle; - U8* mBuffer; - S32 mBufferSize; S32 mRequestedSize; + S32 mRequestedOffset; S32 mDesiredSize; S32 mFileSize; S32 mCachedSize; @@ -273,12 +539,9 @@ private: BOOL mInCache; bool mCanUseHTTP ; bool mCanUseNET ; //can get from asset server. - S32 mHTTPFailCount; S32 mRetryAttempt; S32 mActiveCount; - U32 mGetStatus; - U32 mHTTPHandle; - F32 mDelay; + LLCore::HttpStatus mGetStatus; std::string mGetReason; // Work Data @@ -298,105 +561,19 @@ private: U8 mImageCodec; LLViewerAssetStats::duration_t mMetricsStartTime; -}; - -////////////////////////////////////////////////////////////////////////////// - -class HTTPGetResponder : public LLCurl::Responder -{ - LOG_CLASS(HTTPGetResponder); -public: - HTTPGetResponder(LLTextureFetch* fetcher, const LLUUID& id, U64 startTime, S32 requestedSize, U32 offset, bool redir) - : mFetcher(fetcher), mID(id), mStartTime(startTime), mRequestedSize(requestedSize), mOffset(offset), mFollowRedir(redir) - { - } - ~HTTPGetResponder() - { - } - - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); - static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); - static LLCachedControl<bool> log_texture_traffic(gSavedSettings,"LogTextureNetworkTraffic") ; - - if (log_to_viewer_log || log_to_sim) - { - mFetcher->mTextureInfo.setRequestStartTime(mID, mStartTime); - U64 timeNow = LLTimer::getTotalTime(); - mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); - mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); - mFetcher->mTextureInfo.setRequestOffset(mID, mOffset); - mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); - } - - S32 data_size = 0; - lldebugs << "HTTP COMPLETE: " << mID << llendl; - LLTextureFetchWorker* worker = mFetcher->getWorker(mID); - if (worker) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - - if (!success) - { - worker->setGetStatus(status, reason); -// llwarns << "CURL GET FAILED, status:" << status << " reason:" << reason << llendl; - } - - data_size = worker->callbackHttpGet(channels, buffer, partial, success); - - if(log_texture_traffic && data_size > 0) - { - LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID) ; - if(tex) - { - gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; - } - } - - if (worker->mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType, - LLViewerAssetStatsFF::get_timestamp() - worker->mMetricsStartTime); - worker->mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == worker->mType); - } - else - { - llwarns << "Worker not found: " << mID << llendl; - } - mFetcher->getCurlRequest().completeRequest(data_size); - } - - virtual bool followRedir() - { - return mFollowRedir; - } - -private: - LLTextureFetch* mFetcher; - LLUUID mID; - U64 mStartTime; - S32 mRequestedSize; - U32 mOffset; - bool mFollowRedir; + LLCore::HttpHandle mHttpHandle; // Handle of any active request + LLCore::BufferArray * mHttpBufferArray; // Refcounted pointer to response data + int mHttpPolicyClass; + bool mHttpActive; // Active request to http library + unsigned int mHttpReplySize; // Actual received data size + unsigned int mHttpReplyOffset; // Actual received data offset + bool mHttpHasResource; // Counts against Fetcher's mHttpSemaphore + + // State history + U32 mCacheReadCount; + U32 mCacheWriteCount; + U32 mResourceWaitCount; // Requests entering WAIT_HTTP_RESOURCE2 }; ////////////////////////////////////////////////////////////////////////////// @@ -632,13 +809,15 @@ const char* LLTextureFetchWorker::sStateDescs[] = { "CACHE_POST", "LOAD_FROM_NETWORK", "LOAD_FROM_SIMULATOR", + "WAIT_HTTP_RESOURCE", + "WAIT_HTTP_RESOURCE2", "SEND_HTTP_REQ", "WAIT_HTTP_REQ", "DECODE_IMAGE", "DECODE_IMAGE_UPDATE", "WRITE_TO_CACHE", "WAIT_ON_WRITE", - "DONE", + "DONE" }; // static @@ -654,6 +833,7 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, S32 discard, // Desired discard S32 size) // Desired size : LLWorkerClass(fetcher, "TextureFetch"), + LLCore::HttpHandler(), mState(INIT), mWriteToCacheState(NOT_WRITE), mFetcher(fetcher), @@ -671,9 +851,8 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mCacheReadTime(0.f), mCacheReadHandle(LLTextureCache::nullHandle()), mCacheWriteHandle(LLTextureCache::nullHandle()), - mBuffer(NULL), - mBufferSize(0), mRequestedSize(0), + mRequestedOffset(0), mDesiredSize(TEXTURE_CACHE_ENTRY_SIZE), mFileSize(0), mCachedSize(0), @@ -687,18 +866,24 @@ LLTextureFetchWorker::LLTextureFetchWorker(LLTextureFetch* fetcher, mInLocalCache(FALSE), mInCache(FALSE), mCanUseHTTP(true), - mHTTPFailCount(0), mRetryAttempt(0), mActiveCount(0), - mGetStatus(0), mWorkMutex(NULL), mFirstPacket(0), mLastPacket(-1), mTotalPackets(0), mImageCodec(IMG_CODEC_INVALID), mMetricsStartTime(0), - mHTTPHandle(0), - mDelay(-1.f) + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID), + mHttpBufferArray(NULL), + mHttpPolicyClass(mFetcher->mHttpPolicyClass), + mHttpActive(false), + mHttpReplySize(0U), + mHttpReplyOffset(0U), + mHttpHasResource(false), + mCacheReadCount(0U), + mCacheWriteCount(0U), + mResourceWaitCount(0U) { mCanUseNET = mUrl.empty() ; @@ -720,7 +905,20 @@ LLTextureFetchWorker::~LLTextureFetchWorker() // << " Requested=" << mRequestedDiscard // << " Desired=" << mDesiredDiscard << llendl; llassert_always(!haveWork()); - lockWorkMutex(); + + lockWorkMutex(); // +Mw (should be useless) + if (mHttpHasResource) + { + // Last-chance catchall to recover the resource. Using an + // atomic datatype solely because this can be running in + // another thread. + releaseHttpSemaphore(); + } + if (mHttpActive) + { + // Issue a cancel on a live request... + mFetcher->getHttpRequest().requestCancel(mHttpHandle, NULL); + } if (mCacheReadHandle != LLTextureCache::nullHandle() && mFetcher->mTextureCache) { mFetcher->mTextureCache->readComplete(mCacheReadHandle, true); @@ -731,22 +929,18 @@ LLTextureFetchWorker::~LLTextureFetchWorker() } mFormattedImage = NULL; clearPackets(); - unlockWorkMutex(); - - removeFromHTTPQueue(); -} - -void LLTextureFetchWorker::removeFromHTTPQueue() -{ - if(mHTTPHandle > 0) + if (mHttpBufferArray) { - llassert_always(mState == WAIT_HTTP_REQ); - - mFetcher->getCurlRequest().removeRequest(mHTTPHandle); - mHTTPHandle = 0; + mHttpBufferArray->release(); + mHttpBufferArray = NULL; } + unlockWorkMutex(); // -Mw + mFetcher->removeFromHTTPQueue(mID, 0); + mFetcher->removeHttpWaiter(mID); + mFetcher->updateStateStats(mCacheReadCount, mCacheWriteCount, mResourceWaitCount); } +// Locks: Mw void LLTextureFetchWorker::clearPackets() { for_each(mPackets.begin(), mPackets.end(), DeletePointer()); @@ -756,6 +950,7 @@ void LLTextureFetchWorker::clearPackets() mFirstPacket = 0; } +// Locks: Mw void LLTextureFetchWorker::setupPacketData() { S32 data_size = 0; @@ -788,6 +983,7 @@ void LLTextureFetchWorker::setupPacketData() } } +// Locks: Mw (ctor invokes without lock) U32 LLTextureFetchWorker::calcWorkPriority() { //llassert_always(mImagePriority >= 0 && mImagePriority <= LLViewerFetchedTexture::maxDecodePriority()); @@ -797,7 +993,7 @@ U32 LLTextureFetchWorker::calcWorkPriority() return mWorkPriority; } -// mWorkMutex is locked +// Locks: Mw (ctor invokes without lock) void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) { bool prioritize = false; @@ -833,6 +1029,7 @@ void LLTextureFetchWorker::setDesiredDiscard(S32 discard, S32 size) } } +// Locks: Mw void LLTextureFetchWorker::setImagePriority(F32 priority) { // llassert_always(priority >= 0 && priority <= LLViewerTexture::maxDecodePriority()); @@ -842,35 +1039,41 @@ void LLTextureFetchWorker::setImagePriority(F32 priority) mImagePriority = priority; calcWorkPriority(); U32 work_priority = mWorkPriority | (getPriority() & LLWorkerThread::PRIORITY_HIGHBITS); - mFetcher->getCurlRequest().updatePriority(mHTTPHandle, mWorkPriority); setPriority(work_priority); } } +// Locks: Mw void LLTextureFetchWorker::resetFormattedData() { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } if (mFormattedImage.notNull()) { mFormattedImage->deleteData(); } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; } -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::startWork(S32 param) { llassert(mFormattedImage.isNull()); } -#include "llviewertexturelist.h" // debug - -// Called from LLWorkerThread::processRequest() +// Threads: Ttf bool LLTextureFetchWorker::doWork(S32 param) { - LLMutexLock lock(&mWorkMutex); + static const LLCore::HttpStatus http_not_found(HTTP_NOT_FOUND); // 404 + static const LLCore::HttpStatus http_service_unavail(HTTP_SERVICE_UNAVAILABLE); // 503 + static const LLCore::HttpStatus http_not_sat(HTTP_REQUESTED_RANGE_NOT_SATISFIABLE); // 416; + + LLMutexLock lock(&mWorkMutex); // +Mw if ((mFetcher->isQuitting() || getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) { @@ -914,22 +1117,26 @@ bool LLTextureFetchWorker::doWork(S32 param) mLoadedDiscard = -1; mDecodedDiscard = -1; mRequestedSize = 0; + mRequestedOffset = 0; mFileSize = 0; mCachedSize = 0; mLoaded = FALSE; mSentRequest = UNSENT; mDecoded = FALSE; mWritten = FALSE; - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; - mBufferSize = 0; + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } + mHttpReplySize = 0; + mHttpReplyOffset = 0; mHaveAllData = FALSE; clearPackets(); // TODO: Shouldn't be necessary mCacheReadHandle = LLTextureCache::nullHandle(); mCacheWriteHandle = LLTextureCache::nullHandle(); mState = LOAD_FROM_TEXTURE_CACHE; mInCache = FALSE; - mDelay = -1.f; mDesiredSize = llmax(mDesiredSize, TEXTURE_CACHE_ENTRY_SIZE); // min desired size is TEXTURE_CACHE_ENTRY_SIZE LL_DEBUGS("Texture") << mID << ": Priority: " << llformat("%8.0f",mImagePriority) << " Desired Discard: " << mDesiredDiscard << " Desired Size: " << mDesiredSize << LL_ENDL; @@ -945,7 +1152,7 @@ bool LLTextureFetchWorker::doWork(S32 param) S32 size = mDesiredSize - offset; if (size <= 0) { - mState = CACHE_POST; //have enough data, will fall to decode + mState = CACHE_POST; return false; } mFileSize = 0; @@ -956,6 +1163,7 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it // read file from local disk + ++mCacheReadCount; std::string filename = mUrl.substr(7, std::string::npos); CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(filename, mID, cache_priority, @@ -966,6 +1174,7 @@ bool LLTextureFetchWorker::doWork(S32 param) { setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + ++mCacheReadCount; CacheReadResponder* responder = new CacheReadResponder(mFetcher, mID, mFormattedImage); mCacheReadHandle = mFetcher->mTextureCache->readFromCache(mID, cache_priority, offset, size, responder); @@ -979,7 +1188,7 @@ bool LLTextureFetchWorker::doWork(S32 param) llwarns << "Unknown URL Type: " << mUrl << llendl; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; } else { @@ -1082,7 +1291,7 @@ bool LLTextureFetchWorker::doWork(S32 param) } if (mCanUseHTTP && !mUrl.empty()) { - mState = SEND_HTTP_REQ; + mState = WAIT_HTTP_RESOURCE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); if(mWriteToCacheState != NOT_WRITE) { @@ -1099,13 +1308,7 @@ bool LLTextureFetchWorker::doWork(S32 param) mRequestedDiscard = mDesiredDiscard; mSentRequest = QUEUED; mFetcher->addToNetworkQueue(this); - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureStart(false); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; @@ -1116,12 +1319,7 @@ bool LLTextureFetchWorker::doWork(S32 param) //llassert_always(mFetcher->mNetworkQueue.find(mID) != mFetcher->mNetworkQueue.end()); // Make certain this is in the network queue //mFetcher->addToNetworkQueue(this); - //if (! mMetricsStartTime) - //{ - // mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - //} - //LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, false, - // LLImageBase::TYPE_AVATAR_BAKE == mType); + //recordTextureStart(false); //setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1146,193 +1344,201 @@ bool LLTextureFetchWorker::doWork(S32 param) setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mState = DECODE_IMAGE; mWriteToCacheState = SHOULD_WRITE; - - if (mMetricsStartTime) - { - LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType, - LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); - mMetricsStartTime = 0; - } - LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); + recordTextureDone(false); } else { mFetcher->addToNetworkQueue(this); // failsafe - if (! mMetricsStartTime) - { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); - } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - false, - LLImageBase::TYPE_AVATAR_BAKE == mType); setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + recordTextureStart(false); } return false; } + if (mState == WAIT_HTTP_RESOURCE) + { + // NOTE: + // control the number of the http requests issued for: + // 1, not openning too many file descriptors at the same time; + // 2, control the traffic of http so udp gets bandwidth. + // + // If it looks like we're busy, keep this request here. + // Otherwise, advance into the HTTP states. + if (mFetcher->getHttpWaitersCount() || ! acquireHttpSemaphore()) + { + mState = WAIT_HTTP_RESOURCE2; + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mFetcher->addHttpWaiter(this->mID); + ++mResourceWaitCount; + return false; + } + + mState = SEND_HTTP_REQ; + // *NOTE: You must invoke releaseHttpSemaphore() if you transition + // to a state other than SEND_HTTP_REQ or WAIT_HTTP_REQ or abort + // the request. + } + + if (mState == WAIT_HTTP_RESOURCE2) + { + // Just idle it if we make it to the head... + return false; + } + if (mState == SEND_HTTP_REQ) { - if(mCanUseHTTP) + if (! mCanUseHTTP) { - mFetcher->removeFromNetworkQueue(this, false); + releaseHttpSemaphore(); + return true; // abort + } + + mFetcher->removeFromNetworkQueue(this, false); - S32 cur_size = 0; - if (mFormattedImage.notNull()) + S32 cur_size = 0; + if (mFormattedImage.notNull()) + { + cur_size = mFormattedImage->getDataSize(); // amount of data we already have + if (mFormattedImage->getDiscardLevel() == 0) { - cur_size = mFormattedImage->getDataSize(); // amount of data we already have - if (mFormattedImage->getDiscardLevel() == 0) - { - if(cur_size > 0) - { - // We already have all the data, just decode it - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; - return false; - } - else - { - return true ; //abort. - } - } - } - mRequestedSize = mDesiredSize; - mRequestedDiscard = mDesiredDiscard; - mRequestedSize -= cur_size; - S32 offset = cur_size; - mBufferSize = cur_size; // This will get modified by callbackHttpGet() - - bool res = false; - if (!mUrl.empty()) - { - mRequestedTimer.reset(); - - mLoaded = FALSE; - mGetStatus = 0; - mGetReason.clear(); - LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << offset - << " Bytes: " << mRequestedSize - << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth - << LL_ENDL; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); - mState = WAIT_HTTP_REQ; - - if (! mMetricsStartTime) + if (cur_size > 0) { - mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + // We already have all the data, just decode it + mLoadedDiscard = mFormattedImage->getDiscardLevel(); + setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; } - LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, - true, - LLImageBase::TYPE_AVATAR_BAKE == mType); - - // Will call callbackHttpGet when curl request completes - std::vector<std::string> headers; - headers.push_back("Accept: image/x-j2c"); - // If we try to fetch the whole file, we set the size to 0 so that we generate the correct curl range request - // Note: it looks a bit hacky but we need to limit this (size==0) to mean "whole file" to HTTP only as it messes up UDP fetching - if ((offset+mRequestedSize) == MAX_IMAGE_DATA_SIZE) + else { - mRequestedSize = 0; + releaseHttpSemaphore(); + return true; // abort. } - mHTTPHandle = mFetcher->mCurlGetRequest->getByteRange(mUrl, headers, offset, mRequestedSize, mWorkPriority, - new HTTPGetResponder(mFetcher, mID, LLTimer::getTotalTime(), mRequestedSize, offset, true), mDelay); - mDelay = -1.f; //reset - res = true; - } - if (!res) - { - llwarns << "HTTP GET request failed for " << mID << llendl; - resetFormattedData(); - ++mHTTPFailCount; - return true; // failed } - // fall through } - else //can not use http fetch. + mRequestedSize = mDesiredSize; + mRequestedDiscard = mDesiredDiscard; + mRequestedSize -= cur_size; + mRequestedOffset = cur_size; + if (mRequestedOffset) { - return true ; //abort + // Texture fetching often issues 'speculative' loads that + // start beyond the end of the actual asset. Some cache/web + // systems, e.g. Varnish, will respond to this not with a + // 416 but with a 200 and the entire asset in the response + // body. By ensuring that we always have a partially + // satisfiable Range request, we avoid that hit to the network. + // We just have to deal with the overlapping data which is made + // somewhat harder by the fact that grid services don't necessarily + // return the Content-Range header on 206 responses. *Sigh* + mRequestedOffset -= 1; + mRequestedSize += 1; } + + mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; + if (!mUrl.empty()) + { + mRequestedTimer.reset(); + mLoaded = FALSE; + mGetStatus = LLCore::HttpStatus(); + mGetReason.clear(); + LL_DEBUGS("Texture") << "HTTP GET: " << mID << " Offset: " << mRequestedOffset + << " Bytes: " << mRequestedSize + << " Bandwidth(kbps): " << mFetcher->getTextureBandwidth() << "/" << mFetcher->mMaxBandwidth + << LL_ENDL; + + // Will call callbackHttpGet when curl request completes + mHttpHandle = mFetcher->mHttpRequest->requestGetByteRange(mHttpPolicyClass, + mWorkPriority, + mUrl, + mRequestedOffset, + mRequestedSize, + mFetcher->mHttpOptions, + mFetcher->mHttpHeaders, + this); + } + if (LLCORE_HTTP_HANDLE_INVALID == mHttpHandle) + { + llwarns << "HTTP GET request failed for " << mID << llendl; + resetFormattedData(); + releaseHttpSemaphore(); + return true; // failed + } + + mHttpActive = true; + mFetcher->addToHTTPQueue(mID); + recordTextureStart(true); + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); + mState = WAIT_HTTP_REQ; + + // fall through } if (mState == WAIT_HTTP_REQ) { + // *NOTE: As stated above, all transitions out of this state should + // call releaseHttpSemaphore(). if (mLoaded) { S32 cur_size = mFormattedImage.notNull() ? mFormattedImage->getDataSize() : 0; if (mRequestedSize < 0) { - S32 max_attempts; - if (mGetStatus == HTTP_NOT_FOUND) + if (http_not_found == mGetStatus) { if(mWriteToCacheState == NOT_WRITE) //map tiles { mState = DONE; + releaseHttpSemaphore(); return true; // failed, means no map tile on the empty region. } - mHTTPFailCount = max_attempts = 1; // Don't retry llwarns << "Texture missing from server (404): " << mUrl << llendl; - //roll back to try UDP - if(mCanUseNET) + // roll back to try UDP + if (mCanUseNET) { - mState = INIT ; - mCanUseHTTP = false ; + mState = INIT; + mCanUseHTTP = false; mUrl.clear(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - return false ; + releaseHttpSemaphore(); + return false; } } - else if (mGetStatus == HTTP_SERVICE_UNAVAILABLE) + else if (http_service_unavail == mGetStatus) { - // *TODO: Should probably introduce a timer here to delay future HTTP requsts - // for a short time (~1s) to ease server load? Ideally the server would queue - // requests instead of returning 503... we already limit the number pending. - ++mHTTPFailCount; - max_attempts = mHTTPFailCount+1; // Keep retrying LL_INFOS_ONCE("Texture") << "Texture server busy (503): " << mUrl << LL_ENDL; - mDelay = 2.0f; //delay 2 second to re-issue the http request + } + else if (http_not_sat == mGetStatus) + { + // Allowed, we'll accept whatever data we have as complete. + mHaveAllData = TRUE; } else { - const S32 HTTP_MAX_RETRY_COUNT = 3; - max_attempts = HTTP_MAX_RETRY_COUNT + 1; - ++mHTTPFailCount; - mDelay = 2.0f; //delay 2 second to re-issue the http request - llinfos << "HTTP GET failed for: " << mUrl - << " Status: " << mGetStatus << " Reason: '" << mGetReason << "'" - << " Attempt:" << mHTTPFailCount+1 << "/" << max_attempts << llendl; + << " Status: " << mGetStatus.toHex() + << " Reason: '" << mGetReason << "'" + << llendl; } - if (mHTTPFailCount >= max_attempts) - { - mUrl.clear(); - if (cur_size > 0) - { - // Use available data - mLoadedDiscard = mFormattedImage->getDiscardLevel(); - setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = DECODE_IMAGE; - return false; - } - else - { - resetFormattedData(); - mState = DONE; - return true; // failed - } - } - else + mUrl.clear(); + if (cur_size > 0) { + // Use available data + mLoadedDiscard = mFormattedImage->getDiscardLevel(); setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); - mState = SEND_HTTP_REQ; - return false; // retry + mState = DECODE_IMAGE; + releaseHttpSemaphore(); + return false; } + + // Fail harder + resetFormattedData(); + mState = DONE; + releaseHttpSemaphore(); + return true; // failed } // Clear the url since we're done with the fetch @@ -1342,18 +1548,46 @@ bool LLTextureFetchWorker::doWork(S32 param) { mUrl.clear(); } - - llassert_always(mBufferSize == cur_size + mRequestedSize); - if(!mBufferSize)//no data received. + + if (! mHttpBufferArray || ! mHttpBufferArray->size()) { - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); - mBuffer = NULL; + // no data received. + if (mHttpBufferArray) + { + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + } - //abort. + // abort. mState = DONE; + releaseHttpSemaphore(); return true; } + S32 append_size(mHttpBufferArray->size()); + S32 total_size(cur_size + append_size); + S32 src_offset(0); + llassert_always(append_size == mRequestedSize); + if (mHttpReplyOffset && mHttpReplyOffset != cur_size) + { + // In case of a partial response, our offset may + // not be trivially contiguous with the data we have. + // Get back into alignment. + if (mHttpReplyOffset > cur_size) + { + LL_WARNS("Texture") << "Partial HTTP response produces break in image data for texture " + << mID << ". Aborting load." << LL_ENDL; + mState = DONE; + releaseHttpSemaphore(); + return true; + } + src_offset = cur_size - mHttpReplyOffset; + append_size -= src_offset; + total_size -= src_offset; + mRequestedSize -= src_offset; // Make requested values reflect useful part + mRequestedOffset += src_offset; + } + if (mFormattedImage.isNull()) { // For now, create formatted image based on extension @@ -1365,41 +1599,50 @@ bool LLTextureFetchWorker::doWork(S32 param) } } - if (mHaveAllData && mRequestedDiscard == 0) //the image file is fully loaded. + if (mHaveAllData) //the image file is fully loaded. { - mFileSize = mBufferSize; + mFileSize = total_size; } else //the file size is unknown. { - mFileSize = mBufferSize + 1 ; //flag the file is not fully loaded. + mFileSize = total_size + 1 ; //flag the file is not fully loaded. } - U8* buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), mBufferSize); + U8 * buffer = (U8 *) ALLOCATE_MEM(LLImageBase::getPrivatePool(), total_size); if (cur_size > 0) { memcpy(buffer, mFormattedImage->getData(), cur_size); } - memcpy(buffer + cur_size, mBuffer, mRequestedSize); // append + mHttpBufferArray->read(src_offset, (char *) buffer + cur_size, append_size); + // NOTE: setData releases current data and owns new data (buffer) - mFormattedImage->setData(buffer, mBufferSize); - // delete temp data - FREE_MEM(LLImageBase::getPrivatePool(), mBuffer); // Note: not 'buffer' (assigned in setData()) - mBuffer = NULL; - mBufferSize = 0; + mFormattedImage->setData(buffer, total_size); + + // Done with buffer array + mHttpBufferArray->release(); + mHttpBufferArray = NULL; + mHttpReplySize = 0; + mHttpReplyOffset = 0; + mLoadedDiscard = mRequestedDiscard; mState = DECODE_IMAGE; - if(mWriteToCacheState != NOT_WRITE) + if (mWriteToCacheState != NOT_WRITE) { mWriteToCacheState = SHOULD_WRITE ; } setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); + releaseHttpSemaphore(); return false; } else { - // - //No need to timeout, the responder should be triggered automatically. - // + // *HISTORY: There was a texture timeout test here originally that + // would cancel a request that was over 120 seconds old. That's + // probably not a good idea. Particularly rich regions can take + // an enormous amount of time to load textures. We'll revisit the + // various possible timeout components (total request time, connection + // time, I/O time, with and without retries, etc.) in the future. + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return false; } @@ -1408,11 +1651,12 @@ bool LLTextureFetchWorker::doWork(S32 param) if (mState == DECODE_IMAGE) { static LLCachedControl<bool> textures_decode_disabled(gSavedSettings,"TextureDecodeDisabled"); - if(textures_decode_disabled) + + setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + if (textures_decode_disabled) { // for debug use, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1420,7 +1664,6 @@ bool LLTextureFetchWorker::doWork(S32 param) { // We aborted, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } @@ -1430,7 +1673,6 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } if (mLoadedDiscard < 0) @@ -1439,10 +1681,9 @@ bool LLTextureFetchWorker::doWork(S32 param) //abort, don't decode mState = DONE; - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); return true; } - setPriority(LLWorkerThread::PRIORITY_LOW | mWorkPriority); // Set priority first since Responder may change it + mRawImage = NULL; mAuxImage = NULL; llassert_always(mFormattedImage.notNull()); @@ -1528,6 +1769,7 @@ bool LLTextureFetchWorker::doWork(S32 param) U32 cache_priority = mWorkPriority; mWritten = FALSE; mState = WAIT_ON_WRITE; + ++mCacheWriteCount; CacheWriteResponder* responder = new CacheWriteResponder(mFetcher, mID); mCacheWriteHandle = mFetcher->mTextureCache->writeToCache(mID, cache_priority, mFormattedImage->getData(), datasize, @@ -1572,9 +1814,84 @@ bool LLTextureFetchWorker::doWork(S32 param) } return false; -} +} // -Mw + +// Threads: Ttf +// virtual +void LLTextureFetchWorker::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + static LLCachedControl<bool> log_to_viewer_log(gSavedSettings, "LogTextureDownloadsToViewerLog"); + static LLCachedControl<bool> log_to_sim(gSavedSettings, "LogTextureDownloadsToSimulator"); + static LLCachedControl<bool> log_texture_traffic(gSavedSettings, "LogTextureNetworkTraffic") ; + + LLMutexLock lock(&mWorkMutex); // +Mw + + mHttpActive = false; + + if (log_to_viewer_log || log_to_sim) + { + U64 timeNow = LLTimer::getTotalTime(); + mFetcher->mTextureInfo.setRequestStartTime(mID, mMetricsStartTime); + mFetcher->mTextureInfo.setRequestType(mID, LLTextureInfoDetails::REQUEST_TYPE_HTTP); + mFetcher->mTextureInfo.setRequestSize(mID, mRequestedSize); + mFetcher->mTextureInfo.setRequestOffset(mID, mRequestedOffset); + mFetcher->mTextureInfo.setRequestCompleteTimeAndLog(mID, timeNow); + } + + bool success = true; + bool partial = false; + LLCore::HttpStatus status(response->getStatus()); + + lldebugs << "HTTP COMPLETE: " << mID + << " status: " << status.toHex() + << " '" << status.toString() << "'" + << llendl; +// unsigned int offset(0), length(0), full_length(0); +// response->getRange(&offset, &length, &full_length); +// llwarns << "HTTP COMPLETE: " << mID << " handle: " << handle +// << " status: " << status.toULong() << " '" << status.toString() << "'" +// << " req offset: " << mRequestedOffset << " req length: " << mRequestedSize +// << " offset: " << offset << " length: " << length +// << llendl; + + if (! status) + { + success = false; + std::string reason(status.toString()); + setGetStatus(status, reason); + llwarns << "CURL GET FAILED, status: " << status.toHex() + << " reason: " << reason << llendl; + } + else + { + // A warning about partial (HTTP 206) data. Some grid services + // do *not* return a 'Content-Range' header in the response to + // Range requests with a 206 status. We're forced to assume + // we get what we asked for in these cases until we can fix + // the services. + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + partial = (par_status == status); + } + + S32 data_size = callbackHttpGet(response, partial, success); + + if (log_texture_traffic && data_size > 0) + { + LLViewerTexture* tex = LLViewerTextureManager::findTexture(mID); + if (tex) + { + gTotalTextureBytesPerBoostLevel[tex->getBoostLevel()] += data_size ; + } + } + + mFetcher->removeFromHTTPQueue(mID, data_size); + + recordTextureDone(true); +} // -Mw + -// Called from MAIN thread +// Threads: Tmain void LLTextureFetchWorker::endWork(S32 param, bool aborted) { if (mDecodeHandle != 0) @@ -1587,6 +1904,8 @@ void LLTextureFetchWorker::endWork(S32 param, bool aborted) ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf + // virtual void LLTextureFetchWorker::finishWork(S32 param, bool completed) { @@ -1603,10 +1922,37 @@ void LLTextureFetchWorker::finishWork(S32 param, bool completed) } } +// LLQueuedThread's update() method is asking if it's okay to +// delete this worker. You'll notice we're not locking in here +// which is a slight concern. Caller is expected to have made +// this request 'quiet' by whatever means... +// +// Threads: Tmain + // virtual bool LLTextureFetchWorker::deleteOK() { bool delete_ok = true; + + if (mHttpActive) + { + // HTTP library has a pointer to this worker + // and will dereference it to do notification. + delete_ok = false; + } + + if (WAIT_HTTP_RESOURCE2 == mState) + { + if (mFetcher->isHttpWaiter(mID)) + { + // Don't delete the worker out from under the releaseHttpWaiters() + // method. Keep the pointers valid, clean up after that method + // has recognized the cancelation and removed the UUID from the + // waiter list. + delete_ok = false; + } + } + // Allow any pending reads or writes to complete if (mCacheReadHandle != LLTextureCache::nullHandle()) { @@ -1641,6 +1987,7 @@ bool LLTextureFetchWorker::deleteOK() return delete_ok; } +// Threads: Ttf void LLTextureFetchWorker::removeFromCache() { if (!mInLocalCache) @@ -1652,6 +1999,8 @@ void LLTextureFetchWorker::removeFromCache() ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf +// Locks: Mw bool LLTextureFetchWorker::processSimulatorPackets() { if (mFormattedImage.isNull() || mRequestedSize < 0) @@ -1712,14 +2061,13 @@ bool LLTextureFetchWorker::processSimulatorPackets() ////////////////////////////////////////////////////////////////////////////// -S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +// Threads: Ttf +// Locks: Mw +S32 LLTextureFetchWorker::callbackHttpGet(LLCore::HttpResponse * response, + bool partial, bool success) { S32 data_size = 0 ; - LLMutexLock lock(&mWorkMutex); - mHTTPHandle = 0; if (mState != WAIT_HTTP_REQ) { llwarns << "callbackHttpGet for unrequested fetch worker: " << mID @@ -1734,27 +2082,68 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, if (success) { // get length of stream: - data_size = buffer->countAfter(channels.in(), NULL); - + LLCore::BufferArray * body(response->getBody()); + data_size = body ? body->size() : 0; + LL_DEBUGS("Texture") << "HTTP RECEIVED: " << mID.asString() << " Bytes: " << data_size << LL_ENDL; if (data_size > 0) { // *TODO: set the formatted image data here directly to avoid the copy - mBuffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, mBuffer, data_size); - mBufferSize += data_size; - if (mRequestedSize == 0) + + // Hold on to body for later copy + llassert_always(NULL == mHttpBufferArray); + body->addRef(); + mHttpBufferArray = body; + + if (partial) + { + unsigned int offset(0), length(0), full_length(0); + response->getRange(&offset, &length, &full_length); + if (! offset && ! length) + { + // This is the case where we receive a 206 status but + // there wasn't a useful Content-Range header in the response. + // This could be because it was badly formatted but is more + // likely due to capabilities services which scrub headers + // from responses. Assume we got what we asked for... + mHttpReplySize = data_size; + mHttpReplyOffset = mRequestedOffset; + } + else + { + mHttpReplySize = length; + mHttpReplyOffset = offset; + } + } + + if (! partial) + { + // Response indicates this is the entire asset regardless + // of our asking for a byte range. Mark it so and drop + // any partial data we might have so that the current + // response body becomes the entire dataset. + if (data_size <= mRequestedOffset) + { + LL_WARNS("Texture") << "Fetched entire texture " << mID + << " when it was expected to be marked complete. mImageSize: " + << mFileSize << " datasize: " << mFormattedImage->getDataSize() + << LL_ENDL; + } + mHaveAllData = TRUE; + llassert_always(mDecodeHandle == 0); + mFormattedImage = NULL; // discard any previous data we had + } + else if (data_size < mRequestedSize) { mHaveAllData = TRUE; } else if (data_size > mRequestedSize) { - // *TODO: This shouldn't be happening any more + // *TODO: This shouldn't be happening any more (REALLY don't expect this anymore) llwarns << "data_size = " << data_size << " > requested: " << mRequestedSize << llendl; mHaveAllData = TRUE; llassert_always(mDecodeHandle == 0); mFormattedImage = NULL; // discard any previous data we had - mBufferSize = data_size; } } else @@ -1778,10 +2167,11 @@ S32 LLTextureFetchWorker::callbackHttpGet(const LLChannelDescriptors& channels, ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttc void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != LOAD_FROM_TEXTURE_CACHE) { // llwarns << "Read callback for " << mID << " with state = " << mState << llendl; @@ -1801,11 +2191,12 @@ void LLTextureFetchWorker::callbackCacheRead(bool success, LLImageFormatted* ima } mLoaded = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw +// Threads: Ttc void LLTextureFetchWorker::callbackCacheWrite(bool success) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mState != WAIT_ON_WRITE) { // llwarns << "Write callback for " << mID << " with state = " << mState << llendl; @@ -1813,13 +2204,14 @@ void LLTextureFetchWorker::callbackCacheWrite(bool success) } mWritten = TRUE; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Tid void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImageRaw* aux) { - LLMutexLock lock(&mWorkMutex); + LLMutexLock lock(&mWorkMutex); // +Mw if (mDecodeHandle == 0) { return; // aborted, ignore @@ -1852,10 +2244,11 @@ void LLTextureFetchWorker::callbackDecoded(bool success, LLImageRaw* raw, LLImag // llinfos << mID << " : DECODE COMPLETE " << llendl; setPriority(LLWorkerThread::PRIORITY_HIGH | mWorkPriority); mCacheReadTime = mCacheReadTimer.getElapsedTimeF32(); -} +} // -Mw ////////////////////////////////////////////////////////////////////////////// +// Threads: Ttf bool LLTextureFetchWorker::writeToCacheComplete() { // Complete write to cache @@ -1878,6 +2271,36 @@ bool LLTextureFetchWorker::writeToCacheComplete() } +// Threads: Ttf +void LLTextureFetchWorker::recordTextureStart(bool is_http) +{ + if (! mMetricsStartTime) + { + mMetricsStartTime = LLViewerAssetStatsFF::get_timestamp(); + } + LLViewerAssetStatsFF::record_enqueue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + +// Threads: Ttf +void LLTextureFetchWorker::recordTextureDone(bool is_http) +{ + if (mMetricsStartTime) + { + LLViewerAssetStatsFF::record_response_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType, + LLViewerAssetStatsFF::get_timestamp() - mMetricsStartTime); + mMetricsStartTime = 0; + } + LLViewerAssetStatsFF::record_dequeue_thread1(LLViewerAssetType::AT_TEXTURE, + is_http, + LLImageBase::TYPE_AVATAR_BAKE == mType); +} + + ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // public @@ -1893,14 +2316,23 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image mTextureCache(cache), mImageDecodeThread(imagedecodethread), mTextureBandwidth(0), - mCurlGetRequest(NULL), + mHTTPTextureBits(0), + mTotalHTTPRequests(0), mQAMode(qa_mode), + mHttpRequest(NULL), + mHttpOptions(NULL), + mHttpHeaders(NULL), + mHttpMetricsHeaders(NULL), + mHttpPolicyClass(LLCore::HttpRequest::DEFAULT_POLICY_ID), + mHttpSemaphore(HTTP_REQUESTS_IN_QUEUE_HIGH_WATER), + mTotalCacheReadCount(0U), + mTotalCacheWriteCount(0U), + mTotalResourceWaitCount(0U), mFetchDebugger(NULL), mFetchSource(LLTextureFetch::FROM_ALL), mOriginFetchSource(LLTextureFetch::FROM_ALL), mFetcherLocked(FALSE) { - mCurlPOSTRequestCount = 0; mMaxBandwidth = gSavedSettings.getF32("ThrottleBandwidthKBPS"); mTextureInfo.setUpLogging(gSavedSettings.getBOOL("LogTextureDownloadsToViewerLog"), gSavedSettings.getBOOL("LogTextureDownloadsToSimulator"), gSavedSettings.getU32("TextureLoggingThreshold")); @@ -1916,11 +2348,19 @@ LLTextureFetch::LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* image } mOriginFetchSource = mFetchSource; } + + mHttpRequest = new LLCore::HttpRequest; + mHttpOptions = new LLCore::HttpOptions; + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + mHttpMetricsHeaders = new LLCore::HttpHeaders; + mHttpMetricsHeaders->mHeaders.push_back("Content-Type: application/llsd+xml"); + mHttpPolicyClass = LLAppViewer::instance()->getAppCoreHttp().getPolicyDefault(); } LLTextureFetch::~LLTextureFetch() { - clearDeleteList() ; + clearDeleteList(); while (! mCommands.empty()) { @@ -1928,10 +2368,34 @@ LLTextureFetch::~LLTextureFetch() mCommands.erase(mCommands.begin()); delete req; } + + if (mHttpOptions) + { + mHttpOptions->release(); + mHttpOptions = NULL; + } + + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } + + if (mHttpMetricsHeaders) + { + mHttpMetricsHeaders->release(); + mHttpMetricsHeaders = NULL; + } + + mHttpWaitResource.clear(); - // ~LLQueuedThread() called here + delete mHttpRequest; + mHttpRequest = NULL; delete mFetchDebugger; + mFetchDebugger = NULL; + + // ~LLQueuedThread() called here } bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, @@ -1997,7 +2461,7 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con { return false; // need to wait for previous aborted request to complete } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setImagePriority(priority); @@ -2006,41 +2470,44 @@ bool LLTextureFetch::createRequest(const std::string& url, const LLUUID& id, con if (!worker->haveWork()) { worker->mState = LLTextureFetchWorker::INIT; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw worker->addWork(0, LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); } else { - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else { worker = new LLTextureFetchWorker(this, url, id, host, priority, desired_discard, desired_size); - lockQueue() ; + lockQueue(); // +Mfq mRequestMap[id] = worker; - unlockQueue() ; + unlockQueue(); // -Mfq - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->mActiveCount++; worker->mNeedsAux = needs_aux; worker->setCanUseHTTP(can_use_http) ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } // llinfos << "REQUESTED: " << id << " Discard: " << desired_discard << llendl; return true; } + +// Threads: T* (but Ttf in practice) + // protected void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { - lockQueue() ; + lockQueue(); // +Mfq bool in_request_map = (mRequestMap.find(worker->mID) != mRequestMap.end()) ; - unlockQueue() ; + unlockQueue(); // -Mfq - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq if (in_request_map) { // only add to the queue if in the request map @@ -2052,27 +2519,68 @@ void LLTextureFetch::addToNetworkQueue(LLTextureFetchWorker* worker) { iter1->second.erase(worker->mID); } -} +} // -Mfnq +// Threads: T* void LLTextureFetch::removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel) { - LLMutexLock lock(&mNetworkQueueMutex); + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq size_t erased = mNetworkQueue.erase(worker->mID); if (cancel && erased > 0) { mCancelQueue[worker->mHost].insert(worker->mID); } -} +} // -Mfnq + +// Threads: T* +// +// protected +void LLTextureFetch::addToHTTPQueue(const LLUUID& id) +{ + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.insert(id); + mTotalHTTPRequests++; +} // -Mfnq +// Threads: T* +void LLTextureFetch::removeFromHTTPQueue(const LLUUID& id, S32 received_size) +{ + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + mHTTPTextureQueue.erase(id); + mHTTPTextureBits += received_size * 8; // Approximate - does not include header bits +} // -Mfnq + +// NB: If you change deleteRequest() you should probably make +// parallel changes in removeRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::deleteRequest(const LLUUID& id, bool cancel) { - lockQueue() ; + lockQueue(); // +Mfq LLTextureFetchWorker* worker = getWorkerAfterLock(id); - unlockQueue() ; + if (worker) + { + size_t erased_1 = mRequestMap.erase(worker->mID); + unlockQueue(); // -Mfq + + llassert_always(erased_1 > 0) ; + removeFromNetworkQueue(worker, cancel); + llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - removeRequest(worker, cancel); + worker->scheduleDelete(); + } + else + { + unlockQueue(); // -Mfq + } } +// NB: If you change removeRequest() you should probably make +// parallel changes in deleteRequest(). They're functionally +// identical with only argument variations. +// +// Threads: T* void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) { if(!worker) @@ -2080,15 +2588,14 @@ void LLTextureFetch::removeRequest(LLTextureFetchWorker* worker, bool cancel) return; } - lockQueue() ; + lockQueue(); // +Mfq size_t erased_1 = mRequestMap.erase(worker->mID); - unlockQueue() ; + unlockQueue(); // -Mfq llassert_always(erased_1 > 0) ; removeFromNetworkQueue(worker, cancel); llassert_always(!(worker->getFlags(LLWorkerClass::WCF_DELETE_REQUESTED))) ; - worker->removeFromHTTPQueue(); worker->scheduleDelete(); } @@ -2110,16 +2617,39 @@ void LLTextureFetch::deleteAllRequests() } } +// Threads: T* S32 LLTextureFetch::getNumRequests() { - lockQueue() ; + lockQueue(); // +Mfq S32 size = (S32)mRequestMap.size(); - unlockQueue() ; + unlockQueue(); // -Mfq + + return size; +} + +// Threads: T* +S32 LLTextureFetch::getNumHTTPRequests() +{ + mNetworkQueueMutex.lock(); // +Mfq + S32 size = (S32)mHTTPTextureQueue.size(); + mNetworkQueueMutex.unlock(); // -Mfq + + return size; +} + +// Threads: T* +U32 LLTextureFetch::getTotalNumHTTPRequests() +{ + mNetworkQueueMutex.lock(); // +Mfq + U32 size = mTotalHTTPRequests; + mNetworkQueueMutex.unlock(); // -Mfq - return size ; + return size; } // call lockQueue() first! +// Threads: T* +// Locks: Mfq LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) { LLTextureFetchWorker* res = NULL; @@ -2131,14 +2661,16 @@ LLTextureFetchWorker* LLTextureFetch::getWorkerAfterLock(const LLUUID& id) return res; } +// Threads: T* LLTextureFetchWorker* LLTextureFetch::getWorker(const LLUUID& id) { - LLMutexLock lock(&mQueueMutex) ; + LLMutexLock lock(&mQueueMutex); // +Mfq - return getWorkerAfterLock(id) ; -} + return getWorkerAfterLock(id); +} // -Mfq +// Threads: T* bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux) { @@ -2161,7 +2693,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } else if (worker->checkWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw discard_level = worker->mDecodedDiscard; raw = worker->mRawImage; aux = worker->mAuxImage; @@ -2172,11 +2704,11 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, } res = true; LL_DEBUGS("Texture") << id << ": Request Finished. State: " << worker->mState << " Discard: " << discard_level << LL_ENDL; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } else { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw if ((worker->mDecodedDiscard >= 0) && (worker->mDecodedDiscard < discard_level || discard_level < 0) && (worker->mState >= LLTextureFetchWorker::WAIT_ON_WRITE)) @@ -2186,7 +2718,7 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, raw = worker->mRawImage; aux = worker->mAuxImage; } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } } else @@ -2196,15 +2728,16 @@ bool LLTextureFetch::getRequestFinished(const LLUUID& id, S32& discard_level, return res; } +// Threads: T* bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) { bool res = false; LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw worker->setImagePriority(priority); - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw res = true; } return res; @@ -2219,24 +2752,24 @@ bool LLTextureFetch::updateRequestPriority(const LLUUID& id, F32 priority) // in step, at least until this can be refactored and // the redundancy eliminated. // -// May be called from any thread +// Threads: T* //virtual S32 LLTextureFetch::getPending() { S32 res; - lockData(); + lockData(); // +Ct { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq res = mRequestQueue.size(); - res += mCurlPOSTRequestCount; res += mCommands.size(); - } - unlockData(); + } // -Mfq + unlockData(); // -Ct return res; } +// Locks: Ct // virtual bool LLTextureFetch::runCondition() { @@ -2251,42 +2784,53 @@ bool LLTextureFetch::runCondition() bool have_no_commands(false); { - LLMutexLock lock(&mQueueMutex); + LLMutexLock lock(&mQueueMutex); // +Mfq have_no_commands = mCommands.empty(); - } - - bool have_no_curl_requests(0 == mCurlPOSTRequestCount); + } // -Mfq return ! (have_no_commands - && have_no_curl_requests && (mRequestQueue.empty() && mIdleThread)); // From base class } ////////////////////////////////////////////////////////////////////////////// -// MAIN THREAD (unthreaded envs), WORKER THREAD (threaded envs) +// Threads: Ttf void LLTextureFetch::commonUpdate() { + // Release waiters + releaseHttpWaiters(); + // Run a cross-thread command, if any. cmdDoWork(); - // Update Curl on same thread as mCurlGetRequest was constructed - S32 processed = mCurlGetRequest->process(); - if (processed > 0) + // Deliver all completion notifications + LLCore::HttpStatus status = mHttpRequest->update(0); + if (! status) { - lldebugs << "processed: " << processed << " messages." << llendl; + LL_INFOS_ONCE("Texture") << "Problem during HTTP servicing. Reason: " + << status.toString() + << LL_ENDL; } } -// MAIN THREAD +// Threads: Tmain + //virtual S32 LLTextureFetch::update(F32 max_time_ms) { static LLCachedControl<F32> band_width(gSavedSettings,"ThrottleBandwidthKBPS"); - mMaxBandwidth = band_width ; + { + mNetworkQueueMutex.lock(); // +Mfnq + mMaxBandwidth = band_width; + + gTextureList.sTextureBits += mHTTPTextureBits; + mHTTPTextureBits = 0; + + mNetworkQueueMutex.unlock(); // -Mfnq + } S32 res = LLWorkerThread::update(max_time_ms); @@ -2304,19 +2848,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) if (!mThreaded) { commonUpdate(); - - if(mCurlGetRequest) - { - mCurlGetRequest->nextRequests(); - } - } - - if(mCurlGetRequest) - { - gTextureList.sTextureBits += mCurlGetRequest->getTotalReceivedBits(); } - if(mFetchDebugger) + if (mFetchDebugger) { mFetchDebugger->tryToStopDebug(); //check if need to stop debugger. } @@ -2324,7 +2858,9 @@ S32 LLTextureFetch::update(F32 max_time_ms) return res; } -//called in the MAIN thread after the TextureCacheThread shuts down. +// called in the MAIN thread after the TextureCacheThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownTextureCacheThread() { if(mTextureCache) @@ -2334,7 +2870,9 @@ void LLTextureFetch::shutDownTextureCacheThread() } } -//called in the MAIN thread after the ImageDecodeThread shuts down. +// called in the MAIN thread after the ImageDecodeThread shuts down. +// +// Threads: Tmain void LLTextureFetch::shutDownImageDecodeThread() { if(mImageDecodeThread) @@ -2344,37 +2882,27 @@ void LLTextureFetch::shutDownImageDecodeThread() } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::startThread() { - // Construct mCurlGetRequest from Worker Thread - mCurlGetRequest = new LLCurlTextureRequest(8); - - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(mCurlGetRequest); - } } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::endThread() { - // Destroy mCurlGetRequest from Worker Thread - delete mCurlGetRequest; - mCurlGetRequest = NULL; - if(mFetchDebugger) - { - mFetchDebugger->setCurlGetRequest(NULL); - } + LL_INFOS("Texture") << "CacheReads: " << mTotalCacheReadCount + << ", CacheWrites: " << mTotalCacheWriteCount + << ", ResWaits: " << mTotalResourceWaitCount + << ", TotalHTTPReq: " << getTotalNumHTTPRequests() + << LL_ENDL; } -// WORKER THREAD +// Threads: Ttf void LLTextureFetch::threadedUpdate() { - llassert_always(mCurlGetRequest); - - mCurlGetRequest->nextRequests(); + llassert_always(mHttpRequest); +#if 0 // Limit update frequency const F32 PROCESS_TIME = 0.05f; static LLFrameTimer process_timer; @@ -2383,9 +2911,10 @@ void LLTextureFetch::threadedUpdate() return; } process_timer.reset(); +#endif commonUpdate(); - + #if 0 const F32 INFO_TIME = 1.0f; static LLFrameTimer info_timer; @@ -2399,11 +2928,11 @@ void LLTextureFetch::threadedUpdate() } } #endif - } ////////////////////////////////////////////////////////////////////////////// +// Threads: Tmain void LLTextureFetch::sendRequestListToSimulators() { // All requests @@ -2429,48 +2958,48 @@ void LLTextureFetch::sendRequestListToSimulators() typedef std::map< LLHost, request_list_t > work_request_map_t; work_request_map_t requests; { - LLMutexLock lock2(&mNetworkQueueMutex); - for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) - { - queue_t::iterator curiter = iter++; - LLTextureFetchWorker* req = getWorker(*curiter); - if (!req) - { - mNetworkQueue.erase(curiter); - continue; // paranoia - } - if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && - (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) - { - // We already received our URL, remove from the queue - llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; - mNetworkQueue.erase(curiter); - continue; - } - if (req->mID == mDebugID) - { - mDebugCount++; // for setting breakpoints - } - if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && - req->mTotalPackets > 0 && - req->mLastPacket >= req->mTotalPackets-1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + for (queue_t::iterator iter = mNetworkQueue.begin(); iter != mNetworkQueue.end(); ) { - // We have all the packets... make sure this is high priority + queue_t::iterator curiter = iter++; + LLTextureFetchWorker* req = getWorker(*curiter); + if (!req) + { + mNetworkQueue.erase(curiter); + continue; // paranoia + } + if ((req->mState != LLTextureFetchWorker::LOAD_FROM_NETWORK) && + (req->mState != LLTextureFetchWorker::LOAD_FROM_SIMULATOR)) + { + // We already received our URL, remove from the queue + llwarns << "Worker: " << req->mID << " in mNetworkQueue but in wrong state: " << req->mState << llendl; + mNetworkQueue.erase(curiter); + continue; + } + if (req->mID == mDebugID) + { + mDebugCount++; // for setting breakpoints + } + if (req->mSentRequest == LLTextureFetchWorker::SENT_SIM && + req->mTotalPackets > 0 && + req->mLastPacket >= req->mTotalPackets-1) + { + // We have all the packets... make sure this is high priority // req->setPriority(LLWorkerThread::PRIORITY_HIGH | req->mWorkPriority); - continue; - } - F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); - { - F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); - if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || - (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || - (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + continue; + } + F32 elapsed = req->mRequestedTimer.getElapsedTimeF32(); { - requests[req->mHost].insert(req); + F32 delta_priority = llabs(req->mRequestedPriority - req->mImagePriority); + if ((req->mSimRequestedDiscard != req->mDesiredDiscard) || + (delta_priority > MIN_DELTA_PRIORITY && elapsed >= MIN_REQUEST_TIME) || + (elapsed >= SIM_LAZY_FLUSH_TIMEOUT)) + { + requests[req->mHost].insert(req); + } } } - } - } + } // -Mfnq for (work_request_map_t::iterator iter1 = requests.begin(); iter1 != requests.end(); ++iter1) @@ -2493,9 +3022,9 @@ void LLTextureFetch::sendRequestListToSimulators() if (req->mSentRequest != LLTextureFetchWorker::SENT_SIM) { // Initialize packet data based on data read from cache - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->setupPacketData(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw } if (0 == sim_request_count) { @@ -2524,12 +3053,12 @@ void LLTextureFetch::sendRequestListToSimulators() mTextureInfo.setRequestType(req->mID, LLTextureInfoDetails::REQUEST_TYPE_UDP); } - req->lockWorkMutex(); + req->lockWorkMutex(); // +Mw req->mSentRequest = LLTextureFetchWorker::SENT_SIM; req->mSimRequestedDiscard = req->mDesiredDiscard; req->mRequestedPriority = req->mImagePriority; req->mRequestedTimer.reset(); - req->unlockWorkMutex(); + req->unlockWorkMutex(); // -Mw sim_request_count++; if (sim_request_count >= IMAGES_PER_REQUEST) { @@ -2550,55 +3079,57 @@ void LLTextureFetch::sendRequestListToSimulators() // Send cancelations { - LLMutexLock lock2(&mNetworkQueueMutex); - if (gMessageSystem && !mCancelQueue.empty()) - { - for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); - iter1 != mCancelQueue.end(); ++iter1) + LLMutexLock lock2(&mNetworkQueueMutex); // +Mfnq + if (gMessageSystem && !mCancelQueue.empty()) { - LLHost host = iter1->first; - if (host == LLHost::invalid) - { - host = gAgent.getRegionHost(); - } - S32 request_count = 0; - for (queue_t::iterator iter2 = iter1->second.begin(); - iter2 != iter1->second.end(); ++iter2) + for (cancel_queue_t::iterator iter1 = mCancelQueue.begin(); + iter1 != mCancelQueue.end(); ++iter1) { - if (0 == request_count) + LLHost host = iter1->first; + if (host == LLHost::invalid) { - gMessageSystem->newMessageFast(_PREHASH_RequestImage); - gMessageSystem->nextBlockFast(_PREHASH_AgentData); - gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); - gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + host = gAgent.getRegionHost(); } - gMessageSystem->nextBlockFast(_PREHASH_RequestImage); - gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); - gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); - gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); - gMessageSystem->addU32Fast(_PREHASH_Packet, 0); - gMessageSystem->addU8Fast(_PREHASH_Type, 0); + S32 request_count = 0; + for (queue_t::iterator iter2 = iter1->second.begin(); + iter2 != iter1->second.end(); ++iter2) + { + if (0 == request_count) + { + gMessageSystem->newMessageFast(_PREHASH_RequestImage); + gMessageSystem->nextBlockFast(_PREHASH_AgentData); + gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgent.getID()); + gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID()); + } + gMessageSystem->nextBlockFast(_PREHASH_RequestImage); + gMessageSystem->addUUIDFast(_PREHASH_Image, *iter2); + gMessageSystem->addS8Fast(_PREHASH_DiscardLevel, -1); + gMessageSystem->addF32Fast(_PREHASH_DownloadPriority, 0); + gMessageSystem->addU32Fast(_PREHASH_Packet, 0); + gMessageSystem->addU8Fast(_PREHASH_Type, 0); // llinfos << "CANCELING IMAGE REQUEST: " << (*iter2) << llendl; - request_count++; - if (request_count >= IMAGES_PER_REQUEST) + request_count++; + if (request_count >= IMAGES_PER_REQUEST) + { + gMessageSystem->sendSemiReliable(host, NULL, NULL); + request_count = 0; + } + } + if (request_count > 0 && request_count < IMAGES_PER_REQUEST) { gMessageSystem->sendSemiReliable(host, NULL, NULL); - request_count = 0; } } - if (request_count > 0 && request_count < IMAGES_PER_REQUEST) - { - gMessageSystem->sendSemiReliable(host, NULL, NULL); - } + mCancelQueue.clear(); } - mCancelQueue.clear(); - } - } + } // -Mfnq } ////////////////////////////////////////////////////////////////////////////// +// Threads: T* +// Locks: Mw bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) { mRequestedTimer.reset(); @@ -2631,6 +3162,7 @@ bool LLTextureFetchWorker::insertPacket(S32 index, U8* data, S32 size) return true; } +// Threads: T* bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data) { @@ -2665,14 +3197,14 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw // Copy header data into image object worker->mImageCodec = codec; @@ -2683,10 +3215,12 @@ bool LLTextureFetch::receiveImageHeader(const LLHost& host, const LLUUID& id, U8 res = worker->insertPacket(0, data, data_size); worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); worker->mState = LLTextureFetchWorker::LOAD_FROM_SIMULATOR; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } + +// Threads: T* bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data) { LLTextureFetchWorker* worker = getWorker(id); @@ -2711,14 +3245,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 } if (!res) { + mNetworkQueueMutex.lock(); // +Mfnq ++mBadPacketCount; - mNetworkQueueMutex.lock() ; mCancelQueue[host].insert(id); - mNetworkQueueMutex.unlock() ; + mNetworkQueueMutex.unlock(); // -Mfnq return false; } - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw res = worker->insertPacket(packet_num, data, data_size); @@ -2735,7 +3269,7 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 removeFromNetworkQueue(worker, true); // failsafe } - if(packet_num >= (worker->mTotalPackets - 1)) + if (packet_num >= (worker->mTotalPackets - 1)) { static LLCachedControl<bool> log_to_viewer_log(gSavedSettings,"LogTextureDownloadsToViewerLog"); static LLCachedControl<bool> log_to_sim(gSavedSettings,"LogTextureDownloadsToSimulator"); @@ -2747,12 +3281,14 @@ bool LLTextureFetch::receiveImagePacket(const LLHost& host, const LLUUID& id, U1 mTextureInfo.setRequestCompleteTimeAndLog(id, timeNow); } } - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw return res; } ////////////////////////////////////////////////////////////////////////////// + +// Threads: T* BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) { BOOL from_cache = FALSE ; @@ -2760,14 +3296,15 @@ BOOL LLTextureFetch::isFromLocalCache(const LLUUID& id) LLTextureFetchWorker* worker = getWorker(id); if (worker) { - worker->lockWorkMutex() ; - from_cache = worker->mInLocalCache ; - worker->unlockWorkMutex() ; + worker->lockWorkMutex(); // +Mw + from_cache = worker->mInLocalCache; + worker->unlockWorkMutex(); // -Mw } return from_cache ; } +// Threads: T* S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http) { @@ -2781,7 +3318,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r LLTextureFetchWorker* worker = getWorker(id); if (worker && worker->haveWork()) { - worker->lockWorkMutex(); + worker->lockWorkMutex(); // +Mw state = worker->mState; fetch_dtime = worker->mFetchTimer.getElapsedTimeF32(); request_dtime = worker->mRequestedTimer.getElapsedTimeF32(); @@ -2808,7 +3345,7 @@ S32 LLTextureFetch::getFetchState(const LLUUID& id, F32& data_progress_p, F32& r } fetch_priority = worker->getPriority(); can_use_http = worker->getCanUseHTTP() ; - worker->unlockWorkMutex(); + worker->unlockWorkMutex(); // -Mw } data_progress_p = data_progress; requested_priority_p = requested_priority; @@ -2832,12 +3369,219 @@ void LLTextureFetch::dump() << " STATE: " << worker->sStateDescs[worker->mState] << llendl; } + + llinfos << "LLTextureFetch ACTIVE_HTTP:" << llendl; + for (queue_t::const_iterator iter(mHTTPTextureQueue.begin()); + mHTTPTextureQueue.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } + + llinfos << "LLTextureFetch WAIT_HTTP_RESOURCE:" << llendl; + for (wait_http_res_queue_t::const_iterator iter(mHttpWaitResource.begin()); + mHttpWaitResource.end() != iter; + ++iter) + { + llinfos << " ID: " << (*iter) << llendl; + } +} + +////////////////////////////////////////////////////////////////////////////// + +// HTTP Resource Waiting Methods + +// Threads: Ttf +void LLTextureFetch::addHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.insert(tid); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: Ttf +void LLTextureFetch::removeHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + if (mHttpWaitResource.end() != iter) + { + mHttpWaitResource.erase(iter); + } + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +bool LLTextureFetch::isHttpWaiter(const LLUUID & tid) +{ + mNetworkQueueMutex.lock(); // +Mfnq + wait_http_res_queue_t::iterator iter(mHttpWaitResource.find(tid)); + const bool ret(mHttpWaitResource.end() != iter); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + +// Release as many requests as permitted from the WAIT_HTTP_RESOURCE2 +// state to the SEND_HTTP_REQ state based on their current priority. +// +// This data structures and code associated with this looks a bit +// indirect and naive but it's done in the name of safety. An +// ordered container may become invalid from time to time due to +// priority changes caused by actions in other threads. State itself +// could also suffer the same fate with canceled operations. Even +// done this way, I'm not fully trusting we're truly safe. This +// module is due for a major refactoring and we'll deal with it then. +// +// Threads: Ttf +// Locks: -Mw (must not hold any worker when called) +void LLTextureFetch::releaseHttpWaiters() +{ + // Use mHttpSemaphore rather than mHTTPTextureQueue.size() + // to avoid a lock. + if (mHttpSemaphore < (HTTP_REQUESTS_IN_QUEUE_HIGH_WATER - HTTP_REQUESTS_IN_QUEUE_LOW_WATER)) + return; + + // Quickly make a copy of all the LLUIDs. Get off the + // mutex as early as possible. + typedef std::vector<LLUUID> uuid_vec_t; + uuid_vec_t tids; + + { + LLMutexLock lock(&mNetworkQueueMutex); // +Mfnq + + if (mHttpWaitResource.empty()) + return; + tids.reserve(mHttpWaitResource.size()); + tids.assign(mHttpWaitResource.begin(), mHttpWaitResource.end()); + } // -Mfnq + + // Now lookup the UUUIDs to find valid requests and sort + // them in priority order, highest to lowest. We're going + // to modify priority later as a side-effect of releasing + // these objects. That, in turn, would violate the partial + // ordering assumption of std::set, std::map, etc. so we + // don't use those containers. We use a vector and an explicit + // sort to keep the containers valid later. + typedef std::vector<LLTextureFetchWorker *> worker_list_t; + worker_list_t tids2; + + tids2.reserve(tids.size()); + for (uuid_vec_t::iterator iter(tids.begin()); + tids.end() != iter; + ++iter) + { + LLTextureFetchWorker * worker(getWorker(* iter)); + if (worker) + { + tids2.push_back(worker); + } + else + { + // If worker isn't found, this should be due to a request + // for deletion. We signal our recognition that this + // uuid shouldn't be used for resource waiting anymore by + // erasing it from the resource waiter list. That allows + // deleteOK to do final deletion on the worker. + removeHttpWaiter(* iter); + } + } + tids.clear(); + + // Sort into priority order, if necessary and only as much as needed + if (tids2.size() > mHttpSemaphore) + { + LLTextureFetchWorker::Compare compare; + std::partial_sort(tids2.begin(), tids2.begin() + mHttpSemaphore, tids2.end(), compare); + } + + // Release workers up to the high water mark. Since we aren't + // holding any locks at this point, we can be in competition + // with other callers. Do defensive things like getting + // refreshed counts of requests and checking if someone else + // has moved any worker state around.... + for (worker_list_t::iterator iter2(tids2.begin()); tids2.end() != iter2; ++iter2) + { + LLTextureFetchWorker * worker(* iter2); + + worker->lockWorkMutex(); // +Mw + if (LLTextureFetchWorker::WAIT_HTTP_RESOURCE2 != worker->mState) + { + // Not in expected state, remove it, try the next one + worker->unlockWorkMutex(); // -Mw + LL_WARNS("Texture") << "Resource-waited texture " << worker->mID + << " in unexpected state: " << worker->mState + << ". Removing from wait list." + << LL_ENDL; + removeHttpWaiter(worker->mID); + continue; + } + + if (! worker->acquireHttpSemaphore()) + { + // Out of active slots, quit + worker->unlockWorkMutex(); // -Mw + break; + } + + worker->mState = LLTextureFetchWorker::SEND_HTTP_REQ; + worker->setPriority(LLWorkerThread::PRIORITY_HIGH | worker->mWorkPriority); + worker->unlockWorkMutex(); // -Mw + + removeHttpWaiter(worker->mID); + } +} + +// Threads: T* +void LLTextureFetch::cancelHttpWaiters() +{ + mNetworkQueueMutex.lock(); // +Mfnq + mHttpWaitResource.clear(); + mNetworkQueueMutex.unlock(); // -Mfnq +} + +// Threads: T* +int LLTextureFetch::getHttpWaitersCount() +{ + mNetworkQueueMutex.lock(); // +Mfnq + int ret(mHttpWaitResource.size()); + mNetworkQueueMutex.unlock(); // -Mfnq + return ret; +} + + +// Threads: T* +void LLTextureFetch::updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait) +{ + LLMutexLock lock(&mQueueMutex); // +Mfq + + mTotalCacheReadCount += cache_read; + mTotalCacheWriteCount += cache_write; + mTotalResourceWaitCount += res_wait; +} // -Mfq + + +// Threads: T* +void LLTextureFetch::getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait) +{ + U32 ret1(0U), ret2(0U), ret3(0U); + + { + LLMutexLock lock(&mQueueMutex); // +Mfq + ret1 = mTotalCacheReadCount; + ret2 = mTotalCacheWriteCount; + ret3 = mTotalResourceWaitCount; + } // -Mfq + + *cache_read = ret1; + *cache_write = ret2; + *res_wait = ret3; } ////////////////////////////////////////////////////////////////////////////// // cross-thread command methods +// Threads: T* void LLTextureFetch::commandSetRegion(U64 region_handle) { TFReqSetRegion * req = new TFReqSetRegion(region_handle); @@ -2845,6 +3589,7 @@ void LLTextureFetch::commandSetRegion(U64 region_handle) cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, @@ -2855,6 +3600,7 @@ void LLTextureFetch::commandSendMetrics(const std::string & caps_url, cmdEnqueue(req); } +// Threads: T* void LLTextureFetch::commandDataBreak() { // The pedantically correct way to implement this is to create a command @@ -2865,30 +3611,33 @@ void LLTextureFetch::commandDataBreak() LLTextureFetch::svMetricsDataBreak = true; } +// Threads: T* void LLTextureFetch::cmdEnqueue(TFRequest * req) { - lockQueue(); + lockQueue(); // +Mfq mCommands.push_back(req); - unlockQueue(); + unlockQueue(); // -Mfq unpause(); } +// Threads: T* LLTextureFetch::TFRequest * LLTextureFetch::cmdDequeue() { TFRequest * ret = 0; - lockQueue(); + lockQueue(); // +Mfq if (! mCommands.empty()) { ret = mCommands.front(); mCommands.erase(mCommands.begin()); } - unlockQueue(); + unlockQueue(); // -Mfq return ret; } +// Threads: Ttf void LLTextureFetch::cmdDoWork() { if (mDebugPause) @@ -2912,6 +3661,37 @@ void LLTextureFetch::cmdDoWork() namespace { + +// Example of a simple notification handler for metrics +// delivery notification. Earlier versions of the code used +// a Responder that tried harder to detect delivery breaks +// but it really isn't that important. If someone wants to +// revisit that effort, here is a place to start. +class AssetReportHandler : public LLCore::HttpHandler +{ +public: + + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) + { + LLCore::HttpStatus status(response->getStatus()); + + if (status) + { + LL_WARNS("Texture") << "Successfully delivered asset metrics to grid." + << LL_ENDL; + } + else + { + LL_WARNS("Texture") << "Error delivering asset metrics to grid. Reason: " + << status.toString() << LL_ENDL; + } + } +}; // end class AssetReportHandler + +AssetReportHandler stats_handler; + + /** * Implements the 'Set Region' command. * @@ -2942,75 +3722,8 @@ TFReqSendMetrics::~TFReqSendMetrics() bool TFReqSendMetrics::doWork(LLTextureFetch * fetcher) { - /* - * HTTP POST responder. Doesn't do much but tries to - * detect simple breaks in recording the metrics stream. - * - * The 'volatile' modifiers don't indicate signals, - * mmap'd memory or threads, really. They indicate that - * the referenced data is part of a pseudo-closure for - * this responder rather than being required for correct - * operation. - * - * We don't try very hard with the POST request. We give - * it one shot and that's more-or-less it. With a proper - * refactoring of the LLQueuedThread usage, these POSTs - * could be put in a request object and made more reliable. - */ - class lcl_responder : public LLCurl::Responder - { - public: - lcl_responder(LLTextureFetch * fetcher, - S32 expected_sequence, - volatile const S32 & live_sequence, - volatile bool & reporting_break, - volatile bool & reporting_started) - : LLCurl::Responder(), - mFetcher(fetcher), - mExpectedSequence(expected_sequence), - mLiveSequence(live_sequence), - mReportingBreak(reporting_break), - mReportingStarted(reporting_started) - { - mFetcher->incrCurlPOSTCount(); - } - - ~lcl_responder() - { - LL_CHECK_MEMORY - mFetcher->decrCurlPOSTCount(); - LL_CHECK_MEMORY - } - - // virtual - void error(U32 status_num, const std::string & reason) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = true; - } - LL_WARNS("Texture") << "Break in metrics stream due to POST failure to metrics collection service. Reason: " - << reason << LL_ENDL; - } - - // virtual - void result(const LLSD & content) - { - if (mLiveSequence == mExpectedSequence) - { - mReportingBreak = false; - mReportingStarted = true; - } - } - - private: - LLTextureFetch * mFetcher; - S32 mExpectedSequence; - volatile const S32 & mLiveSequence; - volatile bool & mReportingBreak; - volatile bool & mReportingStarted; - - }; // class lcl_responder + static const U32 report_priority(1); + static LLCore::HttpHandler * const handler(fetcher->isQAMode() || true ? &stats_handler : NULL); if (! gViewerAssetStatsThread1) return true; @@ -3038,21 +3751,26 @@ TFReqSendMetrics::doWork(LLTextureFetch * fetcher) // Update sequence number if (S32_MAX == ++report_sequence) report_sequence = 0; - + reporting_started = true; + // Limit the size of the stats report if necessary. merged_llsd["truncated"] = truncate_viewer_metrics(10, merged_llsd); if (! mCapsURL.empty()) { - LLCurlRequest::headers_t headers; - fetcher->getCurlRequest().post(mCapsURL, - headers, - merged_llsd, - new lcl_responder(fetcher, - report_sequence, - report_sequence, - LLTextureFetch::svMetricsDataBreak, - reporting_started)); + LLCore::BufferArray * ba = new LLCore::BufferArray; + LLCore::BufferArrayStream bas(ba); + LLSDSerialize::toXML(merged_llsd, bas); + + fetcher->getHttpRequest().requestPost(fetcher->getPolicyClass(), + report_priority, + mCapsURL, + ba, + NULL, + fetcher->getMetricsHeaders(), + handler); + ba->release(); + LLTextureFetch::svMetricsDataBreak = false; } else { @@ -3110,6 +3828,7 @@ truncate_viewer_metrics(int max_regions, LLSD & metrics) } // end of anonymous namespace + /////////////////////////////////////////////////////////////////////////////////////////// //Start LLTextureFetchDebugger /////////////////////////////////////////////////////////////////////////////////////////// @@ -3163,48 +3882,13 @@ private: S32 mID; }; -class LLDebuggerHTTPResponder : public LLCurl::Responder -{ -public: - LLDebuggerHTTPResponder(LLTextureFetchDebugger* debugger, S32 index) - : mDebugger(debugger), mIndex(index) - { - } - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) - { - bool success = false; - bool partial = false; - if (HTTP_OK <= status && status < HTTP_MULTIPLE_CHOICES) - { - success = true; - if (HTTP_PARTIAL_CONTENT == status) // partial information - { - partial = true; - } - } - if (!success) - { - llinfos << "Fetch Debugger : CURL GET FAILED, index = " << mIndex << ", status:" << status << " reason:" << reason << llendl; - } - mDebugger->callbackHTTP(mIndex, channels, buffer, partial, success); - mDebugger->getCurlGetRequest()->completeRequest(0); - } - virtual bool followRedir() - { - return true; - } -private: - LLTextureFetchDebugger* mDebugger; - S32 mIndex; -}; LLTextureFetchDebugger::LLTextureFetchDebugger(LLTextureFetch* fetcher, LLTextureCache* cache, LLImageDecodeThread* imagedecodethread) : mFetcher(fetcher), mTextureCache(cache), mImageDecodeThread(imagedecodethread), - mCurlGetRequest(NULL) + mHttpHeaders(NULL), + mHttpPolicyClass(fetcher->getPolicyClass()) { init(); } @@ -3214,6 +3898,11 @@ LLTextureFetchDebugger::~LLTextureFetchDebugger() mFetchingHistory.clear(); mStopDebug = TRUE; tryToStopDebug(); + if (mHttpHeaders) + { + mHttpHeaders->release(); + mHttpHeaders = NULL; + } } void LLTextureFetchDebugger::init() @@ -3251,6 +3940,12 @@ void LLTextureFetchDebugger::init() mFreezeHistory = FALSE; mStopDebug = FALSE; mClearHistory = FALSE; + + if (! mHttpHeaders) + { + mHttpHeaders = new LLCore::HttpHeaders; + mHttpHeaders->mHeaders.push_back("Accept: image/x-j2c"); + } } void LLTextureFetchDebugger::startWork(e_debug_state state) @@ -3388,7 +4083,7 @@ void LLTextureFetchDebugger::tryToStopDebug() { mTextureCache->readComplete(mFetchingHistory[i].mCacheHandle, true); } - } + } break; case WRITE_CACHE: for(S32 i = 0 ; i < size; i++) @@ -3430,6 +4125,7 @@ void LLTextureFetchDebugger::tryToStopDebug() if(mClearHistory) { mFetchingHistory.clear(); + mHandleToFetchIndex.clear(); init(); mTotalFetchingTime = gDebugTimers[0].getElapsedTimeF32(); //reset } @@ -3608,6 +4304,7 @@ void LLTextureFetchDebugger::debugHTTP() mFetchingHistory[i].mCurlState = FetchEntry::CURL_NOT_DONE; mFetchingHistory[i].mCurlReceivedSize = 0; mFetchingHistory[i].mHTTPFailCount = 0; + mFetchingHistory[i].mFormattedImage = NULL; } mNbCurlRequests = 0; mNbCurlCompleted = 0; @@ -3622,15 +4319,12 @@ S32 LLTextureFetchDebugger::fillCurlQueue() mNbCurlCompleted = mFetchingHistory.size(); return 0; } - S32 size = mFetchingHistory.size(); - - if (mNbCurlRequests == size) //all issued + if (mNbCurlRequests > HTTP_REQUESTS_IN_QUEUE_LOW_WATER) { - return 0; - } + return mNbCurlRequests; + } - S32 counter = 8; - mNbCurlRequests = 0; + S32 size = mFetchingHistory.size(); for (S32 i = 0 ; i < size ; i++) { mNbCurlRequests++; @@ -3645,13 +4339,28 @@ S32 LLTextureFetchDebugger::fillCurlQueue() requestedSize = llmax(0,requestedSize); // We request the whole file if the size was set to an absurdly high value (meaning all file) requestedSize = (requestedSize == 33554432 ? 0 : requestedSize); - std::vector<std::string> headers; - headers.push_back("Accept: image/x-j2c"); - mCurlGetRequest->getByteRange(texture_url, headers, 0, requestedSize, 0x10000, new LLDebuggerHTTPResponder(this, i)); - - mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; - counter--; - if(counter < 1) + + LLCore::HttpHandle handle = mFetcher->getHttpRequest().requestGetByteRange(mHttpPolicyClass, + LLWorkerThread::PRIORITY_LOWBITS, + texture_url, + 0, + requestedSize, + NULL, + mHttpHeaders, + this); + if (LLCORE_HTTP_HANDLE_INVALID != handle) + { + mHandleToFetchIndex[handle] = i; + mFetchingHistory[i].mHttpHandle = handle; + mFetchingHistory[i].mCurlState = FetchEntry::CURL_IN_PROGRESS; + mNbCurlRequests++; + // Hack + if (mNbCurlRequests == HTTP_REQUESTS_IN_QUEUE_HIGH_WATER) // emulate normal pipeline + { + break; + } + } + else { break; } @@ -3881,9 +4590,8 @@ bool LLTextureFetchDebugger::update(F32 max_time) } break; case HTTP_FETCHING: - mCurlGetRequest->process(); - mCurlGetRequest->nextRequests(); - LLCurl::getCurlThread()->update(1); + // Do some notifications... + mFetcher->getHttpRequest().update(10); if (!fillCurlQueue() && mNbCurlCompleted == mFetchingHistory.size()) { mHTTPTime = mTimer.getElapsedTimeF32() ; @@ -3954,6 +4662,28 @@ bool LLTextureFetchDebugger::update(F32 max_time) return mState == IDLE; } +void LLTextureFetchDebugger::onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response) +{ + handle_fetch_map_t::iterator iter(mHandleToFetchIndex.find(handle)); + if (mHandleToFetchIndex.end() == iter) + { + llinfos << "Fetch Debugger : Couldn't find handle " << handle << " in fetch list." << llendl; + return; + } + + S32 fetch_ind(iter->second); + mHandleToFetchIndex.erase(iter); + if (fetch_ind >= mFetchingHistory.size() || mFetchingHistory[fetch_ind].mHttpHandle != handle) + { + llinfos << "Fetch Debugger : Handle and fetch object in disagreement. Punting." << llendl; + } + else + { + callbackHTTP(mFetchingHistory[fetch_ind], response); + mFetchingHistory[fetch_ind].mHttpHandle = LLCORE_HTTP_HANDLE_INVALID; // Not valid after notification + } +} + void LLTextureFetchDebugger::callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal) { @@ -3980,50 +4710,60 @@ void LLTextureFetchDebugger::callbackDecoded(S32 id, bool success, LLImageRaw* r } } -void LLTextureFetchDebugger::callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success) +void LLTextureFetchDebugger::callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response) { - if (success) + static const LLCore::HttpStatus par_status(HTTP_PARTIAL_CONTENT); + + LLCore::HttpStatus status(response->getStatus()); + mNbCurlRequests--; + if (status) { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + const bool partial(par_status == status); + LLCore::BufferArray * ba(response->getBody()); // *Not* holding reference to body + + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; - S32 data_size = buffer->countAfter(channels.in(), NULL); - mFetchingHistory[id].mCurlReceivedSize += data_size; - //llinfos << "Fetch Debugger : got results for " << id << ", data_size = " << data_size << ", received = " << mFetchingHistory[id].mCurlReceivedSize << ", requested = " << mFetchingHistory[id].mRequestedSize << ", partial = " << partial << llendl; - if ((mFetchingHistory[id].mCurlReceivedSize >= mFetchingHistory[id].mRequestedSize) || !partial || (mFetchingHistory[id].mRequestedSize == 600)) + S32 data_size = ba ? ba->size() : 0; + fetch.mCurlReceivedSize += data_size; + //llinfos << "Fetch Debugger : got results for " << fetch.mID << ", data_size = " << data_size << ", received = " << fetch.mCurlReceivedSize << ", requested = " << fetch.mRequestedSize << ", partial = " << partial << llendl; + if ((fetch.mCurlReceivedSize >= fetch.mRequestedSize) || !partial || (fetch.mRequestedSize == 600)) { U8* d_buffer = (U8*)ALLOCATE_MEM(LLImageBase::getPrivatePool(), data_size); - buffer->readAfter(channels.in(), NULL, d_buffer, data_size); + if (ba) + { + ba->read(0, d_buffer, data_size); + } - mFetchingHistory[id].mFormattedImage = NULL; + llassert_always(fetch.mFormattedImage.isNull()); { // For now, create formatted image based on extension - std::string texture_url = mHTTPUrl + "/?texture_id=" + mFetchingHistory[id].mID.asString().c_str(); + std::string texture_url = mHTTPUrl + "/?texture_id=" + fetch.mID.asString().c_str(); std::string extension = gDirUtilp->getExtension(texture_url); - mFetchingHistory[id].mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); - if (mFetchingHistory[id].mFormattedImage.isNull()) + fetch.mFormattedImage = LLImageFormatted::createFromType(LLImageBase::getCodecFromExtension(extension)); + if (fetch.mFormattedImage.isNull()) { - mFetchingHistory[id].mFormattedImage = new LLImageJ2C; // default + fetch.mFormattedImage = new LLImageJ2C; // default } } - mFetchingHistory[id].mFormattedImage->setData(d_buffer, data_size); + fetch.mFormattedImage->setData(d_buffer, data_size); } } else //failed { - mFetchingHistory[id].mHTTPFailCount++; - if(mFetchingHistory[id].mHTTPFailCount < 5) + llinfos << "Fetch Debugger : CURL GET FAILED, ID = " << fetch.mID + << ", status: " << status.toHex() + << " reason: " << status.toString() << llendl; + fetch.mHTTPFailCount++; + if(fetch.mHTTPFailCount < 5) { // Fetch will have to be redone - mFetchingHistory[id].mCurlState = FetchEntry::CURL_NOT_DONE; - mNbCurlRequests--; + fetch.mCurlState = FetchEntry::CURL_NOT_DONE; } else //skip { - mFetchingHistory[id].mCurlState = FetchEntry::CURL_DONE; + fetch.mCurlState = FetchEntry::CURL_DONE; mNbCurlCompleted++; } } diff --git a/indra/newview/lltexturefetch.h b/indra/newview/lltexturefetch.h index f5072a79f16dc9ef566bfd9608f0615b0fb5215a..3a99432b48e8bf31b769b789ee3cf4eaa52ed007 100644 --- a/indra/newview/lltexturefetch.h +++ b/indra/newview/lltexturefetch.h @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,19 +27,24 @@ #ifndef LL_LLTEXTUREFETCH_H #define LL_LLTEXTUREFETCH_H +#include <vector> +#include <map> + #include "lldir.h" #include "llimage.h" #include "lluuid.h" #include "llworkerthread.h" -#include "llcurl.h" #include "lltextureinfo.h" #include "llapr.h" #include "llimageworker.h" -//#include "lltexturecache.h" +#include "llcurl.h" +#include "httprequest.h" +#include "httpoptions.h" +#include "httpheaders.h" +#include "httphandler.h" class LLViewerTexture; class LLTextureFetchWorker; -class HTTPGetResponder; class LLImageDecodeThread; class LLHost; class LLViewerAssetStats; @@ -47,10 +52,10 @@ class LLTextureFetchDebugger; class LLTextureCache; // Interface class + class LLTextureFetch : public LLWorkerThread { friend class LLTextureFetchWorker; - friend class HTTPGetResponder; public: LLTextureFetch(LLTextureCache* cache, LLImageDecodeThread* imagedecodethread, bool threaded, bool qa_mode); @@ -58,70 +63,201 @@ public: class TFRequest; - /*virtual*/ S32 update(F32 max_time_ms); - void shutDownTextureCacheThread() ; //called in the main thread after the TextureCacheThread shuts down. - void shutDownImageDecodeThread() ; //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + /*virtual*/ S32 update(F32 max_time_ms); + + // called in the main thread after the TextureCacheThread shuts down. + // Threads: Tmain + void shutDownTextureCacheThread(); + + //called in the main thread after the ImageDecodeThread shuts down. + // Threads: Tmain + void shutDownImageDecodeThread(); + // Threads: T* (but Tmain mostly) bool createRequest(const std::string& url, const LLUUID& id, const LLHost& host, F32 priority, S32 w, S32 h, S32 c, S32 discard, bool needs_aux, bool can_use_http); + + // Requests that a fetch operation be deleted from the queue. + // If @cancel is true, also stops any I/O operations pending. + // Actual delete will be scheduled and performed later. + // + // Note: This *looks* like an override/variant of the + // base class's deleteRequest() but is functionally quite + // different. + // + // Threads: T* void deleteRequest(const LLUUID& id, bool cancel); + void deleteAllRequests(); + + // Threads: T* bool getRequestFinished(const LLUUID& id, S32& discard_level, LLPointer<LLImageRaw>& raw, LLPointer<LLImageRaw>& aux); + + // Threads: T* bool updateRequestPriority(const LLUUID& id, F32 priority); + // Threads: T* bool receiveImageHeader(const LLHost& host, const LLUUID& id, U8 codec, U16 packets, U32 totalbytes, U16 data_size, U8* data); + + // Threads: T* bool receiveImagePacket(const LLHost& host, const LLUUID& id, U16 packet_num, U16 data_size, U8* data); + // Threads: T* (but not safe) void setTextureBandwidth(F32 bandwidth) { mTextureBandwidth = bandwidth; } + + // Threads: T* (but not safe) F32 getTextureBandwidth() { return mTextureBandwidth; } - // Debug + // Threads: T* BOOL isFromLocalCache(const LLUUID& id); + + // @return Magic number giving the internal state of the + // request. We should make these codes public if we're + // going to return them as a status value. + // + // Threads: T* S32 getFetchState(const LLUUID& id, F32& decode_progress_p, F32& requested_priority_p, U32& fetch_priority_p, F32& fetch_dtime_p, F32& request_dtime_p, bool& can_use_http); + + // Debug utility - generally not safe void dump(); - S32 getNumRequests() ; + + // Threads: T* + S32 getNumRequests(); + + // Threads: T* + S32 getNumHTTPRequests(); + + // Threads: T* + U32 getTotalNumHTTPRequests(); - // Public for access by callbacks + // Threads: T* S32 getPending(); + + // Threads: T* void lockQueue() { mQueueMutex.lock(); } + + // Threads: T* void unlockQueue() { mQueueMutex.unlock(); } + + // Threads: T* LLTextureFetchWorker* getWorker(const LLUUID& id); + + // Threads: T* + // Locks: Mfq LLTextureFetchWorker* getWorkerAfterLock(const LLUUID& id); - LLTextureInfo* getTextureInfo() { return &mTextureInfo; } - // Commands available to other threads to control metrics gathering operations. + + // Threads: T* void commandSetRegion(U64 region_handle); + + // Threads: T* void commandSendMetrics(const std::string & caps_url, const LLUUID & session_id, const LLUUID & agent_id, LLViewerAssetStats * main_stats); + + // Threads: T* void commandDataBreak(); - LLCurlTextureRequest & getCurlRequest() { return *mCurlGetRequest; } + // Threads: T* + LLCore::HttpRequest & getHttpRequest() { return *mHttpRequest; } + + // Threads: T* + LLCore::HttpRequest::policy_t getPolicyClass() const { return mHttpPolicyClass; } + + // Return a pointer to the shared metrics headers definition. + // Does not increment the reference count, caller is required + // to do that to hold a reference for any length of time. + // + // Threads: T* + LLCore::HttpHeaders * getMetricsHeaders() const { return mHttpMetricsHeaders; } bool isQAMode() const { return mQAMode; } - // Curl POST counter maintenance - inline void incrCurlPOSTCount() { mCurlPOSTRequestCount++; } - inline void decrCurlPOSTCount() { mCurlPOSTRequestCount--; } + // ---------------------------------- + // HTTP resource waiting methods + + // Threads: T* + void addHttpWaiter(const LLUUID & tid); + + // Threads: T* + void removeHttpWaiter(const LLUUID & tid); + // Threads: T* + bool isHttpWaiter(const LLUUID & tid); + + // If there are slots, release one or more LLTextureFetchWorker + // requests from resource wait state (WAIT_HTTP_RESOURCE) to + // active (SEND_HTTP_REQ). + // + // Because this will modify state of many workers, you may not + // hold any Mw lock while calling. This makes it a little + // inconvenient to use but that's the rule. + // + // Threads: T* + // Locks: -Mw (must not hold any worker when called) + void releaseHttpWaiters(); + + // Threads: T* + void cancelHttpWaiters(); + + // Threads: T* + int getHttpWaitersCount(); + // ---------------------------------- + // Stats management + + // Add given counts to the global totals for the states/requests + // Threads: T* + void updateStateStats(U32 cache_read, U32 cache_write, U32 res_wait); + + // Return the global counts + // Threads: T* + void getStateStats(U32 * cache_read, U32 * cache_write, U32 * res_wait); + + // ---------------------------------- + protected: + // Threads: T* (but Ttf in practice) void addToNetworkQueue(LLTextureFetchWorker* worker); + + // Threads: T* void removeFromNetworkQueue(LLTextureFetchWorker* worker, bool cancel); + + // Threads: T* void addToHTTPQueue(const LLUUID& id); - void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // XXX possible delete + // Threads: T* + void removeFromHTTPQueue(const LLUUID& id, S32 received_size); + + // Identical to @deleteRequest but with different arguments + // (caller already has the worker pointer). + // + // Threads: T* + void removeRequest(LLTextureFetchWorker* worker, bool cancel); + // Overrides from the LLThread tree + // Locks: Ct bool runCondition(); private: + // Threads: Tmain void sendRequestListToSimulators(); + + // Threads: Ttf /*virtual*/ void startThread(void); + + // Threads: Ttf /*virtual*/ void endThread(void); + + // Threads: Ttf /*virtual*/ void threadedUpdate(void); + + // Threads: Ttf void commonUpdate(); // Metrics command helpers @@ -132,6 +268,8 @@ private: * Takes ownership of the TFRequest object. * * Method locks the command queue. + * + * Threads: T* */ void cmdEnqueue(TFRequest *); @@ -142,6 +280,8 @@ private: * Caller acquires ownership of the object and must dispose of it. * * Method locks the command queue. + * + * Threads: T* */ TFRequest * cmdDequeue(); @@ -151,6 +291,8 @@ private: * additional commands. * * Method locks the command queue. + * + * Threads: Ttf */ void cmdDoWork(); @@ -170,38 +312,64 @@ private: LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlTextureRequest* mCurlGetRequest; - + // Map of all requests by UUID typedef std::map<LLUUID,LLTextureFetchWorker*> map_t; - map_t mRequestMap; + map_t mRequestMap; // Mfq // Set of requests that require network data typedef std::set<LLUUID> queue_t; - queue_t mNetworkQueue; - queue_t mHTTPTextureQueue; + queue_t mNetworkQueue; // Mfnq + queue_t mHTTPTextureQueue; // Mfnq typedef std::map<LLHost,std::set<LLUUID> > cancel_queue_t; - cancel_queue_t mCancelQueue; - F32 mTextureBandwidth; - F32 mMaxBandwidth; + cancel_queue_t mCancelQueue; // Mfnq + F32 mTextureBandwidth; // <none> + F32 mMaxBandwidth; // Mfnq LLTextureInfo mTextureInfo; + // XXX possible delete + U32 mHTTPTextureBits; // Mfnq + + // XXX possible delete + //debug use + U32 mTotalHTTPRequests; + // Out-of-band cross-thread command queue. This command queue // is logically tied to LLQueuedThread's list of // QueuedRequest instances and so must be covered by the // same locks. typedef std::vector<TFRequest *> command_queue_t; - command_queue_t mCommands; + command_queue_t mCommands; // Mfq // If true, modifies some behaviors that help with QA tasks. const bool mQAMode; - // Count of POST requests outstanding. We maintain the count - // indirectly in the CURL request responder's ctor and dtor and - // use it when determining whether or not to sleep the thread. Can't - // use the LLCurl module's request counter as it isn't thread compatible. - // *NOTE: Don't mix Atomic and static, apr_initialize must be called first. - LLAtomic32<S32> mCurlPOSTRequestCount; + // Interfaces and objects into the core http library used + // to make our HTTP requests. These replace the various + // LLCurl interfaces used in the past. + LLCore::HttpRequest * mHttpRequest; // Ttf + LLCore::HttpOptions * mHttpOptions; // Ttf + LLCore::HttpHeaders * mHttpHeaders; // Ttf + LLCore::HttpHeaders * mHttpMetricsHeaders; // Ttf + LLCore::HttpRequest::policy_t mHttpPolicyClass; // T* + + // We use a resource semaphore to keep HTTP requests in + // WAIT_HTTP_RESOURCE2 if there aren't sufficient slots in the + // transport. This keeps them near where they can be cheaply + // reprioritized rather than dumping them all across a thread + // where it's more expensive to get at them. Requests in either + // SEND_HTTP_REQ or WAIT_HTTP_REQ charge against the semaphore + // and tracking state transitions is critical to liveness. + LLAtomicS32 mHttpSemaphore; // Ttf + Tmain + + typedef std::set<LLUUID> wait_http_res_queue_t; + wait_http_res_queue_t mHttpWaitResource; // Mfnq + + // Cumulative stats on the states/requests issued by + // textures running through here. + U32 mTotalCacheReadCount; // Mfq + U32 mTotalCacheWriteCount; // Mfq + U32 mTotalResourceWaitCount; // Mfq public: // A probabilistically-correct indicator that the current @@ -237,7 +405,7 @@ public: //debug use class LLViewerFetchedTexture; -class LLTextureFetchDebugger +class LLTextureFetchDebugger : public LLCore::HttpHandler { friend class LLTextureFetch; public: @@ -282,11 +450,13 @@ private: e_curl_state mCurlState; S32 mCurlReceivedSize; S32 mHTTPFailCount; + LLCore::HttpHandle mHttpHandle; FetchEntry() : mDecodedLevel(-1), mFetchedSize(0), - mDecodedSize(0) + mDecodedSize(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} FetchEntry(LLUUID& id, S32 r_size, /*S32 f_discard, S32 c,*/ S32 level, S32 f_size, S32 d_size) : mID(id), @@ -295,10 +465,15 @@ private: mFetchedSize(f_size), mDecodedSize(d_size), mNeedsAux(false), - mHTTPFailCount(0) + mHTTPFailCount(0), + mHttpHandle(LLCORE_HTTP_HANDLE_INVALID) {} }; - std::vector<FetchEntry> mFetchingHistory; + typedef std::vector<FetchEntry> fetch_list_t; + fetch_list_t mFetchingHistory; + + typedef std::map<LLCore::HttpHandle, S32> handle_fetch_map_t; + handle_fetch_map_t mHandleToFetchIndex; e_debug_state mState; @@ -319,7 +494,8 @@ private: LLTextureFetch* mFetcher; LLTextureCache* mTextureCache; LLImageDecodeThread* mImageDecodeThread; - LLCurlTextureRequest* mCurlGetRequest; + LLCore::HttpHeaders* mHttpHeaders; + LLCore::HttpRequest::policy_t mHttpPolicyClass; S32 mNumFetchedTextures; S32 mNumCacheHits; @@ -359,21 +535,18 @@ public: void clearHistory(); void addHistoryEntry(LLTextureFetchWorker* worker); - void setCurlGetRequest(LLCurlTextureRequest* request) { mCurlGetRequest = request;} - LLCurlTextureRequest* getCurlGetRequest() { return mCurlGetRequest;} + // Inherited from LLCore::HttpHandler + // Threads: Ttf + virtual void onCompleted(LLCore::HttpHandle handle, LLCore::HttpResponse * response); void startWork(e_debug_state state); void setStopDebug() {mStopDebug = TRUE;} void tryToStopDebug(); //stop everything - void callbackCacheRead(S32 id, bool success, LLImageFormatted* image, S32 imagesize, BOOL islocal); void callbackCacheWrite(S32 id, bool success); void callbackDecoded(S32 id, bool success, LLImageRaw* raw, LLImageRaw* aux); - void callbackHTTP(S32 id, const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer, - bool partial, bool success); - + void callbackHTTP(FetchEntry & fetch, LLCore::HttpResponse * response); e_debug_state getState() {return mState;} S32 getNumFetchedTextures() {return mNumFetchedTextures;} diff --git a/indra/newview/lltextureview.cpp b/indra/newview/lltextureview.cpp index c60b4155a0c44e0c6d7d3307eb3334a200c74e5e..16c42dbd4330458dd3cc31fd4962788ab690af1b 100755 --- a/indra/newview/lltextureview.cpp +++ b/indra/newview/lltextureview.cpp @@ -4,7 +4,7 @@ * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. + * Copyright (C) 2012, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -74,7 +74,7 @@ static std::string title_string4(" W x H (Dis) Mem"); static S32 title_x1 = 0; static S32 title_x2 = 460; static S32 title_x3 = title_x2 + 40; -static S32 title_x4 = title_x3 + 50; +static S32 title_x4 = title_x3 + 46; static S32 texture_bar_height = 8; //////////////////////////////////////////////////////////////////////////// @@ -232,6 +232,8 @@ void LLTextureBar::draw() { "DSK", LLColor4::blue }, // CACHE_POST { "NET", LLColor4::green }, // LOAD_FROM_NETWORK { "SIM", LLColor4::green }, // LOAD_FROM_SIMULATOR + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE + { "HTW", LLColor4::green }, // WAIT_HTTP_RESOURCE2 { "REQ", LLColor4::yellow },// SEND_HTTP_REQ { "HTP", LLColor4::green }, // WAIT_HTTP_REQ { "DEC", LLColor4::yellow },// DECODE_IMAGE @@ -239,7 +241,7 @@ void LLTextureBar::draw() { "WRT", LLColor4::purple },// WRITE_TO_CACHE { "WRT", LLColor4::orange },// WAIT_ON_WRITE { "END", LLColor4::red }, // DONE -#define LAST_STATE 12 +#define LAST_STATE 14 { "CRE", LLColor4::magenta }, // LAST_STATE+1 { "FUL", LLColor4::green }, // LAST_STATE+2 { "BAD", LLColor4::red }, // LAST_STATE+3 @@ -345,7 +347,7 @@ void LLTextureBar::draw() // draw the image size at the end { - std::string num_str = llformat("%3dx%3d (%d) %7d", mImagep->getWidth(), mImagep->getHeight(), + std::string num_str = llformat("%3dx%3d (%2d) %7d", mImagep->getWidth(), mImagep->getHeight(), mImagep->getDiscardLevel(), mImagep->hasGLTexture() ? mImagep->getTextureMemory() : 0); LLFontGL::getFontMonospace()->renderUTF8(num_str, 0, title_x4, getRect().getHeight(), color, LLFontGL::LEFT, LLFontGL::TOP); @@ -514,14 +516,18 @@ void LLGLTexMemBar::draw() S32 v_offset = 0;//(S32)((texture_bar_height + 2.2f) * mTextureView->mNumTextureBars + 2.0f); F32 total_texture_downloaded = (F32)gTotalTextureBytes / (1024 * 1024); F32 total_object_downloaded = (F32)gTotalObjectBytes / (1024 * 1024); - U32 total_http_requests = LLAppViewer::getTextureFetch()->getCurlRequest().getTotalIssuedRequests() ; + U32 total_http_requests = LLAppViewer::getTextureFetch()->getTotalNumHTTPRequests(); //---------------------------------------------------------------------------- LLGLSUIDefault gls_ui; LLColor4 text_color(1.f, 1.f, 1.f, 0.75f); LLColor4 color; - - std::string text = ""; + // Gray background using completely magic numbers + gGL.color4f(0.f, 0.f, 0.f, 0.25f); + // const LLRect & rect(getRect()); + // gl_rect_2d(-4, v_offset, rect.mRight - rect.mLeft + 2, v_offset + line_height*4); + + std::string text = ""; LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*6, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -531,14 +537,26 @@ void LLGLTexMemBar::draw() bound_mem, max_bound_mem, LLRenderTarget::sBytesAllocated/(1024*1024), - LLImageRaw::sGlobalRawMemory >> 20, discard_bias, - cache_usage, cache_max_usage); + LLImageRaw::sGlobalRawMemory >> 20, + discard_bias, + cache_usage, + cache_max_usage); + //, cache_entries, cache_max_entries + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*4, text_color, LLFontGL::LEFT, LLFontGL::TOP); - text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d", - total_texture_downloaded, total_object_downloaded, total_http_requests); - //, cache_entries, cache_max_entries + U32 cache_read(0U), cache_write(0U), res_wait(0U); + LLAppViewer::getTextureFetch()->getStateStats(&cache_read, &cache_write, &res_wait); + + text = llformat("Net Tot Tex: %.1f MB Tot Obj: %.1f MB Tot Htp: %d Cread: %u Cwrite: %u Rwait: %u", + total_texture_downloaded, + total_object_downloaded, + total_http_requests, + cache_read, + cache_write, + res_wait); + LLFontGL::getFontMonospace()->renderUTF8(text, 0, 0, v_offset + line_height*3, text_color, LLFontGL::LEFT, LLFontGL::TOP); @@ -552,7 +570,7 @@ void LLGLTexMemBar::draw() LLAppViewer::getTextureCache()->getNumReads(), LLAppViewer::getTextureCache()->getNumWrites(), LLLFSThread::sLocal->getPending(), LLImageRaw::sRawImageCount, - LLAppViewer::getTextureFetch()->getCurlRequest().getNumRequests(), + LLAppViewer::getTextureFetch()->getNumHTTPRequests(), LLAppViewer::getImageDecodeThread()->getPending(), gTextureList.mCreateTextureList.size()); diff --git a/indra/newview/llviewerassetstats.cpp b/indra/newview/llviewerassetstats.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerassetstats.h b/indra/newview/llviewerassetstats.h old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerjointmesh.h b/indra/newview/llviewerjointmesh.h old mode 100755 new mode 100644 diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index 2fe6cd578be4333af50d89f243c5b7965fad1cc7..f9342a9736d63769600a3c309e070107b80507e7 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -2871,21 +2871,6 @@ void LLViewerObject::updateInventory( { LLMemType mt(LLMemType::MTYPE_OBJECT); - std::list<LLUUID>::iterator begin = mPendingInventoryItemsIDs.begin(); - std::list<LLUUID>::iterator end = mPendingInventoryItemsIDs.end(); - - bool is_fetching = std::find(begin, end, item->getAssetUUID()) != end; - bool is_fetched = getInventoryItemByAsset(item->getAssetUUID()) != NULL; - - if (is_fetched || is_fetching) - { - return; - } - else - { - mPendingInventoryItemsIDs.push_back(item->getAssetUUID()); - } - // This slices the object into what we're concerned about on the // viewer. The simulator will take the permissions and transfer // ownership. diff --git a/indra/newview/llviewerstats.h b/indra/newview/llviewerstats.h old mode 100755 new mode 100644 diff --git a/indra/newview/llviewertexture.cpp b/indra/newview/llviewertexture.cpp index 8eb8717de2d49edc3947a4e2f4cec190a68aa04f..32cf8cc1b30fd178ec7a015f538ab30ba4087da5 100644 --- a/indra/newview/llviewertexture.cpp +++ b/indra/newview/llviewertexture.cpp @@ -1904,7 +1904,7 @@ void LLViewerFetchedTexture::updateVirtualSize() for(U32 i = 0 ; i < mNumFaces ; i++) { LLFace* facep = mFaceList[i] ; - if(facep->getDrawable()->isRecentlyVisible()) + if( facep && facep->getDrawable() && facep->getDrawable()->isRecentlyVisible()) { addTextureStats(facep->getVirtualSize()) ; setAdditionalDecodePriority(facep->getImportanceToCamera()) ; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 366b6004bee8420b316fd227b7512c532446d999..627238b0f52239540a3d7fca11b849c9139d0d7e 100755 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -4421,7 +4421,9 @@ U32 LLVOAvatar::renderTransparent(BOOL first_pass) } // Can't test for baked hair being defined, since that won't always be the case (not all viewers send baked hair) // TODO: 1.25 will be able to switch this logic back to calling isTextureVisible(); - if (getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) + + if ( getImage(TEX_HAIR_BAKED, 0) + && getImage(TEX_HAIR_BAKED, 0)->getID() != IMG_INVISIBLE || LLDrawPoolAlpha::sShowDebugAlpha) { num_indices += mMeshLOD[MESH_ID_HAIR]->render(mAdjustedPixelArea, first_pass, mIsDummy); first_pass = FALSE; @@ -4565,7 +4567,20 @@ void LLVOAvatar::updateTextures() LLWearableType::EType wearable_type = LLVOAvatarDictionary::getTEWearableType((ETextureIndex)texture_index); U32 num_wearables = gAgentWearables.getWearableCount(wearable_type); const LLTextureEntry *te = getTE(texture_index); - const F32 texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + + // getTE can return 0. + // Not sure yet why it does, but of course it crashes when te->mScale? gets used. + // Put safeguard in place so this corner case get better handling and does not result in a crash. + F32 texel_area_ratio = 1.0f; + if( te ) + { + texel_area_ratio = fabs(te->mScaleS * te->mScaleT); + } + else + { + llwarns << "getTE( " << texture_index << " ) returned 0" <<llendl; + } + LLViewerFetchedTexture *imagep = NULL; for (U32 wearable_index = 0; wearable_index < num_wearables; wearable_index++) { @@ -8705,6 +8720,12 @@ BOOL LLVOAvatar::isTextureDefined(LLVOAvatarDefines::ETextureIndex te, U32 index return FALSE; } + if( !getImage( te, index ) ) + { + llwarns << "getImage( " << te << ", " << index << " ) returned 0" << llendl; + return FALSE; + } + return (getImage(te, index)->getID() != IMG_DEFAULT_AVATAR && getImage(te, index)->getID() != IMG_DEFAULT); } diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h old mode 100755 new mode 100644 diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h old mode 100755 new mode 100644 diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index e99898a83c4a3dab4b62d0b7f7f5be785ac2c04a..5d1c3350785e80d5818283a00e69dc992aecc7fd 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -1227,6 +1227,13 @@ BOOL LLVOVolume::calcLOD() if (mDrawable->isState(LLDrawable::RIGGED)) { LLVOAvatar* avatar = getAvatar(); + + // Not sure how this can really happen, but alas it does. Better exit here than crashing. + if( !avatar || !avatar->mDrawable ) + { + return FALSE; + } + distance = avatar->mDrawable->mDistanceWRTCamera; radius = avatar->getBinRadius(); } @@ -1335,7 +1342,8 @@ BOOL LLVOVolume::setDrawableParent(LLDrawable* parentp) void LLVOVolume::updateFaceFlags() { - for (S32 i = 0; i < getVolume()->getNumFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; i < getVolume()->getNumFaces() && i < mDrawable->getNumFaces(); i++) { LLFace *face = mDrawable->getFace(i); if (face) @@ -1436,7 +1444,10 @@ BOOL LLVOVolume::genBBoxes(BOOL force_global) volume = getVolume(); } - for (S32 i = 0; i < getVolume()->getNumVolumeFaces(); i++) + // There's no guarantee that getVolume()->getNumFaces() == mDrawable->getNumFaces() + for (S32 i = 0; + i < getVolume()->getNumVolumeFaces() && i < mDrawable->getNumFaces() && i < getNumTEs(); + i++) { LLFace *face = mDrawable->getFace(i); if (!face) @@ -1737,6 +1748,11 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) void LLVOVolume::updateFaceSize(S32 idx) { + if( mDrawable->getNumFaces() <= idx ) + { + return; + } + LLFace* facep = mDrawable->getFace(idx); if (facep) { @@ -2427,7 +2443,12 @@ void LLVOVolume::addMediaImpl(LLViewerMediaImpl* media_impl, S32 texture_index) //add the face to show the media if it is in playing if(mDrawable) { - LLFace* facep = mDrawable->getFace(texture_index) ; + LLFace* facep(NULL); + if( texture_index < mDrawable->getNumFaces() ) + { + facep = mDrawable->getFace(texture_index) ; + } + if(facep) { LLViewerMediaTexture* media_tex = LLViewerTextureManager::findMediaTexture(mMediaImplList[texture_index]->getMediaTextureID()) ; @@ -3826,6 +3847,7 @@ void LLRiggedVolume::update(const LLMeshSkinInfo* skin, LLVOAvatar* avatar, cons LLVector4a* pos = dst_face.mPositions; + if( pos && weight && dst_face.mExtents ) { LLFastTimer t(FTM_SKIN_RIGGED); diff --git a/indra/newview/llwlhandlers.cpp b/indra/newview/llwlhandlers.cpp index 2425b96678d1d0d6cdccbdd96f12e104a897c6e6..be3e3ff30e0cb6c4ba8c260d72c82f571ebfb05e 100644 --- a/indra/newview/llwlhandlers.cpp +++ b/indra/newview/llwlhandlers.cpp @@ -105,10 +105,16 @@ LLEnvironmentRequestResponder::LLEnvironmentRequestResponder() return; } - if (unvalidated_content[0]["regionID"].asUUID() != gAgent.getRegion()->getRegionID()) + LLUUID regionId; + if( gAgent.getRegion() ) + { + regionId = gAgent.getRegion()->getRegionID(); + } + + if (unvalidated_content[0]["regionID"].asUUID() != regionId ) { LL_WARNS("WindlightCaps") << "Not in the region from where this data was received (wanting " - << gAgent.getRegion()->getRegionID() << " but got " << unvalidated_content[0]["regionID"].asUUID() + << regionId << " but got " << unvalidated_content[0]["regionID"].asUUID() << ") - ignoring..." << LL_ENDL; return; } diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 678898797f0ef7464e527c2a1298fed9c7457af6..91dd44131906119ec58dfc2301f689675691c921 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -2011,6 +2011,7 @@ void LLPipeline::grabReferences(LLCullResult& result) void LLPipeline::clearReferences() { sCull = NULL; + mGroupSaveQ1.clear(); } void check_references(LLSpatialGroup* group, LLDrawable* drawable) @@ -2619,6 +2620,7 @@ void LLPipeline::rebuildPriorityGroups() group->clearState(LLSpatialGroup::IN_BUILD_Q1); } + mGroupSaveQ1 = mGroupQ1; mGroupQ1.clear(); mGroupQ1Locked = false; diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index fd2a1e06cd47f094135fce9f89d1e56fa28f84bc..7a0ca86231f12ff6fe0528bc4f03036681b07d5f 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -668,6 +668,8 @@ protected: LLSpatialGroup::sg_vector_t mGroupQ1; //priority LLSpatialGroup::sg_vector_t mGroupQ2; // non-priority + LLSpatialGroup::sg_vector_t mGroupSaveQ1; // a place to save mGroupQ1 until it is safe to unref + LLSpatialGroup::sg_vector_t mMeshDirtyGroup; //groups that need rebuildMesh called U32 mMeshDirtyQueryObject; diff --git a/indra/newview/skins/default/xui/de/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/de/floater_pathfinding_linksets.xml index f1d561e51b645df2f45c658c317c65b8b06cf88b..0d3ba59efb4dbf8ddbb8f607e7c278c6c0db6d43 100644 --- a/indra/newview/skins/default/xui/de/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/de/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [Gruppe] </floater.string> + <floater.string name="linkset_is_scripted"> + Ja + </floater.string> + <floater.string name="linkset_is_not_scripted"> + Nein + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Unbekannt + </floater.string> <floater.string name="linkset_use_walkable"> Begehbar </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Name (Hauptprim)" name="name"/> <scroll_list.columns label="Beschreibung (Hauptprim)" name="description"/> <scroll_list.columns label="Eigentümer" name="owner"/> + <scroll_list.columns label="Geskriptet" name="scripted"/> <scroll_list.columns label="Belastung" name="land_impact"/> <scroll_list.columns label="Abstand" name="dist_from_you"/> <scroll_list.columns label="Linkset-Nutzung" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/de/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/de/floater_texture_fetch_debugger.xml index 0f407cb15c0505ab8cfd539f113c2f664c8cc176..97b03648322948bfe0e4048ff01bd4ce056a9c66 100644 --- a/indra/newview/skins/default/xui/de/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/de/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Neuabruf sichtbarer Texturen aus Cache, Zeit: [TIME] s, Abrufmenge: [SIZE2] KB, [PIXEL] MPixel </text> + <text name="total_time_refetch_all_cache_label"> + 16, Neuabruf aller Texturen aus Cache, Zeit: [TIME] s, Abrufmenge: [SIZE2] KB, [PIXEL] MPixel + </text> <text name="total_time_refetch_vis_http_label"> - 16, Neuabruf sichtbarer Texturen von HTTP, Zeit: [TIME] s, Abrufmenge: [SIZE2] KB, [PIXEL] MPixel + 17, Neuabruf sichtbarer Texturen von HTTP, Zeit: [TIME] s, Abrufmenge: [SIZE2] KB, [PIXEL] MPixel + </text> + <text name="total_time_refetch_all_http_label"> + 18, Neuabruf aller Texturen von HTTP, Zeit: [TIME] s, Abrufmenge: [SIZE2] KB, [PIXEL] MPixel + </text> + <spinner label="19, Verhältnis Texel/Pixel:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, Texturquelle: </text> - <spinner label="17, Verhältnis Texel/Pixel:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Cache + HTTP" name="0"/> + <radio_item label="Nur HTTP" name="1"/> + </radio_group> <button label="Starten" name="start_btn"/> <button label="Zurücksetzen" name="clear_btn"/> <button label="Schließen" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Decodieren" name="decode_btn"/> <button label="GL-Textur" name="gl_btn"/> <button label="Neuabruf sichtbarer Texturen (Cache)" name="refetchviscache_btn"/> + <button label="Neuabruf des gesamten Cache" name="refetchallcache_btn"/> <button label="Neuabruf sichtbarer Texturen (HTTP)" name="refetchvishttp_btn"/> + <button label="Neuabruf des gesamten HTTP" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/de/menu_inventory.xml b/indra/newview/skins/default/xui/de/menu_inventory.xml index 39b3099336ab2279fc1e0d9de0317f90283e7aec..cd2fca313e5424b9e6f822eff6d0e60585e7144d 100644 --- a/indra/newview/skins/default/xui/de/menu_inventory.xml +++ b/indra/newview/skins/default/xui/de/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Systemordner löschen" name="Delete System Folder"/> <menu_item_call label="Konferenz-Chat starten" name="Conference Chat Folder"/> <menu_item_call label="Wiedergeben/Abspielen" name="Sound Play"/> + <menu_item_call label="SLurl kopieren" name="url_copy"/> <menu_item_call label="Landmarken-Info" name="About Landmark"/> <menu_item_call label="Inworld abspielen" name="Animation Play"/> <menu_item_call label="Lokal abspielen" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index 33dfcdcc8430953b94d166b90f1b825ce7ae7f16..4b7a60b4eb74601204601f9fa6ed7f75f0dbb638 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -1221,7 +1221,7 @@ in „[THIS_GPU]“ Sie wurden zur nächstgelegenen Region teleportiert. </notification> <notification name="AvatarMovedLast"> - Ihr letzter Standort ist zurzeit nicht verfügbar. + Ihr angeforderter Standort ist zurzeit nicht verfügbar. Sie wurden zur nächstgelegenen Region teleportiert. </notification> <notification name="AvatarMovedHome"> @@ -1240,7 +1240,7 @@ Sie können [SECOND_LIFE] normal verwenden. Andere Benutzer können Sie korrekt Installation von [APP_NAME] vollständig abgeschlossen. Falls Sie [SECOND_LIFE] zum ersten Mal verwenden, müssen Sie zuerst ein Konto erstellen, bevor Sie sich anmelden können. - <usetemplate name="okcancelbuttons" notext="Weiter" yestext="Neues Konto..."/> + <usetemplate name="okcancelbuttons" notext="Weiter" yestext="Konto erstellen..."/> </notification> <notification name="LoginPacketNeverReceived"> Es gibt Probleme mit der Verbindung. Möglicherweise besteht ein Problem mit Ihrer Internetverbindung oder dem [SECOND_LIFE_GRID]. @@ -3246,19 +3246,58 @@ Durch Ausblenden der Schaltfläche „Sprechen“ wird die Sprechfunktion deakti Durch diese Aktion werden alle Menüelemente und Schaltflächen ausgeblendet. Um sie wieder anzuzeigen, klicken Sie erneut auf [SHORTCUT]. <usetemplate ignoretext="Vor Ausblenden der UI bestätigen" name="okcancelignore" notext="Abbrechen" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> - Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. + <notification name="PathfindingLinksets_WarnOnPhantom"> + Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet. + +Möchten Sie fortfahren? + <usetemplate ignoretext="Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet." name="okcancelignore" notext="Abbrechen" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> + Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. + +Möchten Sie fortfahren? <usetemplate ignoretext="Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht gesetzt werden." name="okcancelignore" notext="Abbrechen" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Einige der ausgewählten Linksets können nicht auf „[REQUESTED_TYPE]“ gesetzt werden, da die Form nicht konvex ist. + +Möchten Sie fortfahren? <usetemplate ignoretext="Einige der ausgewählten Linksets können nicht gesetzt werden, da die Form nicht konvex ist." name="okcancelignore" notext="Abbrechen" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> - Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. - Einige der ausgewählten Linksets können nicht auf „[REQUESTED_TYPE]“ gesetzt werden, da die Form nicht konvex ist. Die Nutzungsarten dieser Linksets bleiben unverändert. + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet. + +Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. + +Möchten Sie fortfahren? + <usetemplate ignoretext="Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet und andere können aufgrund von Berechtigungseinschränkungen nicht gesetzt werden." name="okcancelignore" notext="Abbrechen" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet. + +Einige der ausgewählten Linksets können nicht auf „[REQUESTED_TYPE]“ gesetzt werden, da die Form nicht konvex ist. + +Möchten Sie fortfahren? + <usetemplate ignoretext="Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet und andere können nicht gesetzt werden, da die Form nicht konvex ist." name="okcancelignore" notext="Abbrechen" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> + Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. + +Einige der ausgewählten Linksets können nicht auf „[REQUESTED_TYPE]“ gesetzt werden, da die Form nicht konvex ist. Die Nutzungsarten dieser Linksets bleiben unverändert. + +Möchten Sie fortfahren? <usetemplate ignoretext="Einige der ausgewählten Linksets können nicht gesetzt werden, da die Berechtigungen eingeschränkt sind und die Form nicht konvex ist." name="okcancelignore" notext="Abbrechen" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet. + +Einige der ausgewählten Linksets können aufgrund von Berechtigungseinschränkungen nicht auf „[REQUESTED_TYPE]“ gesetzt werden. Diese Linksets werden stattdessen auf „[RESTRICTED_TYPE]“ gesetzt. + +Einige der ausgewählten Linksets können nicht auf „[REQUESTED_TYPE]“ gesetzt werden, da die Form nicht konvex ist. Die Nutzungsarten dieser Linksets bleiben unverändert. + +Möchten Sie fortfahren? + <usetemplate ignoretext="Bei einigen ausgewählten Linksets wird die Phantom-Markierung umgeschaltet und andere können nicht gesetzt werden, da die Berechtigungen für das Linkset eingeschränkt sind und die Form nicht konvex ist." name="okcancelignore" notext="Abbrechen" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> Das ausgewählte Objekt wirkt sich auf das Navmesh aus. Wenn Sie es in einen flexiblen Pfad ändern, wird es aus dem Navmesh entfernt. <usetemplate ignoretext="Das ausgewählte Objekt wirkt sich auf das Navmesh aus. Wenn Sie es in einen flexiblen Pfad ändern, wird es aus dem Navmesh entfernt." name="okcancelignore" notext="Abbrechen" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/de/panel_login.xml b/indra/newview/skins/default/xui/de/panel_login.xml index 2203b6d310b943167ff6a6fc43cc93c8a1059da7..8cc467185c064defdede897ec4cc7a78783a18e4 100644 --- a/indra/newview/skins/default/xui/de/panel_login.xml +++ b/indra/newview/skins/default/xui/de/panel_login.xml @@ -18,7 +18,7 @@ </layout_panel> <layout_panel name="start_location_panel"> <text name="start_location_text"> - Hier anfangen: + Hier starten: </text> <combo_box name="start_location_combo"> <combo_box.item label="Mein letzter Standort" name="MyLastLocation"/> @@ -28,19 +28,19 @@ </layout_panel> <layout_panel name="links_login_panel"> <text name="login_help"> - Sie brauchen Hilfe? + Brauchen Sie Hilfe beim Anmelden? </text> <text name="forgot_password_text"> Benutzernamen oder Kennwort vergessen? </text> <button label="Anmelden" name="connect_btn"/> - <check_box label="Kennwort merken" name="remember_check"/> + <check_box label="Kennwort speichern" name="remember_check"/> </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + IHR KONTO ERSTELLEN </text> - <button name="create_new_account_btn" label="Registrieren"/> + <button label="Jetzt starten" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/de/panel_region_debug.xml b/indra/newview/skins/default/xui/de/panel_region_debug.xml index 4a85bd85fd49e15d6e0b3daf2ee917c7bcfaec64..a03a0b8b7b49dddde630621019d3d7c5dbc01db7 100644 --- a/indra/newview/skins/default/xui/de/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/de/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" name="top_scripts_help"/> <button label="Region neu starten" name="restart_btn" tool_tip="2-Minuten-Countdown und Region neu starten"/> <button label="?" name="restart_help"/> - <button label="Neustart abbrechen" name="cancel_restart_btn" tool_tip="Regionsneustart um eine Stunde verschieben"/> + <button label="Neustart abbrechen" name="cancel_restart_btn" tool_tip="Regionsneustart abbrechen"/> </panel> diff --git a/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml index e6ab9165d7554e71d6f203513e3a6cd2e63e6567..3d43200e48f5af27bd33542459f286bc36e3d58c 100644 --- a/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml +++ b/indra/newview/skins/default/xui/de/panel_volume_pulldown.xml @@ -1,15 +1,15 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="volumepulldown_floater" width="240"> <button left="217" name="prefs_btn"/> - <slider label="Master" name="System Volume" label_width="80" width="180"/> - <slider label="Schaltflächen" label_width="80" width="180" name="UI Volume"/> - <slider label="Umgebung" label_width="80" width="180" name="Wind Volume"/> - <slider label="Soundeffekte" label_width="80" width="180" name="SFX Volume"/> - <check_box name="gesture_audio_play_btn" tool_tip="Sounds von Gesten abspielen"/> - <slider label="Musikstream" label_width="80" width="180" name="Music Volume"/> - <check_box tool_tip="Musikstream aktivieren" name="enable_music"/> - <slider label="Medien" label_width="80" width="180" name="Media Volume"/> - <check_box tool_tip="Medienstream aktivieren" name="enable_media"/> - <slider label="Voice-Chat" label_width="80" width="180" name="Voice Volume"/> - <check_box tool_tip="Voice-Chat aktivieren" name="enable_voice_check"/> + <slider label="Master" label_width="80" name="System Volume" width="180"/> + <slider label="Schaltflächen" label_width="80" name="UI Volume" width="180"/> + <slider label="Umgebung" label_width="80" name="Wind Volume" width="180"/> + <slider label="Sounds" label_width="80" name="SFX Volume" width="180"/> + <check_box name="gesture_audio_play_btn" tool_tip="Sounds von Gesten aktivieren"/> + <slider label="Musik" label_width="80" name="Music Volume" width="180"/> + <check_box name="enable_music" tool_tip="Streaming-Musik aktivieren"/> + <slider label="Medien" label_width="80" name="Media Volume" width="180"/> + <check_box name="enable_media" tool_tip="Streaming-Medien aktivieren"/> + <slider label="Voice" label_width="80" name="Voice Volume" width="180"/> + <check_box name="enable_voice_check" tool_tip="Voice-Chat aktivieren"/> </panel> diff --git a/indra/newview/skins/default/xui/de/strings.xml b/indra/newview/skins/default/xui/de/strings.xml index 206b63187484adf345da9f6541165c093b01a77c..aeed9f3796c0265ae6bfee0cca44c99ecbb81b98 100644 --- a/indra/newview/skins/default/xui/de/strings.xml +++ b/indra/newview/skins/default/xui/de/strings.xml @@ -1414,6 +1414,12 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. <string name="InvFolder favorite"> Meine Favoriten </string> + <string name="InvFolder Favorites"> + Meine Favoriten + </string> + <string name="InvFolder favorites"> + Meine Favoriten + </string> <string name="InvFolder Current Outfit"> Aktuelles Outfit </string> @@ -1429,6 +1435,12 @@ Warten Sie kurz und versuchen Sie dann noch einmal, sich anzumelden. <string name="InvFolder Meshes"> Netze </string> + <string name="InvFolder Received Items"> + Erhaltene Artikel + </string> + <string name="InvFolder Merchant Outbox"> + Händler-Outbox + </string> <string name="InvFolder Friends"> Freunde </string> diff --git a/indra/newview/skins/default/xui/es/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/es/floater_pathfinding_linksets.xml index 266e8138c982ca92c23904848bf391ef78a50819..e6f864eef5409401e03d0e74fb9be7cd4547893d 100644 --- a/indra/newview/skins/default/xui/es/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/es/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [grupo] </floater.string> + <floater.string name="linkset_is_scripted"> + Sà + </floater.string> + <floater.string name="linkset_is_not_scripted"> + No + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Desconocido + </floater.string> <floater.string name="linkset_use_walkable"> Objeto transitable </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Nombre (prim raÃz)" name="name"/> <scroll_list.columns label="Descripción (prim raÃz)" name="description"/> <scroll_list.columns label="Propietario" name="owner"/> + <scroll_list.columns label="Con scripts" name="scripted"/> <scroll_list.columns label="Impacto" name="land_impact"/> <scroll_list.columns label="Distancia" name="dist_from_you"/> <scroll_list.columns label="Utilización de linkset" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/es/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/es/floater_texture_fetch_debugger.xml index 29fd2ab2a32a9fa498ea398d5a3e4e1844c173a1..59aaf7f74a327acf94ef24c55b1eaa1419cc62d0 100644 --- a/indra/newview/skins/default/xui/es/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/es/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Volviendo a obtener visibles de la caché, Tiempo: [TIME] segundos, Obtenidos: [SIZE] KB, [PIXEL] MPÃxeles </text> + <text name="total_time_refetch_all_cache_label"> + 16, Volviendo a obtener todas las texturas de caché, Tiempo: [TIME] segundos, Obtenidos: [SIZE] KB, [PIXEL] MPÃxeles + </text> <text name="total_time_refetch_vis_http_label"> - 16, Volviendo a obtener visibles de HTTP, Tiempo: [TIME] segundos, Obtenidos: [SIZE] KB, [PIXEL] MPÃxeles + 17, Volviendo a obtener visibles de HTTP, Tiempo: [TIME] segundos, Obtenidos: [SIZE] KB, [PIXEL] MPÃxeles + </text> + <text name="total_time_refetch_all_http_label"> + 18, Volviendo a obtener todas las texturas de HTTP, Tiempo: [TIME] segundos, Obtenidos: [SIZE] KB, [PIXEL] MPÃxeles + </text> + <spinner label="19, Proporción de texeles/pÃxeles:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, Fuente de texturas: </text> - <spinner label="17, Proporción de texeles/pÃxeles:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Caché + HTTP" name="0"/> + <radio_item label="Solo HTTP" name="1"/> + </radio_group> <button label="Iniciar" name="start_btn"/> <button label="Restablecer" name="clear_btn"/> <button label="Cerrar" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Descodificar" name="decode_btn"/> <button label="Textura GL" name="gl_btn"/> <button label="Volver a obtener caché de vis." name="refetchviscache_btn"/> + <button label="Volver a obtener toda la caché" name="refetchallcache_btn"/> <button label="Volver a obtener HTTP de vis." name="refetchvishttp_btn"/> + <button label="Volver a obtener todo el HTTP" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/es/menu_inventory.xml b/indra/newview/skins/default/xui/es/menu_inventory.xml index 4a8f37dee4d825ed54b5d3c607e0e3bbbdf2b414..803d3f1331a1ff9f7188d41255c77b2cb7a4ccdf 100644 --- a/indra/newview/skins/default/xui/es/menu_inventory.xml +++ b/indra/newview/skins/default/xui/es/menu_inventory.xml @@ -67,6 +67,7 @@ <menu_item_call label="Borrar carpeta del sistema" name="Delete System Folder"/> <menu_item_call label="Empezar multiconferencia" name="Conference Chat Folder"/> <menu_item_call label="Escuchar" name="Sound Play"/> + <menu_item_call label="Copiar la SLurl" name="url_copy"/> <menu_item_call label="Acerca del hito" name="About Landmark"/> <menu_item_call label="Escuchar en el mundo" name="Animation Play"/> <menu_item_call label="Ejecutarla para usted" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index 991a079be5265dc5ee40e59704bbf5533431a94e..7dfb27717d30b721a4a22e62cfa80377d5ff0a2e 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -1211,7 +1211,7 @@ a '[THIS_GPU]' Se te ha llevado a una región cercana. </notification> <notification name="AvatarMovedLast"> - En estos momentos no está disponible tu última posición. + En estos momentos no está disponible la posición solicitada. Se te ha llevado a una región cercana. </notification> <notification name="AvatarMovedHome"> @@ -1229,8 +1229,8 @@ Puedes usar [SECOND_LIFE] de forma normal; los demás residentes te verán corre <notification name="FirstRun"> Se ha completado la instalación de [SECOND_LIFE]. -Si es la primera vez que usas [SECOND_LIFE], debes crear una cuenta antes de poder iniciar una sesión. - <usetemplate name="okcancelbuttons" notext="Continuar" yestext="Cuenta nueva..."/> +Si es la primera vez que usas [SECOND_LIFE], debes crear una cuenta para poder iniciar una sesión. + <usetemplate name="okcancelbuttons" notext="Continuar" yestext="Crear cuenta..."/> </notification> <notification name="LoginPacketNeverReceived"> Tenemos problemas de conexión. Puede deberse a un problema de tu conexión a Internet o de [SECOND_LIFE_GRID]. @@ -3231,19 +3231,58 @@ Al ocultar el botón Hablar se desactiva la función de voz. Esta acción ocultará todos los botones y elementos de menú. Para restaurarlos, pulsa otra vez en [SHORTCUT]. <usetemplate ignoretext="Confirmar antes de ocultar la IU" name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> - Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. + <notification name="PathfindingLinksets_WarnOnPhantom"> + El indicador de inmaterial de algunos linksets seleccionados se conmutará. + +¿Quieres continuar? + <usetemplate ignoretext="El indicador de inmaterial de algunos linksets seleccionados se conmutará." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> + Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. + +¿Quieres continuar? <usetemplate ignoretext="Algunos de los linksets seleccionados no pueden configurarse debido a restricciones de los permisos del linkset." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' porque su forma no es convexa. + +¿Quieres continuar? <usetemplate ignoretext="Algunos de los linksets seleccionados no pueden configurarse porque su forma no es convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> - Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. - Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' porque su forma no es convexa. Los tipos de utilización de estos linksets no cambiarán. + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + El indicador de inmaterial de algunos linksets seleccionados se conmutará. + +Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. + +¿Quieres continuar? + <usetemplate ignoretext="El indicador de inmaterial de algunos linksets seleccionados se conmutará y otros no se podrán establecer debido a restricciones de los permisos del linkset." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + El indicador de inmaterial de algunos linksets seleccionados se conmutará. + +Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' porque su forma no es convexa. + +¿Quieres continuar? + <usetemplate ignoretext="El indicador de inmaterial de algunos linksets seleccionados se conmutará y otros no se podrán establecer porque la forma no es convexa" name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> + Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. + +Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' porque su forma no es convexa. Los tipos de utilización de estos linksets no cambiarán. + +¿Quieres continuar? <usetemplate ignoretext="Algunos de los linksets seleccionados no pueden configurarse debido a restricciones de los permisos del linkset y porque su forma no es convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + El indicador de inmaterial de algunos linksets seleccionados se conmutará. + +Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' debido a restricciones de los permisos del linkset. Estos linksets se configurarán como '[RESTRICTED_TYPE]'. + +Algunos de los linksets seleccionados no pueden configurarse como '[REQUESTED_TYPE]' porque su forma no es convexa. Los tipos de utilización de estos linksets no cambiarán. + +¿Quieres continuar? + <usetemplate ignoretext="El indicador de inmaterial de algunos linksets seleccionados se conmutará y otros no se podrán establecer debido a restricciones de los permisos del linkset y porque su forma no es convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> El objeto que has seleccionado afecta al navmesh. Al cambiarlo a una Ruta flexible se eliminará del navmesh. <usetemplate ignoretext="El objeto que has seleccionado afecta al navmesh. Al cambiarlo a una Ruta flexible se eliminará del navmesh." name="okcancelignore" notext="Cancelar" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/es/panel_login.xml b/indra/newview/skins/default/xui/es/panel_login.xml index 683e0a096a3e6adcb20272d12ebd70f382669d51..1d7f077fe792facacdac6b6a9774c118a27f8b59 100644 --- a/indra/newview/skins/default/xui/es/panel_login.xml +++ b/indra/newview/skins/default/xui/es/panel_login.xml @@ -23,7 +23,7 @@ <combo_box name="start_location_combo"> <combo_box.item label="Mi última posición" name="MyLastLocation"/> <combo_box.item label="Mi Base" name="MyHome"/> - <combo_box.item label="<Escribe en qué región>" name="Typeregionname"/> + <combo_box.item label="<Escribe el nombre de la región>" name="Typeregionname"/> </combo_box> </layout_panel> <layout_panel name="links_login_panel"> @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + CREA TU CUENTA </text> - <button name="create_new_account_btn" label="Registrarme"/> + <button label="Iniciar ahora" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/es/panel_region_debug.xml b/indra/newview/skins/default/xui/es/panel_region_debug.xml index f07f3d39519fe0f66cd61c0e75233416f5ccf20b..71bdba1a25f8ff2d420e88004e6093bf6953a6d7 100644 --- a/indra/newview/skins/default/xui/es/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/es/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" left="297" name="top_scripts_help"/> <button label="Reiniciar la región" name="restart_btn" tool_tip="Cuenta atrás de 2 minutos y reiniciar la región"/> <button label="?" name="restart_help"/> - <button label="Cancelar reinicio" name="cancel_restart_btn" tool_tip="Retrasar una hora el reinicio de la región"/> + <button label="Cancelar reinicio" name="cancel_restart_btn" tool_tip="Cancelar el reinicio de región"/> </panel> diff --git a/indra/newview/skins/default/xui/es/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/es/panel_volume_pulldown.xml index 9193da6cde55597218a607c56c4da4dbeb574bd3..426783aa8e0bdbecc146329a317c7ca7b802e0a7 100644 --- a/indra/newview/skins/default/xui/es/panel_volume_pulldown.xml +++ b/indra/newview/skins/default/xui/es/panel_volume_pulldown.xml @@ -1,14 +1,14 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="volumepulldown_floater" width="215"> - <slider label="Maestro" name="System Volume" label_width="55" width="155"/> - <slider label="Botones" name="UI Volume" label_width="55" width="155"/> - <slider label="Ambiente" name="Wind Volume" label_width="55" width="155"/> - <slider label="Sonidos" name="SFX Volume" label_width="55" width="155"/> - <check_box name="gesture_audio_play_btn" tool_tip="Activa el sonido de los gestos"/> - <slider label="Música" name="Music Volume" label_width="55" width="155"/> - <check_box tool_tip="Activa el flujo de audio" name="enable_music"/> - <slider label="Medios" name="Media Volume" label_width="55" width="155"/> - <check_box tool_tip="Activa el flujo de medios" name="enable_media"/> - <slider label="Voz" name="Voice Volume" label_width="55" width="155"/> - <check_box tool_tip="Activar chat de voz" name="enable_voice_check"/> + <slider label="General" label_width="55" name="System Volume" width="155"/> + <slider label="Botones" label_width="55" name="UI Volume" width="155"/> + <slider label="Ambiental" label_width="55" name="Wind Volume" width="155"/> + <slider label="Sonidos" label_width="55" name="SFX Volume" width="155"/> + <check_box name="gesture_audio_play_btn" tool_tip="Activar sonidos de los gestos"/> + <slider label="Música" label_width="55" name="Music Volume" width="155"/> + <check_box name="enable_music" tool_tip="Activar música en streaming"/> + <slider label="Media" label_width="55" name="Media Volume" width="155"/> + <check_box name="enable_media" tool_tip="Activar media en streaming"/> + <slider label="Voz" label_width="55" name="Voice Volume" width="155"/> + <check_box name="enable_voice_check" tool_tip="Activar el chat de voz"/> </panel> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index d4698b8cb3c78c73f823693609123b5a5f00c48c..dcde68a93aa75a044a9c4594cbd73610e6a5b07c 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -1390,6 +1390,12 @@ Intenta iniciar sesión de nuevo en unos instantes. <string name="InvFolder favorite"> Mis Favoritos </string> + <string name="InvFolder Favorites"> + Mis Favoritos + </string> + <string name="InvFolder favorites"> + Mis Favoritos + </string> <string name="InvFolder Current Outfit"> Vestuario actual </string> @@ -1405,6 +1411,12 @@ Intenta iniciar sesión de nuevo en unos instantes. <string name="InvFolder Meshes"> Redes </string> + <string name="InvFolder Received Items"> + Objetos recibidos + </string> + <string name="InvFolder Merchant Outbox"> + Buzón de salida de comerciante + </string> <string name="InvFolder Friends"> Amigos </string> diff --git a/indra/newview/skins/default/xui/fr/floater_about_land.xml b/indra/newview/skins/default/xui/fr/floater_about_land.xml index 26844d98499f2f475d7271dcdcb25e341e84cde9..25c49b97b55804f6e4c5f2b645957515224f36be 100644 --- a/indra/newview/skins/default/xui/fr/floater_about_land.xml +++ b/indra/newview/skins/default/xui/fr/floater_about_land.xml @@ -339,7 +339,7 @@ Seules les parcelles de grande taille peuvent apparaître dans la recherche. <check_box label="Groupe" name="check group scripts"/> <check_box label="Sécurisé (pas de dégâts)" name="check safe" tool_tip="Si cette option est cochée, le terrain est sécurisé et il n'y pas de risques de dommages causés par des combats. Si elle est décochée, des dommages causés par les combats peuvent avoir lieu."/> <check_box label="Pas de bousculades" name="PushRestrictCheck" tool_tip="Empêche l'utilisation de scripts causant des bousculades. Cette option est utile pour empêcher les comportements abusifs sur votre terrain."/> - <check_box label="Afficher le lieu dans la recherche (30 L$/semaine)" name="ShowDirectoryCheck" tool_tip="Afficher la parcelle dans les résultats de recherche"/> + <check_box label="Voir le lieu dans la recherche (30 L$/sem.)" name="ShowDirectoryCheck" tool_tip="Afficher la parcelle dans les résultats de recherche"/> <combo_box name="land category with adult"> <combo_box.item label="Toutes catégories" name="item0"/> <combo_box.item label="Appartenant aux Lindens" name="item1"/> diff --git a/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml b/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml index de1ba220a01138aa45ab5e630e5af17883794ce1..5ec68458e1e6d60f3b0fdbcb29ce96124db34b8b 100644 --- a/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml +++ b/indra/newview/skins/default/xui/fr/floater_edit_day_cycle.xml @@ -22,13 +22,13 @@ Remarque : si vous changez votre préréglage de nom, un nouveau préréglage sera créé et celui existant restera tel quel. </text> <text name="hint_item1"> - - Modifier un réglage de ciel et d'heure : clic sur le repère + - Modifier un réglage de ciel/heure : clic sur le repère </text> <text name="hint_item2"> - - Définir les heures de transition : clic et glissement des repères + - Heures de transition : clic-glissement des repères </text> <text name="hint_item3"> - - Afficher un aperçu du cycle du jour : déplacer le triangle + - Aperçu du cycle du jour : déplacement du triangle </text> <panel name="day_cycle_slider_panel"> <multi_slider initial_value="0" name="WLTimeSlider"/> diff --git a/indra/newview/skins/default/xui/fr/floater_model_preview.xml b/indra/newview/skins/default/xui/fr/floater_model_preview.xml index 0f717cb2cd9846fe61f39dd317812e1600941cad..bd3dae65995e850ccd88ad0df2e8eee5ba3b0810 100644 --- a/indra/newview/skins/default/xui/fr/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/fr/floater_model_preview.xml @@ -201,7 +201,7 @@ Passes : </text> <text name="Detail Scale label"> - Échelle de détail : + Échelle détail : </text> <text name="Retain%_label"> Retenue : @@ -227,7 +227,7 @@ </panel> <panel label="Option de chargement" name="modifiers_panel"> <text name="scale_label"> - Echelle (1 = pas d'échelle) : + Échelle (1 = aucune) : </text> <spinner name="import_scale" value="1.0"/> <text name="dimensions_label"> @@ -238,12 +238,12 @@ </text> <check_box label="Inclure les textures" name="upload_textures"/> <text name="include_label"> - Pour les modèles d'avatar uniquement : + Modèles d'avatar uniquement : </text> - <check_box label="Inclure la pondération de la peau :" name="upload_skin"/> - <check_box label="Inclure la position des articulations :" name="upload_joints"/> + <check_box label="Inclure pondération de la peau :" name="upload_skin"/> + <check_box label="Inclure position des articulations :" name="upload_joints"/> <text name="pelvis_offset_label"> - Décalage Z (élever ou abaisser l'avatar) : + Décalage Z (élever/abaisser l'avatar) : </text> <spinner name="pelvis_offset" value="0.0"/> </panel> @@ -252,7 +252,7 @@ <button label="Calculer les poids et les frais" name="calculate_btn" tool_tip="Calculer les poids et les frais."/> <button label="Annuler" name="cancel_btn"/> <button label="Charger le modèle" name="ok_btn" tool_tip="Charger dans le simulateur"/> - <button label="Effacer les paramètres et réinitialiser le formulaire" name="reset_btn"/> + <button label="Effacer les paramètres / réinitialiser le formulaire" name="reset_btn"/> <text name="upload_fee"> Frais de chargement : [FEE] L$ </text> diff --git a/indra/newview/skins/default/xui/fr/floater_pathfinding_console.xml b/indra/newview/skins/default/xui/fr/floater_pathfinding_console.xml index 6d85f8035d87817434bda2686d1f9d2ce7919e52..02d969dc0896734ede6fabdf4e77e81cfbfd1a7d 100644 --- a/indra/newview/skins/default/xui/fr/floater_pathfinding_console.xml +++ b/indra/newview/skins/default/xui/fr/floater_pathfinding_console.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="floater_pathfinding_console" title="Vue / test de recherche de chemin"> +<floater name="floater_pathfinding_console" title="Vue/Test de recherche de chemin"> <floater.string name="navmesh_viewer_status_library_not_implemented"> Implémentation de la bibliothèque de recherche de chemin introuvable </floater.string> @@ -93,10 +93,10 @@ </panel> <panel label="Chemin test" name="test_panel"> <text name="ctrl_click_label"> - Ctrl-clic : sélection du point de départ + Ctrl-clic : sélection point de départ </text> <text name="shift_click_label"> - Maj-clic : sélection du point d'arrivée + Maj-clic : sélection point d'arrivée </text> <text name="character_width_label"> Largeur du personnage @@ -115,7 +115,7 @@ <combo_box.item label="C" name="path_character_type_c"/> <combo_box.item label="D" name="path_character_type_d"/> </combo_box> - <button label="Effacer le chemin" name="clear_path"/> + <button label="Effacer chemin" name="clear_path"/> </panel> </tab_container> </floater> diff --git a/indra/newview/skins/default/xui/fr/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/fr/floater_pathfinding_linksets.xml index 3abddd54ce47a0a3a8b8410cd45e67d9974192a3..894ec6dd9c74df409b17466d75e45573114a6d58 100644 --- a/indra/newview/skins/default/xui/fr/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/fr/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [Groupe] </floater.string> + <floater.string name="linkset_is_scripted"> + Oui + </floater.string> + <floater.string name="linkset_is_not_scripted"> + Non + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Inconnu + </floater.string> <floater.string name="linkset_use_walkable"> Marche possible </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Nom (prim racine)" name="name"/> <scroll_list.columns label="Description (prim racine)" name="description"/> <scroll_list.columns label="Propriétaire" name="owner"/> + <scroll_list.columns label="Scripté" name="scripted"/> <scroll_list.columns label="Impact" name="land_impact"/> <scroll_list.columns label="Distance" name="dist_from_you"/> <scroll_list.columns label="Usage du groupe de liens" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/fr/floater_spellcheck.xml b/indra/newview/skins/default/xui/fr/floater_spellcheck.xml index 077ce2582844892a651428eafc4eb5dfb969d233..635db52ffa9d0f354ef25d1fc269855c0fe3e5e1 100644 --- a/indra/newview/skins/default/xui/fr/floater_spellcheck.xml +++ b/indra/newview/skins/default/xui/fr/floater_spellcheck.xml @@ -2,7 +2,7 @@ <floater name="spellcheck_floater" title="Paramètres du vérificateur orthographique"> <check_box label="Activer le vérificateur orthographique" name="spellcheck_enable"/> <text name="spellcheck_main"> - Dictionnaire principal : + Diction. principal : </text> <text label="Journaux :" name="spellcheck_additional"> Dictionnaires supplémentaires : diff --git a/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml index 43397c2acd43057441fb144d58049294de73ec08..f86c1a42171e6df30fbb35bebe340bd85b17a38c 100644 --- a/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/fr/floater_texture_ctrl.xml @@ -20,7 +20,7 @@ <button label="Vierge" label_selected="Vierge" name="Blank" width="60"/> <button label="Aucune" label_selected="Aucune" left="68" name="None" width="60"/> <button bottom="-240" label="" label_selected="" name="Pipette"/> - <check_box initial_value="true" label="Aperçu en direct" name="apply_immediate_check"/> + <check_box initial_value="true" label="Aperçu direct" name="apply_immediate_check"/> <text name="preview_disabled" value="Aperçu désactivé"/> <filter_editor label="Filtrer les textures" name="inventory search editor"/> <check_box initial_value="false" label="Afficher les dossiers" name="show_folders_check"/> diff --git a/indra/newview/skins/default/xui/fr/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/fr/floater_texture_fetch_debugger.xml index f0cc95319d0d46e1628008407eb261082e601a07..caae15ea17db990f4f1012ba2074ac0810a5756b 100644 --- a/indra/newview/skins/default/xui/fr/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/fr/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, nouvelle récupération des données visibles du cache, Durée : [TIME] secondes, Récupéré : [SIZE] Ko, [PIXEL] Mpixels </text> + <text name="total_time_refetch_all_cache_label"> + 16, nouvelle récupération de toutes les textures du cache, Durée : [TIME] secondes, Récupéré : [SIZE] Ko, [PIXEL] Mpixels + </text> <text name="total_time_refetch_vis_http_label"> - 16, nouvelle récupération des données visibles de la requête HTTP, Durée : [TIME] secondes, Récupéré : [SIZE] Ko, [PIXEL] Mpixels + 17, nouvelle récupération des données visibles de la requête HTTP, Durée : [TIME] secondes, Récupéré : [SIZE] Ko, [PIXEL] Mpixels + </text> + <text name="total_time_refetch_all_http_label"> + 18, nouvelle récupération de toutes les textures de la requête HTTP, Durée : [TIME] secondes, Récupéré : [SIZE] Ko, [PIXEL] Mpixels + </text> + <spinner label="19, taux de texels/pixels :" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, source des textures : </text> - <spinner label="17, taux de texels/pixels :" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Cache + HTTP" name="0"/> + <radio_item label="HTTP uniquement" name="1"/> + </radio_group> <button label="Démarrer" name="start_btn"/> <button label="Réinitialiser" name="clear_btn"/> <button label="Fermer" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Décoder" name="decode_btn"/> <button label="Texture GL" name="gl_btn"/> <button label="Récupérer à nouveau les données visibles du cache" name="refetchviscache_btn"/> + <button label="Récupérer cache" name="refetchallcache_btn"/> <button label="Récupérer à nouveau les données visibles de la requête HTTP" name="refetchvishttp_btn"/> + <button label="Récupérer HTTP" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/fr/floater_top_objects.xml b/indra/newview/skins/default/xui/fr/floater_top_objects.xml index aeeb462ac638c21005bd544237d185b8e719e20f..eb084d91841350610ab3049bec2b34ed9c7925ca 100644 --- a/indra/newview/skins/default/xui/fr/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/fr/floater_top_objects.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="top_objects" title="Objets les plus utilisés"> +<floater name="top_objects" title="Objets les plus consommateurs"> <floater.string name="top_scripts_title"> Scripts les plus consommateurs </floater.string> @@ -45,7 +45,7 @@ <text name="owner_name_text"> Propriétaire : </text> - <button label="Filtre" name="filter_owner_btn"/> + <button label="Filtrer" name="filter_owner_btn"/> <text name="parcel_name_text"> Parcelle : </text> diff --git a/indra/newview/skins/default/xui/fr/menu_inventory.xml b/indra/newview/skins/default/xui/fr/menu_inventory.xml index 59dcff907556df80dcf24dbde73ffaa3363a9e36..627d3068c3d5e224631f61e60e6e3ad20840de72 100644 --- a/indra/newview/skins/default/xui/fr/menu_inventory.xml +++ b/indra/newview/skins/default/xui/fr/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Supprimer le dossier système" name="Delete System Folder"/> <menu_item_call label="Démarrer le chat conférence" name="Conference Chat Folder"/> <menu_item_call label="Jouer" name="Sound Play"/> + <menu_item_call label="Copier la SLurl" name="url_copy"/> <menu_item_call label="À propos du repère" name="About Landmark"/> <menu_item_call label="Jouer dans Second Life" name="Animation Play"/> <menu_item_call label="Jouer localement" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/fr/menu_viewer.xml b/indra/newview/skins/default/xui/fr/menu_viewer.xml index 3982a6159fab1b31574767bfadbf7f7ba9dc0fa4..85020afe252b645cbfbc2c66a1968f270319337c 100644 --- a/indra/newview/skins/default/xui/fr/menu_viewer.xml +++ b/indra/newview/skins/default/xui/fr/menu_viewer.xml @@ -8,7 +8,7 @@ <menu_item_call label="Nouvelle fenêtre d'inventaire" name="NewInventoryWindow"/> <menu_item_call label="Endroits..." name="Places"/> <menu_item_call label="Favoris..." name="Picks"/> - <menu_item_call label="Caméra..." name="Camera Controls"/> + <menu_item_call label="Contrôles de la caméra..." name="Camera Controls"/> <menu label="Déplacement" name="Movement"> <menu_item_call label="M'asseoir" name="Sit Down Here"/> <menu_item_check label="Voler" name="Fly"/> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index bb23a1063dba434207f181add8c64a20dc042877..30154d18730a32dad1026c5176b3660b0943b1e1 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -1202,7 +1202,7 @@ par une carte [THIS_GPU]. Vous avez été téléporté vers une région voisine. </notification> <notification name="AvatarMovedLast"> - Votre dernière destination n'est pas disponible actuellement. + La destination demandée n'est pas disponible actuellement. Vous avez été téléporté vers une région voisine. </notification> <notification name="AvatarMovedHome"> @@ -1221,7 +1221,7 @@ Vous pouvez utiliser [SECOND_LIFE] normalement, les autres résidents vous voien L'installation de [APP_NAME] est terminée. Si vous utilisez [SECOND_LIFE] pour la première fois, vous devez ouvrir un compte avant de pouvoir vous connecter. - <usetemplate name="okcancelbuttons" notext="Continuer" yestext="Nouveau compte..."/> + <usetemplate name="okcancelbuttons" notext="Continuer" yestext="Créer un compte..."/> </notification> <notification name="LoginPacketNeverReceived"> Nous avons des difficultés à vous connecter. Il y a peut-être un problème avec votre connexion Internet ou la [SECOND_LIFE_GRID]. @@ -3233,19 +3233,58 @@ Cliquez sur un point dans le monde et faites glisser votre souris pour faire tou Cette action masquera tous les boutons et articles de menu. Pour les récupérer, cliquez de nouveau sur [SHORTCUT]. <usetemplate ignoretext="Confirmer avant de masquer l'interface" name="okcancelignore" notext="Annuler" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> - Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison des restrictions d'autorisation définies les concernant. Ces groupes de liens seront définis sur [RESTRICTED_TYPE]. + <notification name="PathfindingLinksets_WarnOnPhantom"> + L'indicateur Fantôme de certains groupes de liens sélectionnés basculera. + +Voulez-vous continuer ? + <usetemplate ignoretext="L'indicateur Fantôme de certains groupes de liens basculera." name="okcancelignore" notext="Annuler" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> + Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison des restrictions d'autorisation les concernant. Ces groupes de liens seront définis sur [RESTRICTED_TYPE]. + +Voulez-vous continuer ? <usetemplate ignoretext="Certains groupes de liens sélectionnés ne peuvent pas être définis en raison des restrictions d'autorisation les concernant." name="okcancelignore" notext="Annuler" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison d'une forme non convexe. + +Voulez-vous continuer ? <usetemplate ignoretext="Certains groupes de liens sélectionnés ne peuvent pas être définis en raison d'une forme non convexe." name="okcancelignore" notext="Annuler" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + L'indicateur Fantôme de certains groupes de liens sélectionnés basculera. + +Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison des restrictions d'autorisation les concernant. Ces groupes de liens seront définis sur [RESTRICTED_TYPE]. + +Voulez-vous continuer ? + <usetemplate ignoretext="L'indicateur Fantôme de certains groupes de liens sélectionnés basculera et d'autres ne peuvent pas être définis en raison de restrictions d'autorisation sur les groupes de liens." name="okcancelignore" notext="Annuler" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + L'indicateur Fantôme de certains groupes de liens sélectionnés basculera. + +Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison d'une forme non convexe. + +Voulez-vous continuer ? + <usetemplate ignoretext="L'indicateur Fantôme de certains groupes de liens sélectionnés basculera et d'autres ne peuvent pas être définis en raison d'une forme non convexe." name="okcancelignore" notext="Annuler" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison des restrictions d'autorisation les concernant. Ces groupes de liens seront définis sur [RESTRICTED_TYPE]. - Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison d'une forme non convexe. Les types d'usage de ces groupes de liens ne seront pas modifiés. + +Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison d'une forme non convexe. Les types d'usage de ces groupes de liens ne seront pas modifiés. + +Voulez-vous continuer ? <usetemplate ignoretext="Certains groupes de liens sélectionnés ne peuvent pas être définis en raison des restrictions d'autorisation les concernant et d'une forme non convexe." name="okcancelignore" notext="Annuler" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + L'indicateur Fantôme de certains groupes de liens sélectionnés basculera. + +Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison des restrictions d'autorisation les concernant. Ces groupes de liens seront définis sur [RESTRICTED_TYPE]. + +Certains groupes de liens sélectionnés ne peuvent pas être définis sur [REQUESTED_TYPE] en raison d'une forme non convexe. Les types d'usage de ces groupes de liens ne seront pas modifiés. + +Voulez-vous continuer ? + <usetemplate ignoretext="L'indicateur Fantôme de certains groupes de liens sélectionnés basculera et d'autres ne peuvent pas être définis en raison de restrictions d'autorisation sur les groupes de liens et d'une forme non convexe." name="okcancelignore" notext="Annuler" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> L'objet sélectionné affecte le maillage de navigation. Si vous le modifiez en Flexibilité, il sera supprimé de ce maillage. <usetemplate ignoretext="L'objet sélectionné affecte le maillage de navigation. Si vous le modifiez en Flexibilité, il sera supprimé de ce maillage." name="okcancelignore" notext="Annuler" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/fr/panel_block_list_sidetray.xml b/indra/newview/skins/default/xui/fr/panel_block_list_sidetray.xml index f54bed4faeeff5c9470a0565e7b65d81eb66bfdf..96add2a74b1d226bb326bda94eadce3278b8739f 100644 --- a/indra/newview/skins/default/xui/fr/panel_block_list_sidetray.xml +++ b/indra/newview/skins/default/xui/fr/panel_block_list_sidetray.xml @@ -5,6 +5,6 @@ </text> <scroll_list name="blocked" tool_tip="Liste des résidents actuellement ignorés"/> <button label="Ignorer une personne" label_selected="Ignorer le résident..." name="Block resident..." tool_tip="Choisir un résident à ignorer"/> - <button label="Ignorer l'objet par nom" label_selected="Ignorer l'objet par nom..." name="Block object by name..." tool_tip="Choisir un objet à ignorer par nom"/> + <button label="Ignorer un objet par son nom" label_selected="Ignorer un objet par son nom..." name="Block object by name..." tool_tip="Choisir un objet à ignorer par nom"/> <button label="Ne plus ignorer" label_selected="Ne plus ignorer" name="Unblock" tool_tip="Enlever le résident ou l'objet de la liste des ignorés"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_login.xml b/indra/newview/skins/default/xui/fr/panel_login.xml index 7843513e001bc4fb5c342b022d21cb4b988108a1..c8a1fe875197b8c91fcc2cdd852d96cf130a38a9 100644 --- a/indra/newview/skins/default/xui/fr/panel_login.xml +++ b/indra/newview/skins/default/xui/fr/panel_login.xml @@ -23,7 +23,7 @@ <combo_box name="start_location_combo"> <combo_box.item label="Dernier emplacement" name="MyLastLocation"/> <combo_box.item label="Domicile" name="MyHome"/> - <combo_box.item label="<Saisir le nom de la région>" name="Typeregionname"/> + <combo_box.item label="<Nom de la région>" name="Typeregionname"/> </combo_box> </layout_panel> <layout_panel name="links_login_panel"> @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + CRÉER VOTRE COMPTE </text> - <button name="create_new_account_btn" label="S'inscrire"/> + <button label="Commencer" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_postcard_settings.xml b/indra/newview/skins/default/xui/fr/panel_postcard_settings.xml index 6f4e9c23f9c25b8eee022c9b460d49d5dc5914fc..945a5e02721e736d04adfd9001c0c5f683c93673 100644 --- a/indra/newview/skins/default/xui/fr/panel_postcard_settings.xml +++ b/indra/newview/skins/default/xui/fr/panel_postcard_settings.xml @@ -9,12 +9,12 @@ </combo_box> <layout_stack name="postcard_image_params_ls"> <layout_panel name="postcard_image_size_lp"> - <spinner label="Largeur" name="postcard_snapshot_width"/> - <spinner label="Hauteur" name="postcard_snapshot_height"/> + <spinner label="Larg." name="postcard_snapshot_width"/> + <spinner label="Haut." name="postcard_snapshot_height"/> <check_box label="Conserver les proportions" name="postcard_keep_aspect_check"/> </layout_panel> <layout_panel name="postcard_image_format_quality_lp"> - <slider label="Qualité de l'image" name="image_quality_slider"/> + <slider label="Qualité image" name="image_quality_slider"/> <text name="image_quality_level"> ([QLVL]) </text> diff --git a/indra/newview/skins/default/xui/fr/panel_region_debug.xml b/indra/newview/skins/default/xui/fr/panel_region_debug.xml index b15af0d1f20e54de7c950a072c8abf51f89db3ac..98ae250215e59b7db0816079021eb506d12258a2 100644 --- a/indra/newview/skins/default/xui/fr/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/fr/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" left="337" name="top_scripts_help"/> <button label="Redémarrer la région" name="restart_btn" tool_tip="Redémarrer la région au bout de 2 minutes" width="160"/> <button label="?" left="177" name="restart_help"/> - <button label="Annuler le redémarrage" name="cancel_restart_btn" tool_tip="Retarder le redémarrage de la région d'une heure" width="160"/> + <button label="Annuler le redémarrage" name="cancel_restart_btn" tool_tip="Annuler le redémarrage de la région." width="160"/> </panel> diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml index f40bcec9089b94af4171ba817126c5817cba8c58..472c4a5e8f8dc7b5d1e1bea51e5d2c7bae00dd47 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_inventory.xml @@ -4,7 +4,7 @@ Enregistrer dans l'inventaire </text> <text name="hint_lbl"> - L'enregistrement d'une image dans votre inventaire coûte [UPLOAD_COST] L$. Pour enregistrer votre image sous forme de texture, sélectionnez l'un des formats carrés. + L'enregistrement d'une image dans l'inventaire coûte [UPLOAD_COST] L$. Pour enregistrer votre image sous forme de texture, sélectionnez un format carré. </text> <combo_box label="Résolution" name="texture_size_combo"> <combo_box.item label="Fenêtre actuelle" name="CurrentWindow"/> diff --git a/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml b/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml index 48ccacb37429d8fb73c87b5b6c6ad779bf430053..97dc3e7e2bf22102586a9ef6f7d7447c4617f20e 100644 --- a/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml +++ b/indra/newview/skins/default/xui/fr/panel_snapshot_local.xml @@ -25,7 +25,7 @@ <combo_box.item label="JPEG" name="JPEG"/> <combo_box.item label="BMP (sans perte)" name="BMP"/> </combo_box> - <slider label="Qualité d'image" name="image_quality_slider"/> + <slider label="Qualité image" name="image_quality_slider"/> <text name="image_quality_level"> ([QLVL]) </text> diff --git a/indra/newview/skins/default/xui/fr/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/fr/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..e05c93a4acdb0d19a6e25b3f683a68171af2b2f8 --- /dev/null +++ b/indra/newview/skins/default/xui/fr/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="Principal" name="System Volume"/> + <slider label="Boutons" name="UI Volume"/> + <slider label="Ambiant" name="Wind Volume"/> + <slider label="Sons" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="Activer les sons des gestes."/> + <slider label="Musique" name="Music Volume"/> + <check_box name="enable_music" tool_tip="Activer les flux de musique."/> + <slider label="Médias" name="Media Volume"/> + <check_box name="enable_media" tool_tip="Activer les flux de média."/> + <slider label="Voix" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="Activer le chat vocal."/> +</panel> diff --git a/indra/newview/skins/default/xui/fr/strings.xml b/indra/newview/skins/default/xui/fr/strings.xml index 6dc1439593e0e0193500b02606fc6f9d343743f2..27f0e9464b946fe3521743a8846bac6bc59940f8 100644 --- a/indra/newview/skins/default/xui/fr/strings.xml +++ b/indra/newview/skins/default/xui/fr/strings.xml @@ -1414,6 +1414,12 @@ Veuillez réessayer de vous connecter dans une minute. <string name="InvFolder favorite"> Mes Favoris </string> + <string name="InvFolder Favorites"> + Mes favoris + </string> + <string name="InvFolder favorites"> + Mes favoris + </string> <string name="InvFolder Current Outfit"> Tenue actuelle </string> @@ -1429,6 +1435,12 @@ Veuillez réessayer de vous connecter dans une minute. <string name="InvFolder Meshes"> Maillages </string> + <string name="InvFolder Received Items"> + Articles reçus + </string> + <string name="InvFolder Merchant Outbox"> + Boîte d'envoi vendeur + </string> <string name="InvFolder Friends"> Amis </string> @@ -4902,7 +4914,7 @@ Essayez avec le chemin d'accès à l'éditeur entre guillemets doubles Parler </string> <string name="Command_View_Label"> - Paramètres de la caméra + Caméra </string> <string name="Command_Voice_Label"> Paramètres vocaux diff --git a/indra/newview/skins/default/xui/it/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/it/floater_pathfinding_linksets.xml index 2c6d620b0af40b29315aecc6f812105fe30fea42..7edac3ff463fbc709b2b75ad62a135c034550d35 100644 --- a/indra/newview/skins/default/xui/it/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/it/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [gruppo] </floater.string> + <floater.string name="linkset_is_scripted"> + Sì + </floater.string> + <floater.string name="linkset_is_not_scripted"> + No + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Sconosciuto + </floater.string> <floater.string name="linkset_use_walkable"> Camminabile </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Nome (prim principale)" name="name"/> <scroll_list.columns label="Descrizione (prim principale)" name="description"/> <scroll_list.columns label="Proprietario" name="owner"/> + <scroll_list.columns label="Scriptato" name="scripted"/> <scroll_list.columns label="Impatto" name="land_impact"/> <scroll_list.columns label="Distanza" name="dist_from_you"/> <scroll_list.columns label="Uso set collegati" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/it/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/it/floater_texture_fetch_debugger.xml index 57e65c3456d83efdd64c11b22be5ecb3a6557c5e..49b64533192ccf2a1a89841228babaea1360a72e 100644 --- a/indra/newview/skins/default/xui/it/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/it/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Nuovo fetching elementi visibili dalla cache, Tempo: [TIME] secondi, fetching: [SIZE] KB, [PIXEL] MPixels </text> + <text name="total_time_refetch_all_cache_label"> + 16, Nuovo fetching di tutte le texture dalla cache, Tempo: [TIME] secondi, fetching: [SIZE] KB, [PIXEL] MPixels + </text> <text name="total_time_refetch_vis_http_label"> - 16, Nuovo fetching elementi visibili da HTTP, Tempo: [TIME] secondi, fetching: [SIZE] KB, [PIXEL] MPixels + 17, Nuovo fetching elementi visibili da HTTP, Tempo: [TIME] secondi, fetching: [SIZE] KB, [PIXEL] MPixels + </text> + <text name="total_time_refetch_all_http_label"> + 18, Nuovo fetching di tutte le texture da HTTP, Tempo: [TIME] secondi, fetching: [SIZE] KB, [PIXEL] MPixels + </text> + <spinner label="19, Rapporto Texel/Pixel:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, Fonte texture: </text> - <spinner label="17, Rapporto Texel/Pixel:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Cache + HTTP" name="0"/> + <radio_item label="Solo HTTP" name="1"/> + </radio_group> <button label="Attiva" name="start_btn"/> <button label="Reimposta" name="clear_btn"/> <button label="Chiudi" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Decodifica" name="decode_btn"/> <button label="Texture GL" name="gl_btn"/> <button label="Nuovo fetch visibili cache" name="refetchviscache_btn"/> + <button label="Nuovo fetching di tutta la cache" name="refetchallcache_btn"/> <button label="Nuovo fetch visibili HTTP" name="refetchvishttp_btn"/> + <button label="Nuovo fetching di tutto il contenuto HTTP" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/it/menu_inventory.xml b/indra/newview/skins/default/xui/it/menu_inventory.xml index 4bf6be82fd8afd067f0e0d0ed512f548deb2996a..b31e35771dfcbde52c3355d42fed15cd31e8dd0e 100644 --- a/indra/newview/skins/default/xui/it/menu_inventory.xml +++ b/indra/newview/skins/default/xui/it/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Elimina la cartella di sistema" name="Delete System Folder"/> <menu_item_call label="Inizia la conferenza chat" name="Conference Chat Folder"/> <menu_item_call label="Esegui" name="Sound Play"/> + <menu_item_call label="Copia SLurl" name="url_copy"/> <menu_item_call label="Informazioni sul punto di riferimento" name="About Landmark"/> <menu_item_call label="Riproduci in Second Life" name="Animation Play"/> <menu_item_call label="Esegui localmente" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml index 9bdce2e2f647f478e48d180b66053416f004c394..d5fdde4e7de8452e44c41e6d6f35116dfbedac49 100644 --- a/indra/newview/skins/default/xui/it/notifications.xml +++ b/indra/newview/skins/default/xui/it/notifications.xml @@ -1207,7 +1207,7 @@ a '[THIS_GPU]' Sei stato trasferito in una regione vicina. </notification> <notification name="AvatarMovedLast"> - La tua ultima posizione non è al momento disponibile. + La posizione richiesta non è al momento disponibile. Sei stato trasferito in una regione vicina. </notification> <notification name="AvatarMovedHome"> @@ -1226,7 +1226,7 @@ Puoi comunque usare [SECOND_LIFE] normalmente e gli altri residenti ti vedranno L'installazione di [APP_NAME] è terminata. Se questa è la prima volta che usi [SECOND_LIFE], devi creare un account prima che tu possa effettuare l'accesso. - <usetemplate name="okcancelbuttons" notext="Continua" yestext="Nuovo Account..."/> + <usetemplate name="okcancelbuttons" notext="Continua" yestext="Crea account..."/> </notification> <notification name="LoginPacketNeverReceived"> Ci sono problemi di connessione. È possibile che ci siano problemi con la tua connessione Internet oppure sulla [SECOND_LIFE_GRID]. @@ -3238,19 +3238,58 @@ Clicca e trascina dovunque nel mondo per ruotare la visuale Questa azione cancellerà tutte le voci di menu e i pulsanti. Per visualizzarli nuovamente cliccare ancora [SHORTCUT]. <usetemplate ignoretext="Conferma prima di nascondere l'interfaccia" name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> + <notification name="PathfindingLinksets_WarnOnPhantom"> + L'indicatore oggetto fantasma di alcuni set collegati verrà commutato. + +Vuoi continuare? + <usetemplate ignoretext="L'indicatore oggetto fantasma di alcuni set collegati verrà commutato." name="okcancelignore" notext="Annulla" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> Alcuni set collegati selezionati non possono essere impostati su '[REQUESTED_TYPE]' a causa di limitazioni nelle autorizzazioni per i set collegati. Questi set collegati verranno invece impostati su '[RESTRICTED_TYPE]'. + +Vuoi continuare? <usetemplate ignoretext="Alcuni set collegati selezionati non possono essere impostati a causa di limitazioni nelle autorizzazioni per i set collegati." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Alcuni set collegati selezionati non possono essere impostati a '[REQUESTED_TYPE]' perché la forma è non-convessa. + +Vuoi continuare? <usetemplate ignoretext="Alcuni set collegati selezionati non possono essere impostati perché la forma è non-convessa." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + L'indicatore oggetto fantasma di alcuni set collegati verrà commutato. + +Alcuni set collegati selezionati non possono essere impostati su '[REQUESTED_TYPE]' a causa di limitazioni nelle autorizzazioni per i set collegati. Questi set collegati verranno invece impostati su '[RESTRICTED_TYPE]'. + +Vuoi continuare? + <usetemplate ignoretext="L'indicatore oggetto fantasma per alcuni set collegati selezionati verrà commutato, mentre quello degli altri non può essere impostato a causa di limitazioni nelle autorizzazioni per i set collegati." name="okcancelignore" notext="Annulla" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + L'indicatore oggetto fantasma di alcuni set collegati verrà commutato. + +Alcuni set collegati selezionati non possono essere impostati a '[REQUESTED_TYPE]' perché la forma è non-convessa. + +Vuoi continuare? + <usetemplate ignoretext="L'indicatore oggetto fantasma per alcuni set collegati selezionati verrà commutato, mentre quello degli altri non può essere impostato perché la forma è non-convessa." name="okcancelignore" notext="Annulla" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> Alcuni set collegati selezionati non possono essere impostati su '[REQUESTED_TYPE]' a causa di limitazioni nelle autorizzazioni per i set collegati. Questi set collegati verranno invece impostati su '[RESTRICTED_TYPE]'. - Alcuni set collegati selezionati non possono essere impostati a '[REQUESTED_TYPE]' perché la forma è non-convessa. Il tipo di utilizzo di questi set collegati non cambierà . + +Alcuni set collegati selezionati non possono essere impostati a '[REQUESTED_TYPE]' perché la forma è non-convessa. Il tipo di utilizzo di questi set collegati non cambierà . + +Vuoi continuare? <usetemplate ignoretext="Alcuni set collegati selezionati non possono essere impostati a causa delle limitazioni nelle autorizzazioni per il set collegato e perché la forma è non-convessa." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + L'indicatore oggetto fantasma di alcuni set collegati verrà commutato. + +Alcuni set collegati selezionati non possono essere impostati su '[REQUESTED_TYPE]' a causa di limitazioni nelle autorizzazioni per i set collegati. Questi set collegati verranno invece impostati su '[RESTRICTED_TYPE]'. + +Alcuni set collegati selezionati non possono essere impostati a '[REQUESTED_TYPE]' perché la forma è non-convessa. Il tipo di utilizzo di questi set collegati non cambierà . + +Vuoi continuare? + <usetemplate ignoretext="L'indicatore oggetto fantasma per alcuni set collegati selezionati verrà commutato, mentre quello degli altri non può essere impostato a causa delle limitazioni nelle autorizzazioni per il set collegato e perché la forma è non-convessa." name="okcancelignore" notext="Annulla" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> L'oggetto selezionato influenza il navmesh. Se lo si trasforma in un percorso flessibile verrà rimosso dal navmesh. <usetemplate ignoretext="L'oggetto selezionato influenza il navmesh. Se lo si trasforma in un percorso flessibile verrà rimosso dal navmesh." name="okcancelignore" notext="Annulla" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/it/panel_login.xml b/indra/newview/skins/default/xui/it/panel_login.xml index 02a3f8271c9d95191f03726c2c9a1f257285c289..2afde4094017590b6068dbd2b963b63c0b74af6d 100644 --- a/indra/newview/skins/default/xui/it/panel_login.xml +++ b/indra/newview/skins/default/xui/it/panel_login.xml @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + CREA IL TUO ACCOUNT </text> - <button name="create_new_account_btn" label="Iscriviti"/> + <button label="Inizia adesso" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/it/panel_region_debug.xml b/indra/newview/skins/default/xui/it/panel_region_debug.xml index 789dade097260033c8dfe27f10d17222b5c9978d..aba60d03aabd225d3ef5643507a9fcd423235609 100644 --- a/indra/newview/skins/default/xui/it/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/it/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" left="297" name="top_scripts_help"/> <button label="Riavvia la regione" name="restart_btn" tool_tip="Dai 2 minuti di tempo massimo e fai riavviare la regione"/> <button label="?" name="restart_help"/> - <button label="Annulla riavvio" name="cancel_restart_btn" tool_tip="Ritarda il riavvio della regione di un'ora"/> + <button label="Annulla riavvio" name="cancel_restart_btn" tool_tip="Annulla riavvio regione"/> </panel> diff --git a/indra/newview/skins/default/xui/it/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/it/panel_volume_pulldown.xml index 1792b094135eb65181bba7f0922edf29c4767baa..bc17fc0c89479b6080a2a0c2a227060403c7315e 100644 --- a/indra/newview/skins/default/xui/it/panel_volume_pulldown.xml +++ b/indra/newview/skins/default/xui/it/panel_volume_pulldown.xml @@ -1,15 +1,15 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="volumepulldown_floater" width="220"> - <button left="197" name="prefs_btn"/> - <slider label="Generale" name="System Volume" label_width="60" width="160"/> - <slider label="Interfaccia" name="UI Volume" label_width="60" width="160"/> - <slider label="Ambiente" name="Wind Volume" label_width="60" width="160"/> - <slider label="Suoni" name="SFX Volume" label_width="60" width="160"/> - <check_box name="gesture_audio_play_btn" tool_tip="Attiva suoni Gesture"/> - <slider label="Musica" name="Music Volume" label_width="60" width="160"/> - <check_box tool_tip="Abilita musica in streaming" name="enable_music"/> - <slider label="Media" name="Media Volume" label_width="60" width="160"/> - <check_box tool_tip="Abilita riproduzione media" name="enable_media"/> - <slider label="Voice" name="Voice Volume" label_width="60" width="160"/> - <check_box tool_tip="Abilita il voice" name="enable_voice_check"/> + <button left="197" name="prefs_btn"/> + <slider label="Principale" label_width="60" name="System Volume" width="160"/> + <slider label="Pulsanti" label_width="60" name="UI Volume" width="160"/> + <slider label="Ambiente" label_width="60" name="Wind Volume" width="160"/> + <slider label="Suoni" label_width="60" name="SFX Volume" width="160"/> + <check_box name="gesture_audio_play_btn" tool_tip="Attiva suoni dai gesti"/> + <slider label="Musica" label_width="60" name="Music Volume" width="160"/> + <check_box name="enable_music" tool_tip="Attiva streaming musica"/> + <slider label="Multimedia" label_width="60" name="Media Volume" width="160"/> + <check_box name="enable_media" tool_tip="Attiva streaming multimediale"/> + <slider label="Voce" label_width="60" name="Voice Volume" width="160"/> + <check_box name="enable_voice_check" tool_tip="Attiva chat vocale"/> </panel> diff --git a/indra/newview/skins/default/xui/it/strings.xml b/indra/newview/skins/default/xui/it/strings.xml index f2afcafbea92531f484f78cc3833fe42f946e678..2dc91d316a5b8fec0e70b6aede86258bbcb03d34 100644 --- a/indra/newview/skins/default/xui/it/strings.xml +++ b/indra/newview/skins/default/xui/it/strings.xml @@ -1399,6 +1399,12 @@ Prova ad accedere nuovamente tra un minuto. <string name="InvFolder favorite"> I miei preferiti </string> + <string name="InvFolder Favorites"> + I miei preferiti + </string> + <string name="InvFolder favorites"> + I miei preferiti + </string> <string name="InvFolder Current Outfit"> Abbigliamento attuale </string> @@ -1414,6 +1420,12 @@ Prova ad accedere nuovamente tra un minuto. <string name="InvFolder Meshes"> Reticoli </string> + <string name="InvFolder Received Items"> + Oggetti ricevuti + </string> + <string name="InvFolder Merchant Outbox"> + Casella venditore in uscita + </string> <string name="InvFolder Friends"> Amici </string> diff --git a/indra/newview/skins/default/xui/ja/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/ja/floater_pathfinding_linksets.xml index d05a74fe092e428147e821149302d2e7cb39b636..4441d5e7381c35c7bf64052bce7611f02a9c6914 100644 --- a/indra/newview/skins/default/xui/ja/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/ja/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [group] </floater.string> + <floater.string name="linkset_is_scripted"> + ã¯ã„ + </floater.string> + <floater.string name="linkset_is_not_scripted"> + ã„ã„㈠+ </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + 䏿˜Ž + </floater.string> <floater.string name="linkset_use_walkable"> æ©è¡Œå¯èƒ½ </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="åå‰ï¼ˆãƒ«ãƒ¼ãƒˆãƒ—リム)" name="name"/> <scroll_list.columns label="説明(ルートプリム)" name="description"/> <scroll_list.columns label="所有者" name="owner"/> + <scroll_list.columns label="スクリプト" name="scripted"/> <scroll_list.columns label="è² è·" name="land_impact"/> <scroll_list.columns label="è·é›¢" name="dist_from_you"/> <scroll_list.columns label="リンクセットã®ç”¨é€”" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/ja/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/ja/floater_texture_fetch_debugger.xml index 4efdf7d40d647b30a5108b3d2eeaebb33456976e..adc35137b515f0eda7ebc222267f6ff5f7d664fe 100644 --- a/indra/newview/skins/default/xui/ja/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/ja/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, ã‚ャッシュã‹ã‚‰è¡¨ç¤ºãƒ†ã‚¯ã‚¹ãƒãƒ£ã‚’å†å–å¾—ã€æ™‚間:[TIME] ç§’ã€å–得:[SIZE]KBã€[PIXEL]メガピクセル </text> + <text name="total_time_refetch_all_cache_label"> + 16ã€ã‚ャッシュã‹ã‚‰ã™ã¹ã¦ã®ãƒ†ã‚¯ã‚¹ãƒãƒ£ã‚’å†ãƒ•ェッãƒä¸ã€æ™‚é–“: [TIME] ç§’ã€ãƒ•ã‚§ãƒƒãƒæ¸ˆã¿: [SIZE]KBã€[PIXEL]メガピクセル + </text> <text name="total_time_refetch_vis_http_label"> - 16, HTTP ã‹ã‚‰è¡¨ç¤ºãƒ†ã‚¯ã‚¹ãƒãƒ£ã‚’å†å–å¾—ã€æ™‚間:[TIME] ç§’ã€å–得:[SIZE]KBã€[PIXEL]メガピクセル + 17ã€HTTP ã‹ã‚‰å¯è¦–ファイルをå†ãƒ•ェッãƒä¸ã€æ™‚é–“: [TIME] ç§’ã€ãƒ•ã‚§ãƒƒãƒæ¸ˆã¿: [SIZE]KBã€[PIXEL]メガピクセル + </text> + <text name="total_time_refetch_all_http_label"> + 18ã€HTTP ã‹ã‚‰ã™ã¹ã¦ã®ãƒ†ã‚¯ã‚¹ãƒãƒ£ã‚’å†ãƒ•ェッãƒä¸ã€æ™‚é–“: [TIME] ç§’ã€ãƒ•ã‚§ãƒƒãƒæ¸ˆã¿: [SIZE]KBã€[PIXEL]メガピクセル + </text> + <spinner label="19ã€ãƒ†ã‚»ãƒ«/ピクセル比:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20ã€ãƒ†ã‚¯ã‚¹ãƒãƒ£ ソース: </text> - <spinner label="17, テクセルï¼ãƒ”ã‚¯ã‚»ãƒ«ã®æ¯”率:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="ã‚ャッシュ + HTTP" name="0"/> + <radio_item label="HTTP ã®ã¿" name="1"/> + </radio_group> <button label="é–‹å§‹" name="start_btn"/> <button label="リセット" name="clear_btn"/> <button label="é–‰ã˜ã‚‹" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="デコード" name="decode_btn"/> <button label="GL テクスãƒãƒ£" name="gl_btn"/> <button label="ã‚ャッシュ表示テクスãƒãƒ£å†å–å¾—" name="refetchviscache_btn"/> + <button label="ã™ã¹ã¦ã®ã‚ャッシュをå†ãƒ•ェッãƒ" name="refetchallcache_btn"/> <button label="HTTP表示テクスãƒãƒ£å†å–å¾—" name="refetchvishttp_btn"/> + <button label="ã™ã¹ã¦ã® HTTP ã‚’å†ãƒ•ェッãƒ" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/ja/menu_inventory.xml b/indra/newview/skins/default/xui/ja/menu_inventory.xml index d1893a0fc86184b27b30d5f7b3c275ad3acbb34f..106b09453aed859649a7f0d4f6681b08f0bbb206 100644 --- a/indra/newview/skins/default/xui/ja/menu_inventory.xml +++ b/indra/newview/skins/default/xui/ja/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="システムフォルダを削除ã™ã‚‹" name="Delete System Folder"/> <menu_item_call label="コンファレンスãƒãƒ£ãƒƒãƒˆã‚’é–‹å§‹ã™ã‚‹" name="Conference Chat Folder"/> <menu_item_call label="å†ç”Ÿã™ã‚‹" name="Sound Play"/> + <menu_item_call label="SLurl をコピー" name="url_copy"/> <menu_item_call label="ãƒ©ãƒ³ãƒ‰ãƒžãƒ¼ã‚¯ã®æƒ…å ±" name="About Landmark"/> <menu_item_call label="インワールドã§å†ç”Ÿã™ã‚‹" name="Animation Play"/> <menu_item_call label="ãƒãƒ¼ã‚«ãƒ«ã§å†ç”Ÿã™ã‚‹" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index 9453e9ba3b5724030a2c2b233092fc1b9dcc150c..d5cff18a24ab2a674df988191b5f040652532d99 100644 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -1240,7 +1240,7 @@ https://wiki.secondlife.com/wiki/Adding_Spelling_Dictionaries ã‚’å‚ç…§ã—ã¦ã è¿‘ãã®ãƒªãƒ¼ã‚¸ãƒ§ãƒ³ã«ç§»å‹•ã—ã¾ã—ãŸã€‚ </notification> <notification name="AvatarMovedLast"> - å‰å›žã„ãŸå ´æ‰€ã¯ç¾åœ¨ã”利用ã„ãŸã ã‘ã¾ã›ã‚“。 + リクエストã•れãŸå ´æ‰€ã¯ç¾åœ¨ã”利用ã„ãŸã ã‘ã¾ã›ã‚“。 è¿‘ãã®ãƒªãƒ¼ã‚¸ãƒ§ãƒ³ã«ç§»å‹•ã—ã¾ã—ãŸã€‚ </notification> <notification name="AvatarMovedHome"> @@ -1259,7 +1259,7 @@ https://wiki.secondlife.com/wiki/Adding_Spelling_Dictionaries ã‚’å‚ç…§ã—ã¦ã [APP_NAME] ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå®Œäº†ã—ã¾ã—ãŸã€‚ [SECOND_LIFE] を使ã£ãŸã“ã¨ãŒãªã„å ´åˆã¯ã€ãƒã‚°ã‚¤ãƒ³ã™ã‚‹å‰ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ä½œæˆã‚’行ã£ã¦ãã ã•ã„。 - <usetemplate name="okcancelbuttons" notext="続行" yestext="æ–°è¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ..."/> + <usetemplate name="okcancelbuttons" notext="続行" yestext="アカウントを作æˆ..."/> </notification> <notification name="LoginPacketNeverReceived"> 接続ãŒãªã‹ãªã‹ã§ãã¾ã›ã‚“。 ãŠä½¿ã„ã®ã‚¤ãƒ³ã‚¿ãƒ¼ãƒãƒƒãƒˆæŽ¥ç¶šã‹ã€[SECOND_LIFE_GRID] ã®å•題ã¨è€ƒãˆã‚‰ã‚Œã¾ã™ã€‚ @@ -3278,19 +3278,58 @@ M ã‚ーを押ã—ã¦å¤‰æ›´ã—ã¾ã™ã€‚ ã“ã®æ“作ã«ã‚ˆã‚Šã€å…¨ã¦ã®ãƒ¡ãƒ‹ãƒ¥ãƒ¼é …ç›®ã¨ãƒœã‚¿ãƒ³ãŒéžè¡¨ç¤ºã«ãªã‚Šã¾ã™ã€‚å†ã³è¡¨ç¤ºã™ã‚‹ã«ã¯ [SHORTCUT] ã‚’ã‚‚ã†ä¸€åº¦ã‚¯ãƒªãƒƒã‚¯ã—ã¦ãã ã•ã„。 <usetemplate ignoretext="UI ã‚’éžè¡¨ç¤ºã«ã™ã‚‹å‰ã«ç¢ºèª" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> + <notification name="PathfindingLinksets_WarnOnPhantom"> + é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã¾ã™ã€‚ + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ + <usetemplate ignoretext="é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã¾ã™ã€‚" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> 一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ä»£ã‚り㫠'[RESTRICTED_TYPE]' ã«è¨å®šã•れã¾ã™ã€‚ + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ <usetemplate ignoretext="一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。 + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ <usetemplate ignoretext="é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã¾ã™ã€‚ + +一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ä»£ã‚り㫠'[RESTRICTED_TYPE]' ã«è¨å®šã•れã¾ã™ã€‚ + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ + <usetemplate ignoretext="é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã€ä»–ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã¾ã™ã€‚ + +é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。 + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ + <usetemplate ignoretext="é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã€ä»–ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> 一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ä»£ã‚り㫠'[RESTRICTED_TYPE]' ã«è¨å®šã•れã¾ã™ã€‚ + é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ç”¨é€”タイプã¯å¤‰ã‚りã¾ã›ã‚“。 + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ <usetemplate ignoretext="一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ãŠã‚Šã€ã¾ãŸå½¢çжãŒå‡¸çжã§ãªã„ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã¾ã™ã€‚ + +一部ã®é¸æŠžã•れãŸãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã¦ã„ã‚‹ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ä»£ã‚り㫠'[RESTRICTED_TYPE]' ã«è¨å®šã•れã¾ã™ã€‚ + +é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ã€å½¢çжãŒå‡¸çжã§ãªã„ãŸã‚ã€'[REQUESTED_TYPE]' ã«è¨å®šã§ãã¾ã›ã‚“。ã“れらã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ç”¨é€”タイプã¯å¤‰ã‚りã¾ã›ã‚“。 + +ç¶šã‘ã¾ã™ã‹ï¼Ÿ + <usetemplate ignoretext="é¸æŠžã•れãŸä¸€éƒ¨ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã®ãƒ•ァントムフラグãŒåˆ‡ã‚Šæ›¿ãˆã‚‰ã‚Œã€ä»–ã®ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¯ãƒªãƒ³ã‚¯ã‚»ãƒƒãƒˆã¸ã®æ¨©é™ãŒåˆ¶é™ã•れã€ã¾ãŸå½¢çжãŒå‡¸çжã§ãªã„ãŸã‚è¨å®šã§ãã¾ã›ã‚“。" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> é¸æŠžã•れãŸã‚ªãƒ–ジェクトã¯ãƒŠãƒ“メッシュã«å½±éŸ¿ã‚’与ãˆã¾ã™ã€‚オブジェクトをフレã‚シブルパスã«å¤‰æ›´ã™ã‚‹ã¨ã€ãƒŠãƒ“メッシュã‹ã‚‰å‰Šé™¤ã•れã¾ã™ã€‚ <usetemplate ignoretext="é¸æŠžã•れãŸã‚ªãƒ–ジェクトã¯ãƒŠãƒ“メッシュã«å½±éŸ¿ã‚’与ãˆã¾ã™ã€‚オブジェクトをフレã‚シブルパスã«å¤‰æ›´ã™ã‚‹ã¨ã€ãƒŠãƒ“メッシュã‹ã‚‰å‰Šé™¤ã•れã¾ã™ã€‚" name="okcancelignore" notext="å–り消ã—" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/ja/panel_login.xml b/indra/newview/skins/default/xui/ja/panel_login.xml index 780f7aa1ae6dca85bedc074a4ab2d4af7ca824f4..396d9e65b1e14a7a71233ec57bd6c69a46f37806 100644 --- a/indra/newview/skins/default/xui/ja/panel_login.xml +++ b/indra/newview/skins/default/xui/ja/panel_login.xml @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + アカウントを作æˆã—ã¦ãã ã•ã„ </text> - <button name="create_new_account_btn" label="ãŠç”³ã—è¾¼ã¿"/> + <button label="今ã™ãé–‹å§‹" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/ja/panel_region_debug.xml b/indra/newview/skins/default/xui/ja/panel_region_debug.xml index 7d1d837b732dcb22bf5543ec8573c47671f6c626..169da27ce5e33a6b300ac0bc3592bf1bed18735c 100644 --- a/indra/newview/skins/default/xui/ja/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/ja/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" name="top_scripts_help"/> <button label="地域å†èµ·å‹•" name="restart_btn" tool_tip="2分間ã®ã‚«ã‚¦ãƒ³ãƒˆãƒ€ã‚¦ãƒ³å¾Œã€åœ°åŸŸã‚’å†èµ·å‹•ã—ã¾ã™"/> <button label="?" name="restart_help"/> - <button label="å†èµ·å‹•ã‚’ã‚ャンセル" name="cancel_restart_btn" tool_tip="地域ã®å†èµ·å‹•を1時間é…å»¶ã—ã¾ã™"/> + <button label="å†èµ·å‹•ã‚’ã‚ャンセル" name="cancel_restart_btn" tool_tip="リージョンã®å†èµ·å‹•ã‚’ã‚ャンセル"/> </panel> diff --git a/indra/newview/skins/default/xui/ja/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/ja/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..967dedf0619c098b53d83244310e549fd3b41de8 --- /dev/null +++ b/indra/newview/skins/default/xui/ja/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="マスター" name="System Volume"/> + <slider label="ボタン" name="UI Volume"/> + <slider label="環境音" name="Wind Volume"/> + <slider label="サウンド" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="ジェスãƒãƒ£ãƒ¼ã®éŸ³ã‚’有効ã«ã™ã‚‹"/> + <slider label="音楽" name="Music Volume"/> + <check_box name="enable_music" tool_tip="ストリーミング音楽を有効ã«ã™ã‚‹"/> + <slider label="メディア" name="Media Volume"/> + <check_box name="enable_media" tool_tip="ストリーミングメディアを有効ã«ã™ã‚‹"/> + <slider label="ボイス" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="ボイスãƒãƒ£ãƒƒãƒˆã‚’有効ã«ã™ã‚‹"/> +</panel> diff --git a/indra/newview/skins/default/xui/ja/strings.xml b/indra/newview/skins/default/xui/ja/strings.xml index 7c3f9489da7b4aa5edd0df4a2961406ad410da55..ee7c11c3e9f551ba7ccd5950b088714acb840213 100644 --- a/indra/newview/skins/default/xui/ja/strings.xml +++ b/indra/newview/skins/default/xui/ja/strings.xml @@ -1414,6 +1414,12 @@ support@secondlife.com ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 <string name="InvFolder favorite"> ãŠæ°—ã«å…¥ã‚Š </string> + <string name="InvFolder Favorites"> + ãŠæ°—ã«å…¥ã‚Š + </string> + <string name="InvFolder favorites"> + ãŠæ°—ã«å…¥ã‚Š + </string> <string name="InvFolder Current Outfit"> ç€ç”¨ä¸ã®ã‚¢ã‚¦ãƒˆãƒ•ィット </string> @@ -1429,6 +1435,12 @@ support@secondlife.com ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。 <string name="InvFolder Meshes"> メッシュ </string> + <string name="InvFolder Received Items"> + å—ã‘å–ã£ãŸå•†å“ + </string> + <string name="InvFolder Merchant Outbox"> + マーãƒãƒ£ãƒ³ãƒˆã®ã‚¢ã‚¦ãƒˆãƒœãƒƒã‚¯ã‚¹ + </string> <string name="InvFolder Friends"> フレンド </string> diff --git a/indra/newview/skins/default/xui/pt/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/pt/floater_pathfinding_linksets.xml index 8dc14a79a5b2aa887091e0d0dbeec000bd949590..e0c60679dd5cc7324adeadd3e885aa05e59c81fb 100644 --- a/indra/newview/skins/default/xui/pt/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/pt/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [grupo] </floater.string> + <floater.string name="linkset_is_scripted"> + Sim + </floater.string> + <floater.string name="linkset_is_not_scripted"> + Não + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Desconhecido + </floater.string> <floater.string name="linkset_use_walkable"> Caminhável </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Nome (prim raiz)" name="name"/> <scroll_list.columns label="Descrição (prim raiz)" name="description"/> <scroll_list.columns label="Proprietário" name="owner"/> + <scroll_list.columns label="Com script" name="scripted"/> <scroll_list.columns label="Impacto" name="land_impact"/> <scroll_list.columns label="Distância" name="dist_from_you"/> <scroll_list.columns label="Uso do linkset" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/pt/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/pt/floater_texture_fetch_debugger.xml index f20d50362edec89b0f8b84406db052cc657cffe6..0e897aea0950295affd2bbe2b984d3e9c633a523 100644 --- a/indra/newview/skins/default/xui/pt/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/pt/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Obtendo novamente visÃveis do cache, Tempo: [TIME] segundos, Obtidos: [SIZE]KB, [PIXEL]MPixels </text> + <text name="total_time_refetch_all_cache_label"> + 16, Obtendo novamente todas as texturas do cache, Tempo: [TIME] segundos, Obtidos: [SIZE]KB, [PIXEL]MPixels + </text> <text name="total_time_refetch_vis_http_label"> - 16, Obtendo novamente visÃveis do HTTP, Tempo: [TIME] segundos, Obtidos: [SIZE]KB, [PIXEL]MPixels + 17, Obtendo novamente visÃveis do HTTP, Tempo: [TIME] segundos, Obtidos: [SIZE]KB, [PIXEL]MPixels + </text> + <text name="total_time_refetch_all_http_label"> + 18, Obtendo novamente todas as texturas do HTTP, Tempo: [TIME] segundos, Obtidos: [SIZE]KB, [PIXEL]MPixels + </text> + <spinner label="19, Proporção de texel/pixel:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, Origem da textura: </text> - <spinner label="17, Proporção de texel/pixel:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Cache + HTTP" name="0"/> + <radio_item label="Apenas HTTP" name="1"/> + </radio_group> <button label="Iniciar" name="start_btn"/> <button label="Redefinir" name="clear_btn"/> <button label="Fechar" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Decodificar" name="decode_btn"/> <button label="Textura GL" name="gl_btn"/> <button label="Obter novamente cache visÃvel" name="refetchviscache_btn"/> + <button label="Obter todo o cache novamente" name="refetchallcache_btn"/> <button label="Obter novamente HTTP visÃvel" name="refetchvishttp_btn"/> + <button label="Obter todo o HTTP novamente" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/pt/menu_inventory.xml b/indra/newview/skins/default/xui/pt/menu_inventory.xml index 09e1fbf72ea8d5dc97c8dae88fff93f40f0446d5..a3a648eb34cb1c3bf1f1f5fc1f77bb59cf99bef5 100644 --- a/indra/newview/skins/default/xui/pt/menu_inventory.xml +++ b/indra/newview/skins/default/xui/pt/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Excluir pasta do sistema" name="Delete System Folder"/> <menu_item_call label="Pasta conversa em conferência" name="Conference Chat Folder"/> <menu_item_call label="Executar som" name="Sound Play"/> + <menu_item_call label="Copiar SLurl" name="url_copy"/> <menu_item_call label="Sobre o marco" name="About Landmark"/> <menu_item_call label="Executar animação" name="Animation Play"/> <menu_item_call label="Executar áudio" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index 11d987d73a9405b9696e54a0d4822bf2cb81ceb7..ff7382bf8014dd69c224a8afd77ab84f57c419b6 100644 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -1196,7 +1196,7 @@ para '[THIS_GPU]' Você chegou a uma região próxima. </notification> <notification name="AvatarMovedLast"> - Esse destino não está disponÃvel no momento. + O destino solicitado não está disponÃvel no momento. Você chegou a uma região próxima. </notification> <notification name="AvatarMovedHome"> @@ -1212,10 +1212,10 @@ Enquando isso, use o [SECOND_LIFE] normalmente. Seu visual será exibido correta </form> </notification> <notification name="FirstRun"> - A instalação do [APP_NAME] está pronta. + A instalação do [APP_NAME] está pronta. Se você ainda não conhece o [SECOND_LIFE], basta criar uma conta para começar. - <usetemplate name="okcancelbuttons" notext="Continuar" yestext="Nova conta.."/> + <usetemplate name="okcancelbuttons" notext="Continuar" yestext="Criar conta"/> </notification> <notification name="LoginPacketNeverReceived"> Estamos detectando um problema de conexão. Pode haver um problema com a sua conexão à internet ou com o [SECOND_LIFE_GRID]. @@ -3210,18 +3210,57 @@ Se o botão Falar for ocultado, o recurso de voz será desabilitado. Essa ação irá ocultar todos os itens de menu e botões. Para trazê-los de volta, clique em [SHORTCUT] novamente. <usetemplate ignoretext="Confirmar antes de ocultar interface" name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> - Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. + <notification name="PathfindingLinksets_WarnOnPhantom"> + Alguns linksets selecionados terão suas sinalizações fantasmas alternadas. + +Deseja continuar? + <usetemplate ignoretext="Algumas sinalizações fantasmas dos linksets selecionados serão alternadas." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> + Algumas sinalizações fantasmas dos linksets selecionados não podem ser definidas como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. + +Deseja continuar? <usetemplate ignoretext="Alguns linksets selecionados não podem ser definidos devido à s restrições de permissão no linkset." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]', pois a forma não é convexa. + +Deseja continuar? <usetemplate ignoretext="Alguns linksets selecionados não podem ser definidos, pois a forma não é convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> - Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. - Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]', pois a forma não é convexa. Estes tipos de uso de linksets não mudarão. - <usetemplate ignoretext="Alguns linksets selecionados não podem ser definido devido à s restrições de permissão no linkset e porque a forma não é convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + Alguns linksets selecionados terão suas sinalizações fantasmas alternadas. + +Algumas sinalizações fantasmas dos linksets selecionados não podem ser definidas como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. + +Deseja continuar? + <usetemplate ignoretext="Algumas sinalizações fantasmas dos linksets selecionados serão alternadas e outras não poderão ser definidas devido à s restrições de permissão no linkset." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + Alguns linksets selecionados terão suas sinalizações fantasmas alternadas. + +Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]', pois a forma não é convexa. + +Deseja continuar? + <usetemplate ignoretext="Algumas sinalizações fantasmas dos linksets selecionados serão alternadas e outros não podem ser definidas, pois a forma não é convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> + Algumas sinalizações fantasmas dos linksets selecionados não podem ser definidas como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. + +Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]', pois a forma não é convexa. Estes tipos de uso de linksets não mudarão. + +Deseja continuar? + <usetemplate ignoretext="Alguns linksets selecionados não podem ser definidos devido à s restrições de permissão no linkset e porque a forma não é convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + Alguns linksets selecionados terão suas sinalizações fantasmas alternadas. + +Algumas sinalizações fantasmas dos linksets selecionados não podem ser definidas como '[REQUESTED_TYPE]' devido à s restrições de permissão no linkset. Ao invés disso, estes linksets serão definidos como '[RESTRICTED_TYPE]'. + +Alguns linksets selecionados não podem ser definidos como '[REQUESTED_TYPE]', pois a forma não é convexa. Estes tipos de uso de linksets não mudarão. + +Deseja continuar? + <usetemplate ignoretext="Algumas sinalizações fantasmas dos linksets selecionados serão alternadas e outras não poderão ser definidas, pois a forma não é convexa." name="okcancelignore" notext="Cancelar" yestext="OK"/> </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> O objeto selecionado afeta o navmesh. Alterá-lo para um Caminho FlexÃvel irá removê-lo do navmesh. diff --git a/indra/newview/skins/default/xui/pt/panel_login.xml b/indra/newview/skins/default/xui/pt/panel_login.xml index 25a2c9fda045a754e6f3ee03da0f6251a7d8ff7e..d7e9fa76ea9ef323ca0c1b15985a2c044bf4519d 100644 --- a/indra/newview/skins/default/xui/pt/panel_login.xml +++ b/indra/newview/skins/default/xui/pt/panel_login.xml @@ -6,7 +6,7 @@ <layout_stack name="login_widgets"> <layout_panel name="login"> <text name="log_in_text"> - CONECTAR + LOGIN </text> <text name="username_text"> Nome de usuário: @@ -22,24 +22,25 @@ </text> <combo_box name="start_location_combo"> <combo_box.item label="Última posição" name="MyLastLocation"/> - <combo_box.item label="Meu inÃcio" name="MyHome"/> + <combo_box.item label="Minha casa" name="MyHome"/> + <combo_box.item label="<Digite o nome da região>" name="Typeregionname"/> </combo_box> </layout_panel> <layout_panel name="links_login_panel"> <text name="login_help"> - Precisa de ajuda ao conectar? + Precisa de ajuda com o login? </text> <text name="forgot_password_text"> Esqueceu seu nome ou senha? </text> - <button label="conectar" name="connect_btn"/> + <button label="Login" name="connect_btn"/> <check_box label="Lembrar senha" name="remember_check"/> </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + CRIE SUA CONTA </text> - <button name="create_new_account_btn" label="Cadastre-se"/> + <button label="Comece agora" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/pt/panel_region_debug.xml b/indra/newview/skins/default/xui/pt/panel_region_debug.xml index 72e348e48c3251376f678e9ae6da537dfef82f63..be15d40d7427880aaeead7476e8498e599c3c9f9 100644 --- a/indra/newview/skins/default/xui/pt/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/pt/panel_region_debug.xml @@ -36,5 +36,5 @@ <button label="?" left="297" name="top_scripts_help"/> <button label="Reiniciar a região" name="restart_btn" tool_tip="Após 2 minutos de contagem regressiva, reiniciar a região"/> <button label="?" name="restart_help"/> - <button label="Cancelar reinÃcio" name="cancel_restart_btn" tool_tip="Adiar o reinÃcio da região por uma hora"/> + <button label="Cancelar reinÃcio" name="cancel_restart_btn" tool_tip="Cancelar reinÃcio da região"/> </panel> diff --git a/indra/newview/skins/default/xui/pt/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/pt/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..1dfd2a69cadb7fe5f1b904919fa19a0e55593091 --- /dev/null +++ b/indra/newview/skins/default/xui/pt/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="Master" name="System Volume"/> + <slider label="Botões" name="UI Volume"/> + <slider label="Ambiente" name="Wind Volume"/> + <slider label="Sons" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="Ativar sons dos gestos"/> + <slider label="Música" name="Music Volume"/> + <check_box name="enable_music" tool_tip="Ativar streaming de música"/> + <slider label="MÃdia" name="Media Volume"/> + <check_box name="enable_media" tool_tip="Ativar mÃdia em stream"/> + <slider label="Voz" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="Ativar bate-papo de voz"/> +</panel> diff --git a/indra/newview/skins/default/xui/pt/strings.xml b/indra/newview/skins/default/xui/pt/strings.xml index a7234bdc14c7284dc9797f9a64c6ce0a7b807fd6..9f611c08e427bc9867dc8931746b0fc4f4c82f04 100644 --- a/indra/newview/skins/default/xui/pt/strings.xml +++ b/indra/newview/skins/default/xui/pt/strings.xml @@ -1354,6 +1354,12 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para <string name="InvFolder favorite"> Meus favoritos </string> + <string name="InvFolder Favorites"> + Meus favoritos + </string> + <string name="InvFolder favorites"> + Meus favoritos + </string> <string name="InvFolder Current Outfit"> Look atual </string> @@ -1369,6 +1375,12 @@ Pessoas com contas gratuitas não poderão acessar o Second Life no momento para <string name="InvFolder Meshes"> Meshes: </string> + <string name="InvFolder Received Items"> + Itens recebidos + </string> + <string name="InvFolder Merchant Outbox"> + Caixa de saÃda do lojista + </string> <string name="InvFolder Friends"> Amigos </string> diff --git a/indra/newview/skins/default/xui/ru/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/ru/floater_pathfinding_linksets.xml index f1b8e23a2cfac177af6f4befc69f3d176f685da3..db100fa4155ffa84c2ec94603a4f7e6f50927d16 100644 --- a/indra/newview/skins/default/xui/ru/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/ru/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [группа] </floater.string> + <floater.string name="linkset_is_scripted"> + Да + </floater.string> + <floater.string name="linkset_is_not_scripted"> + Ðет + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + ÐеизвеÑтно + </floater.string> <floater.string name="linkset_use_walkable"> Проходимое меÑто </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Ð˜Ð¼Ñ (корневой примитив)" name="name"/> <scroll_list.columns label="ОпиÑание (корневой примитив)" name="description"/> <scroll_list.columns label="Владелец" name="owner"/> + <scroll_list.columns label="Скриптовые" name="scripted"/> <scroll_list.columns label="ВоздейÑтвие" name="land_impact"/> <scroll_list.columns label="РаÑÑтоÑние" name="dist_from_you"/> <scroll_list.columns label="ИÑпользование набора ÑвÑзей" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/ru/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/ru/floater_texture_fetch_debugger.xml index 034106f24c45e3062278a7a35a36e037c73e91d9..628e6c5c870f7daa02d4dcd7fe1d0dddb4a67ebe 100644 --- a/indra/newview/skins/default/xui/ru/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/ru/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Повторное извлечение из кÑша, времÑ: [TIME] Ñ, извлечено: [SIZE] КБ, [PIXEL] МпикÑелов </text> + <text name="total_time_refetch_all_cache_label"> + 16, Повторное извлечение вÑех текÑтур из кÑша, времÑ: [TIME] Ñ, извлечено: [SIZE] КБ, [PIXEL] МпикÑелов + </text> <text name="total_time_refetch_vis_http_label"> - 16, Повторное извлечение из HTTP, времÑ: [TIME] Ñ, извлечено: [SIZE] КБ, [PIXEL] МпикÑелов + 17, Повторное извлечение из HTTP, времÑ: [TIME] Ñ, извлечено: [SIZE] КБ, [PIXEL] МпикÑелов + </text> + <text name="total_time_refetch_all_http_label"> + 18, Повторное извлечение вÑех текÑтур из HTTP, времÑ: [TIME] Ñ, извлечено: [SIZE] КБ, [PIXEL] МпикÑелов + </text> + <spinner label="19, Отношение текÑелы/пикÑелы:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, ИÑтоÑник текÑтур: </text> - <spinner label="17, Отношение текÑелы/пикÑелы" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="КÑш + HTTP" name="0"/> + <radio_item label="Только HTTP" name="1"/> + </radio_group> <button label="ПуÑк" name="start_btn"/> <button label="СброÑ" name="clear_btn"/> <button label="Закрыть" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Декодировать" name="decode_btn"/> <button label="ТекÑтура GL" name="gl_btn"/> <button label="Повторно извлечь из кÑша" name="refetchviscache_btn"/> + <button label="Повторно извлечь вÑе из кÑша" name="refetchallcache_btn"/> <button label="Повторно извлечь из HTTP" name="refetchvishttp_btn"/> + <button label="Повторно извлечь вÑе из HTTP" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/ru/menu_inventory.xml b/indra/newview/skins/default/xui/ru/menu_inventory.xml index 49f7281b4ebae7a58dd9e2d442c168565c9e39a4..37ee19fc1d3d9d57133ab88060550f5b65a780d5 100644 --- a/indra/newview/skins/default/xui/ru/menu_inventory.xml +++ b/indra/newview/skins/default/xui/ru/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Удалить ÑиÑтемную папку" name="Delete System Folder"/> <menu_item_call label="Ðачать конференцию" name="Conference Chat Folder"/> <menu_item_call label="ВоÑпроизвеÑти" name="Sound Play"/> + <menu_item_call label="Копировать URL SL" name="url_copy"/> <menu_item_call label="О закладке" name="About Landmark"/> <menu_item_call label="Проиграть Ð´Ð»Ñ Ð²Ñех" name="Animation Play"/> <menu_item_call label="Проиграть Ð´Ð»Ñ ÑебÑ" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/ru/notifications.xml b/indra/newview/skins/default/xui/ru/notifications.xml index 9dad569ddd7f93db2eb8a0c69d0c5c8569da9a8a..85b7074253f82ee32d08c1a081789df6a98db99f 100644 --- a/indra/newview/skins/default/xui/ru/notifications.xml +++ b/indra/newview/skins/default/xui/ru/notifications.xml @@ -1207,7 +1207,7 @@ Ð’Ñ‹ перемещены в ÑоÑедний регион. </notification> <notification name="AvatarMovedLast"> - Ваше поÑледнее меÑтоположение ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ´Ð¾Ñтупно. + Требуемое вами меÑтоположение ÑÐµÐ¹Ñ‡Ð°Ñ Ð½ÐµÐ´Ð¾Ñтупно. Ð’Ñ‹ перемещены в ÑоÑедний регион. </notification> <notification name="AvatarMovedHome"> @@ -3233,19 +3233,58 @@ http://secondlife.com/download. Ðто дейÑтвие приведет к Ñкрытию вÑех меню и кнопок. Чтобы вернуть их, щелкните [SHORTCUT] Ñнова. <usetemplate ignoretext="Подтверждать перед Ñкрытием интерфейÑа" name="okcancelignore" notext="Отмена" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> + <notification name="PathfindingLinksets_WarnOnPhantom"> + Признак фантомноÑти некоторых выбранных наборов ÑвÑзей будет изменен. + +Продолжить? + <usetemplate ignoretext="Признак фантомноÑти некоторых выбранных наборов ÑвÑзей будет изменен." name="okcancelignore" notext="Отмена" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ тип «[REQUESTED_TYPE]» из-за ограничений Ñтих наборов. Ðти наборы ÑвÑзей будут иметь тип «[RESTRICTED_TYPE]». + +Продолжить? <usetemplate ignoretext="ÐÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ некоторые выбранные наборы ÑвÑзей из-за ограничений Ñтих наборов." name="okcancelignore" notext="Отмена" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей невозможно задать тип «[REQUESTED_TYPE]», так как фигура не выпуклаÑ. + +Продолжить? <usetemplate ignoretext="ÐÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ некоторые выбранные наборы ÑвÑзей, так как фигура не выпуклаÑ." name="okcancelignore" notext="Отмена" yestext="OK"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + Признак фантомноÑти некоторых выбранных наборов ÑвÑзей будет изменен. + +Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ тип «[REQUESTED_TYPE]» из-за ограничений Ñтих наборов. Ðти наборы ÑвÑзей будут иметь тип «[RESTRICTED_TYPE]». + +Продолжить? + <usetemplate ignoretext="Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей будет изменен признак фантомноÑти, а другие наборы Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ из-за ограничений Ñтих наборов." name="okcancelignore" notext="Отмена" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + Признак фантомноÑти некоторых выбранных наборов ÑвÑзей будет изменен. + +Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей невозможно задать тип «[REQUESTED_TYPE]», так как фигура не выпуклаÑ. + +Продолжить? + <usetemplate ignoretext="Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей будет изменен признак фантомноÑти, а другие наборы Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ, так как фигура не выпуклаÑ." name="okcancelignore" notext="Отмена" yestext="OK"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ тип «[REQUESTED_TYPE]» из-за ограничений Ñтих наборов. Ðти наборы ÑвÑзей будут иметь тип «[RESTRICTED_TYPE]». - Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей невозможно задать тип «[REQUESTED_TYPE]», так как фигура не выпуклаÑ. Тип иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтих наборов ÑвÑзей не изменитÑÑ. + +Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей невозможно задать тип «[REQUESTED_TYPE]», так как фигура не выпуклаÑ. Тип иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтих наборов ÑвÑзей не изменитÑÑ. + +Продолжить? <usetemplate ignoretext="ÐÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ некоторые выбранные наборы ÑвÑзей из-за ограничений Ñтих наборов и потому, что фигура не выпуклаÑ." name="okcancelignore" notext="Отмена" yestext="OK"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + Признак фантомноÑти некоторых выбранных наборов ÑвÑзей будет изменен. + +Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ тип «[REQUESTED_TYPE]» из-за ограничений Ñтих наборов. Ðти наборы ÑвÑзей будут иметь тип «[RESTRICTED_TYPE]». + +Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей невозможно задать тип «[REQUESTED_TYPE]», так как фигура не выпуклаÑ. Тип иÑÐ¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ð½Ð¸Ñ Ñтих наборов ÑвÑзей не изменитÑÑ. + +Продолжить? + <usetemplate ignoretext="Ð”Ð»Ñ Ð½ÐµÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… выбранных наборов ÑвÑзей будет изменен признак фантомноÑти, а другие наборы Ð½ÐµÐ»ÑŒÐ·Ñ Ð·Ð°Ð´Ð°Ñ‚ÑŒ из-за ограничений Ñтих наборов и потому, что фигура не выпуклаÑ." name="okcancelignore" notext="Отмена" yestext="OK"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> Выбранный объект влиÑет на навигационную Ñетку. ЕÑли заменить его на гибкий путь, он будет удален из навигационной Ñетки. <usetemplate ignoretext="Выбранный объект влиÑет на навигационную Ñетку. ЕÑли заменить его на гибкий путь, он будет удален из навигационной Ñетки." name="okcancelignore" notext="Отмена" yestext="OK"/> diff --git a/indra/newview/skins/default/xui/ru/panel_login.xml b/indra/newview/skins/default/xui/ru/panel_login.xml index 7ead924c1f9809f05739ed92804527bee3a46a91..f0877731c6e34719515dd5f39dba9956210af5c0 100644 --- a/indra/newview/skins/default/xui/ru/panel_login.xml +++ b/indra/newview/skins/default/xui/ru/panel_login.xml @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + СОЗДÐЙТЕ СВОЙ ÐККÐУÐТ </text> - <button name="create_new_account_btn" label="РегиÑтрациÑ"/> + <button label="Ðачать" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/ru/panel_region_debug.xml b/indra/newview/skins/default/xui/ru/panel_region_debug.xml index 7bef927507e04850510b6ba023b986656b43c7f7..4be1e781fabfa0da3b55d0d69a93eba0d591a2cd 100644 --- a/indra/newview/skins/default/xui/ru/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/ru/panel_region_debug.xml @@ -30,5 +30,5 @@ <button label="Самые активные учаÑтники Ñтолкновений..." name="top_colliders_btn" tool_tip="СпиÑок объектов, Ð´Ð»Ñ ÐºÐ¾Ñ‚Ð¾Ñ€Ñ‹Ñ… ÑÑ‚Ð¾Ð»ÐºÐ½Ð¾Ð²ÐµÐ½Ð¸Ñ Ð½Ð°Ð¸Ð±Ð¾Ð»ÐµÐµ вероÑтны"/> <button label="СпиÑок лучших Ñкриптов..." name="top_scripts_btn" tool_tip="Объекты, в которых Ñкрипты выполнÑÑŽÑ‚ÑÑ Ð´Ð¾Ð»ÑŒÑˆÐµ вÑего"/> <button label="Перезагрузить регион" name="restart_btn" tool_tip="ОтÑчитать 2 минуты и перезагрузить регион"/> - <button label="Отменить перезапуÑк" name="cancel_restart_btn" tool_tip="Отложить перезагрузку региона на чаÑ"/> + <button label="Отменить перезапуÑк" name="cancel_restart_btn" tool_tip="Отменить перезапуÑк региона"/> </panel> diff --git a/indra/newview/skins/default/xui/ru/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/ru/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe044cd083bf49d7d261ad65ccc7012de0967236 --- /dev/null +++ b/indra/newview/skins/default/xui/ru/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="ОбщаÑ" name="System Volume"/> + <slider label="Кнопки" name="UI Volume"/> + <slider label="ОкружающаÑ" name="Wind Volume"/> + <slider label="Звуки" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="Включить звуки от жеÑтов"/> + <slider label="Музыка" name="Music Volume"/> + <check_box name="enable_music" tool_tip="Включить потоковую музыку"/> + <slider label="Медиа" name="Media Volume"/> + <check_box name="enable_media" tool_tip="Включить потоковое медиа"/> + <slider label="ГолоÑ" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="Включить голоÑовой чат"/> +</panel> diff --git a/indra/newview/skins/default/xui/ru/strings.xml b/indra/newview/skins/default/xui/ru/strings.xml index 3745aff8fdd83d2ef59f035f5e63a5a6f38a8ca6..6e8ec05e9fcfe767a8c69b07a35880ca0d9cb940 100644 --- a/indra/newview/skins/default/xui/ru/strings.xml +++ b/indra/newview/skins/default/xui/ru/strings.xml @@ -1411,6 +1411,12 @@ support@secondlife.com. <string name="InvFolder favorite"> Мое избранное </string> + <string name="InvFolder Favorites"> + Мое избранное + </string> + <string name="InvFolder favorites"> + Мое избранное + </string> <string name="InvFolder Current Outfit"> Текущий коÑтюм </string> @@ -1426,6 +1432,12 @@ support@secondlife.com. <string name="InvFolder Meshes"> Меши </string> + <string name="InvFolder Received Items"> + Полученные вещи + </string> + <string name="InvFolder Merchant Outbox"> + Торговые иÑходÑщие + </string> <string name="InvFolder Friends"> Ð”Ñ€ÑƒÐ·ÑŒÑ </string> diff --git a/indra/newview/skins/default/xui/tr/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/tr/floater_pathfinding_linksets.xml index 3aee66a860c2bada48c214d1f3d5852ba9f0fd44..2e416c93119806013e5472ade24bcad4a78f469e 100644 --- a/indra/newview/skins/default/xui/tr/floater_pathfinding_linksets.xml +++ b/indra/newview/skins/default/xui/tr/floater_pathfinding_linksets.xml @@ -30,6 +30,9 @@ <floater.string name="linkset_terrain_owner"> -- </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> <floater.string name="linkset_terrain_land_impact"> -- </floater.string> @@ -45,6 +48,15 @@ <floater.string name="linkset_owner_group"> [grup] </floater.string> + <floater.string name="linkset_is_scripted"> + Evet + </floater.string> + <floater.string name="linkset_is_not_scripted"> + Hayır + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + Bilinmiyor + </floater.string> <floater.string name="linkset_use_walkable"> Yürüyebilir </floater.string> @@ -94,6 +106,7 @@ <scroll_list.columns label="Ad (kök prim)" name="name"/> <scroll_list.columns label="Açıklama (kök prim)" name="description"/> <scroll_list.columns label="Sahip" name="owner"/> + <scroll_list.columns label="Komut Dosyalı" name="scripted"/> <scroll_list.columns label="Etki" name="land_impact"/> <scroll_list.columns label="Mesafe" name="dist_from_you"/> <scroll_list.columns label="BaÄŸlantı kümesi kullanımı" name="linkset_use"/> diff --git a/indra/newview/skins/default/xui/tr/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/tr/floater_texture_fetch_debugger.xml index a592479b6ce5de4e71ed3e8fb510549912ebfe29..42426225c7d376e0f1325b7ed43b5608180deb40 100644 --- a/indra/newview/skins/default/xui/tr/floater_texture_fetch_debugger.xml +++ b/indra/newview/skins/default/xui/tr/floater_texture_fetch_debugger.xml @@ -45,10 +45,23 @@ <text name="total_time_refetch_vis_cache_label"> 15, Görünür dokuların önbellekten tekrar alınması, Süre: [TIME] saniye, Alınan: [SIZE]KB, [PIXEL]MPiksel </text> + <text name="total_time_refetch_all_cache_label"> + 16, Tüm dokuların önbellekten tekrar alınması, Süre: [TIME] saniye, Alınan: [SIZE]KB, [PIXEL]MPiksel + </text> <text name="total_time_refetch_vis_http_label"> - 16, Görünür dokuların HTTP'den tekrar alınması, Süre: [TIME] saniye, Alınan: [SIZE]KB, [PIXEL]MPiksel + 17, Görünür dokuların HTTP'den tekrar alınması, Süre: [TIME] saniye, Alınan: [SIZE]KB, [PIXEL]MPiksel + </text> + <text name="total_time_refetch_all_http_label"> + 18, Tüm dokuların HTTP'den tekrar alınması, Süre: [TIME] saniye, Alınan: [SIZE]KB, [PIXEL]MPiksel + </text> + <spinner label="19, Teksel/Piksel Oranı:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20, Doku Kaynağı: </text> - <spinner label="17, Teksel/Piksel Oranı:" name="texel_pixel_ratio"/> + <radio_group name="texture_source"> + <radio_item label="Önbellek + HTTP" name="0"/> + <radio_item label="Sadece HTTP" name="1"/> + </radio_group> <button label="BaÅŸla" name="start_btn"/> <button label="Sıfırla" name="clear_btn"/> <button label="Kapat" name="close_btn"/> @@ -58,5 +71,7 @@ <button label="Åžifre Çöz" name="decode_btn"/> <button label="GL Dokusu" name="gl_btn"/> <button label="Görünür Dokuları Önbellekten Tekrar Al" name="refetchviscache_btn"/> + <button label="Tüm ÖnbelleÄŸi Tekrar Al" name="refetchallcache_btn"/> <button label="Görünür Dokuları HTTP'den Tekrar Al" name="refetchvishttp_btn"/> + <button label="Tüm HTTP'yi Tekrar Al" name="refetchallhttp_btn"/> </floater> diff --git a/indra/newview/skins/default/xui/tr/menu_inventory.xml b/indra/newview/skins/default/xui/tr/menu_inventory.xml index 170cdebd24ca5e23a34bb8508253a950ac3f07f6..51049427af5267eff3415e1ee78d9d6a4a133633 100644 --- a/indra/newview/skins/default/xui/tr/menu_inventory.xml +++ b/indra/newview/skins/default/xui/tr/menu_inventory.xml @@ -68,6 +68,7 @@ <menu_item_call label="Sistem Klasörünü Sil" name="Delete System Folder"/> <menu_item_call label="Konferans Sohbeti BaÅŸlat" name="Conference Chat Folder"/> <menu_item_call label="Oyna" name="Sound Play"/> + <menu_item_call label="SLurl'i Kopyala" name="url_copy"/> <menu_item_call label="Yer İmi Hakkında" name="About Landmark"/> <menu_item_call label="SL Dünyasında Oynat" name="Animation Play"/> <menu_item_call label="Yerel Olarak Oynat" name="Animation Audition"/> diff --git a/indra/newview/skins/default/xui/tr/notifications.xml b/indra/newview/skins/default/xui/tr/notifications.xml index 708e05062fcff3750d560088d77ccf957172fc19..488702f9caa089f6a06b7f465147a97a310b1099 100644 --- a/indra/newview/skins/default/xui/tr/notifications.xml +++ b/indra/newview/skins/default/xui/tr/notifications.xml @@ -1207,7 +1207,7 @@ sonraki: '[THIS_GPU]' Yakınındaki baÅŸka bir bölgeye taşındınız. </notification> <notification name="AvatarMovedLast"> - Son konumunuz ÅŸu anda kullanılamıyor. + Talep ettiÄŸiniz konum ÅŸu anda kullanılamıyor. Yakınındaki baÅŸka bir bölgeye taşındınız. </notification> <notification name="AvatarMovedHome"> @@ -1223,10 +1223,10 @@ Yeni bir ana konum ayarlamak isteyebilirsiniz. </form> </notification> <notification name="FirstRun"> - [APP_NAME] kurulumu tamamlandı. + [APP_NAME] yüklemesi tamamlandı. [SECOND_LIFE]'ı ilk kez kullanıyorsanız, oturum açmadan önce bir hesap oluÅŸturmalısınız. - <usetemplate name="okcancelbuttons" notext="Devam" yestext="Yeni Hesap..."/> + <usetemplate name="okcancelbuttons" notext="Devam" yestext="Hesap OluÅŸtur..."/> </notification> <notification name="LoginPacketNeverReceived"> BaÄŸlantıda sorun yaşıyoruz. İnternet baÄŸlantınızda ya da [SECOND_LIFE_GRID] uygulamasında bir problem olabilir. @@ -3233,19 +3233,58 @@ Görünümünüzü döndürmek için dünya üzerindeki herhangi bir yeri tıkla Bu eylem tüm menü öğelerini ve düğmelerini gizler. Bunları geri almak için [SHORTCUT] üzerine tekrar tıklayın. <usetemplate ignoretext="KA'ni gizlemeden önce onayla" name="okcancelignore" notext="İptal" yestext="Tamam"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestricted"> + <notification name="PathfindingLinksets_WarnOnPhantom"> + Bazı seçili baÄŸlantı kümelerinin Fantom bayrağı dönüştürülecek. + +Devam etmek istiyor musunuz? + <usetemplate ignoretext="Bazı seçili baÄŸlantı kümelerinin fantom bayrağı dönüştürülecek." name="okcancelignore" notext="İptal" yestext="Tamam"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle '[REQUESTED_TYPE]' olarak ayarlanamıyor. Bunun yerine bu baÄŸlantı kümeleri '[RESTRICTED_TYPE]' olarak ayarlanacak. + +Devam etmek istiyor musunuz? <usetemplate ignoretext="Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle ayarlanamıyor." name="okcancelignore" notext="İptal" yestext="Tamam"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnVolume"> + <notification name="PathfindingLinksets_MismatchOnVolume"> Seçilen bazı baÄŸlantı kümeleri '[REQUESTED_TYPE]' olarak ayarlanamaz, çünkü ÅŸekil konveks deÄŸil. + +Devam etmek istiyor musunuz? <usetemplate ignoretext="Seçilen bazı baÄŸlantı kümeleri ayarlanamaz, çünkü ÅŸekil konveks deÄŸil." name="okcancelignore" notext="İptal" yestext="Tamam"/> </notification> - <notification name="PathfindingLinksets_SetLinksetUseMismatchOnRestrictedAndVolume"> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + Bazı seçili baÄŸlantı kümelerinin Fantom bayrağı dönüştürülecek. + +Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle '[REQUESTED_TYPE]' olarak ayarlanamıyor. Bunun yerine bu baÄŸlantı kümeleri '[RESTRICTED_TYPE]' olarak ayarlanacak. + +Devam etmek istiyor musunuz? + <usetemplate ignoretext="Bazı seçili baÄŸlantı kümelerinin fantom bayrağı dönüştürülecek, diÄŸerleri ise baÄŸlantı kümesi için izin kısıtlamaları nedeniyle ayarlanamıyor." name="okcancelignore" notext="İptal" yestext="Tamam"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + Bazı seçili baÄŸlantı kümelerinin Fantom bayrağı dönüştürülecek. + +Seçilen bazı baÄŸlantı kümeleri '[REQUESTED_TYPE]' olarak ayarlanamaz, çünkü ÅŸekil konveks deÄŸil. + +Devam etmek istiyor musunuz? + <usetemplate ignoretext="Bazı seçili baÄŸlantı kümelerinin fantom bayrağı dönüştürülecek, diÄŸerleri ise ÅŸekil konveks olmadığı için ayarlanamıyor" name="okcancelignore" notext="İptal" yestext="Tamam"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle '[REQUESTED_TYPE]' olarak ayarlanamıyor. Bunun yerine bu baÄŸlantı kümeleri '[RESTRICTED_TYPE]' olarak ayarlanacak. - Seçilen bazı baÄŸlantı kümeleri '[REQUESTED_TYPE]' olarak ayarlanamaz, çünkü ÅŸekil konveks deÄŸil. Bu baÄŸlantı kümelerinin kullanım tipleri deÄŸiÅŸmez. + +Seçilen bazı baÄŸlantı kümeleri '[REQUESTED_TYPE]' olarak ayarlanamaz, çünkü ÅŸekil konveks deÄŸil. Bu baÄŸlantı kümelerinin kullanım tipleri deÄŸiÅŸmez. + +Devam etmek istiyor musunuz? <usetemplate ignoretext="Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle ve ÅŸekil konveks olmadığı için ayarlanamıyor." name="okcancelignore" notext="İptal" yestext="Tamam"/> </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + Bazı seçili baÄŸlantı kümelerinin Fantom bayrağı dönüştürülecek. + +Seçilen bazı baÄŸlantı kümeleri, baÄŸlantı kümesi için izin kısıtlamaları nedeniyle '[REQUESTED_TYPE]' olarak ayarlanamıyor. Bunun yerine bu baÄŸlantı kümeleri '[RESTRICTED_TYPE]' olarak ayarlanacak. + +Seçilen bazı baÄŸlantı kümeleri '[REQUESTED_TYPE]' olarak ayarlanamaz, çünkü ÅŸekil konveks deÄŸil. Bu baÄŸlantı kümelerinin kullanım tipleri deÄŸiÅŸmez. + +Devam etmek istiyor musunuz? + <usetemplate ignoretext="Bazı seçili baÄŸlantı kümelerinin fantom bayrağı dönüştürülecek, diÄŸerleri ise baÄŸlantı kümesi için izin kısıtlamaları nedeniyle ve ÅŸekil konveks olmadığı için ayarlanamıyor." name="okcancelignore" notext="İptal" yestext="Tamam"/> + </notification> <notification name="PathfindingLinksets_ChangeToFlexiblePath"> Seçilen nesne navigasyon örgüsünü etkiliyor. Bunu bir Esnek Yol olarak deÄŸiÅŸtirirseniz, navigasyon örgüsünden çıkartmış olursunuz. <usetemplate ignoretext="Seçilen nesne navigasyon örgüsünü etkiliyor. Bunu bir Esnek Yol olarak deÄŸiÅŸtirirseniz, navigasyon örgüsünden çıkartmış olursunuz." name="okcancelignore" notext="İptal" yestext="Tamam"/> diff --git a/indra/newview/skins/default/xui/tr/panel_login.xml b/indra/newview/skins/default/xui/tr/panel_login.xml index 15684ba402836c14c46fce1cca2ab35d2f124b9d..28d316e46b64799f5d2ecc8123097f74b370f82b 100644 --- a/indra/newview/skins/default/xui/tr/panel_login.xml +++ b/indra/newview/skins/default/xui/tr/panel_login.xml @@ -38,9 +38,9 @@ </layout_panel> <layout_panel name="links"> <text name="create_account_text"> - CREATE YǾUR ACCǾUNT + HESABINIZI OLUÅžTURUN </text> - <button name="create_new_account_btn" label="Kaydolun"/> + <button label="Åžimdi baÅŸla" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/tr/panel_region_debug.xml b/indra/newview/skins/default/xui/tr/panel_region_debug.xml index 3f101a1824af026b3898b4427470cca4d372d0bc..834ece563f9f6ffcc35db634485cec92f6032e03 100644 --- a/indra/newview/skins/default/xui/tr/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/tr/panel_region_debug.xml @@ -30,5 +30,5 @@ <button label="En Çok Çarpışanlar..." name="top_colliders_btn" tool_tip="En çok potansiyel çarpışma yaÅŸayan nesnelerin listesi"/> <button label="En Çok Komut Dsy. ÇalÅŸtr...." name="top_scripts_btn" tool_tip="Komut dosyalarını çalıştırırken en çok zaman harcayan nesnelerin listesi"/> <button label="Bölgeyi Yeniden BaÅŸlat" name="restart_btn" tool_tip="2 dakikalık bir geri sayımdan sonra bölgeyi yeniden baÅŸlat"/> - <button label="Yeniden BaÅŸlatmayı İptal Et" name="cancel_restart_btn" tool_tip="Bölgenin yeniden baÅŸlatılmasını 1 saat ertele"/> + <button label="Yeniden BaÅŸlatmayı İptal Et" name="cancel_restart_btn" tool_tip="Bölge yeniden baÅŸlatmasını iptal et"/> </panel> diff --git a/indra/newview/skins/default/xui/tr/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/tr/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..0c8c7b68b5c1f3680912a1ec1947b4a2a62e0e84 --- /dev/null +++ b/indra/newview/skins/default/xui/tr/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="Ana" name="System Volume"/> + <slider label="Düğmeler" name="UI Volume"/> + <slider label="Ortam" name="Wind Volume"/> + <slider label="Sesler" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="Mimiklerdeki sesleri etkinleÅŸtir"/> + <slider label="Müzik" name="Music Volume"/> + <check_box name="enable_music" tool_tip="Müzik Akışını EtkinleÅŸtir"/> + <slider label="Ortam" name="Media Volume"/> + <check_box name="enable_media" tool_tip="Akış Ortamını EtkinleÅŸtir"/> + <slider label="Ses" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="Sesli Sohbeti EtkinleÅŸtir"/> +</panel> diff --git a/indra/newview/skins/default/xui/tr/strings.xml b/indra/newview/skins/default/xui/tr/strings.xml index eed65f8c7575733bfc517bbedb9bb9adb8e33d43..a35e1de7216bcdddf79653ebbab37f6457d42b15 100644 --- a/indra/newview/skins/default/xui/tr/strings.xml +++ b/indra/newview/skins/default/xui/tr/strings.xml @@ -1411,6 +1411,12 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. <string name="InvFolder favorite"> Favorilerim </string> + <string name="InvFolder Favorites"> + Sık Kullanılanlarım + </string> + <string name="InvFolder favorites"> + Sık Kullanılanlarım + </string> <string name="InvFolder Current Outfit"> Mevcut Dış Görünüm </string> @@ -1426,6 +1432,12 @@ Lütfen bir dakika içerisinde tekrar oturum açmayı deneyin. <string name="InvFolder Meshes"> Örgüler </string> + <string name="InvFolder Received Items"> + Alınan Öğeler + </string> + <string name="InvFolder Merchant Outbox"> + Satıcı Giden Kutusu + </string> <string name="InvFolder Friends"> ArkadaÅŸlar </string> diff --git a/indra/newview/skins/default/xui/zh/floater_about.xml b/indra/newview/skins/default/xui/zh/floater_about.xml index 48a417ba46bc2a58b75cc361246fa48b159b7a62..643881e416e06e5fa9924085ccb4e01f152fc5e6 100644 --- a/indra/newview/skins/default/xui/zh/floater_about.xml +++ b/indra/newview/skins/default/xui/zh/floater_about.xml @@ -10,7 +10,7 @@ <floater.string name="AboutPosition"> ä½ çš„æ–¹ä½æ˜¯ [POSITION_0,number,1], [POSITION_1,number,1], [POSITION_2,number,1],地å€å:[REGION],主機:<nolink>[HOSTNAME]</nolink> ([HOSTIP]) [SERVER_VERSION] -[[SERVER_RELEASE_NOTES_URL] [ReleaseNotes]] +[SERVER_RELEASE_NOTES_URL] </floater.string> <floater.string name="AboutSystem"> CPU:[CPU] @@ -37,51 +37,60 @@ Qt Webkit 版本: [QT_WEBKIT_VERSION] <floater.string name="AboutTraffic"> å°åŒ…æå¤±ï¼š[PACKETS_LOST,number,0]/[PACKETS_IN,number,0] ([PACKETS_PCT,number,1]%) </floater.string> + <floater.string name="ErrorFetchingServerReleaseNotesURL"> + æ“·å–伺æœå™¨ç‰ˆæœ¬èªªæ˜Ž URL 時出錯。 + </floater.string> <tab_container name="about_tab"> <panel label="資訊" name="support_panel"> <button label="覆製到剪貼簿" name="copy_btn"/> </panel> <panel label="貸記" name="credits_panel"> - <text_editor name="credits_editor"> - 「第二人生ã€ç”±â€¦â€¦ç‰å¤šäººå”力開發。 - -感è¬ä¸‹åˆ—居民相助,我們得以推出如æ¤å®Œç¾Žçš„ç‰ˆæœ¬ï¼šâ€¦â€¦ã€‚è¦æ„Ÿè¬çš„人還很多,æ—ç¹ä¸åŠå‚™è¼‰ã€‚ - - - - -「任務永續,目標常在,希望長å˜ï¼Œå¤¢æƒ³è¬å¤ä¸æœ½ã€‚ã€â€”—愛德è¯ãƒ»ç”˜è¿ºè¿ª + <text name="linden_intro"> + 「第二人生ã€ç”±ä»¥ä¸‹çš„ Linden å®¶æ—å¸¶çµ¦ä½ ï¼š + </text> + <text name="contrib_intro"> + 這些人士åšäº†é–‹æ”¾æºç¢¼çš„è²¢ç»ï¼š + </text> + <text_editor name="contrib_names"> + 執行時期被å–代的å‡å稱 + </text_editor> + <text name="trans_intro"> + 以下人士æä¾›ç¿»è¯ï¼š + </text> + <text_editor name="trans_names"> + 執行時期被å–代的å‡å稱 </text_editor> </panel> <panel label="許å¯" name="licenses_panel"> <text_editor name="credits_editor"> - 3Dconnexion SDK Copyright (C) 1992-2007 3Dconnexion -APR Copyright (C) 2000-2004 The Apache Software Foundation -Collada DOM Copyright 2005 Sony Computer Entertainment Inc. -cURL Copyright (C) 1996-2002, Daniel Stenberg, (daniel@haxx.se) -DBus/dbus-glib Copyright (C) 2002, 2003 CodeFactory AB / Copyright (C) 2003, 2004 Red Hat, Inc. -expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd. -FreeType Copyright (C) 1996-2002, The FreeType Project (www.freetype.org). -GL Copyright (C) 1999-2004 Brian Paul. -GLOD Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia. -google-perftools Copyright (c) 2005, Google Inc. -Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited. -jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW) -jpeglib Copyright (C) 1991-1998, Thomas G. Lane. -ogg/vorbis Copyright (C) 2001, Xiphophorus -OpenSSL Copyright (C) 1998-2002 The OpenSSL Project. -PCRE Copyright (c) 1997-2008 University of Cambridge -SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga -SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) -xmlrpc-epi Copyright (C) 2000 Epinions, Inc. -zlib Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler. -google-perftools Copyright (c) 2005, Google Inc. + 3Dconnexion SDK Copyright (C) 1992-2009 3Dconnexion + APR Copyright (C) 2011 The Apache Software Foundation + Collada DOM Copyright 2006 Sony Computer Entertainment Inc. + cURL Copyright (C) 1996-2010, Daniel Stenberg, (daniel@haxx.se) + DBus/dbus-glib Copyright (C) 2002, 2003 CodeFactory AB / Copyright (C) 2003, 2004 Red Hat, Inc. + expat Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd. + FreeType Copyright (C) 1996-2002, 2006 David Turner, Robert Wilhelm, and Werner Lemberg. + GL Copyright (C) 1999-2004 Brian Paul. + GLOD Copyright (C) 2003-04 Jonathan Cohen, Nat Duca, Chris Niski, Johns Hopkins University and David Luebke, Brenden Schubert, University of Virginia. + google-perftools Copyright (c) 2005, Google Inc. + Havok.com(TM) Copyright (C) 1999-2001, Telekinesys Research Limited. + jpeg2000 Copyright (C) 2001, David Taubman, The University of New South Wales (UNSW) + jpeglib Copyright (C) 1991-1998, Thomas G. Lane. + ogg/vorbis Copyright (C) 2002, Xiphophorus + OpenSSL Copyright (C) 1998-2008 The OpenSSL Project. + PCRE Copyright (c) 1997-2012 University of Cambridge + SDL Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga + SSLeay Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + xmlrpc-epi Copyright (C) 2000 Epinions, Inc. + zlib Copyright (C) 1995-2012 Jean-loup Gailly and Mark Adler. + + 第二人生 Viewer 採用 Havok (TM) 物ç†å¼•擎。 (c)Copyright 1999-2010 Havok.com Inc.(åŠå…¶æ”¾ç…§äººï¼‰ã€‚ ä¿ç•™ä¸€åˆ‡æ¬Šåˆ©ã€‚ 詳情見 www.havok.com。 -第二人生 Viewer 採用 Havok (TM) 物ç†å¼•擎。 (c)Copyright 1999-2010 Havok.com Inc.(åŠå…¶æ”¾ç…§äººï¼‰ã€‚ ä¿ç•™ä¸€åˆ‡æ¬Šåˆ©ã€‚ 詳情見 www.havok.com。 + æœ¬è»Ÿé«”å«æœ‰ NVIDIA Corporation æä¾›çš„æºç¨‹å¼ç¢¼ã€‚ -ä¿ç•™ä¸€åˆ‡æ¬Šåˆ©ã€‚ 詳情見 licenses.txt。 + ä¿ç•™ä¸€åˆ‡æ¬Šåˆ©ã€‚ 詳情見 licenses.txt。 -語音èŠå¤©éŸ³é »æŠ€è¡“:Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C) + 語音èŠå¤©éŸ³é »æŠ€è¡“:Polycom(R) Siren14(TM) (ITU-T Rec. G.722.1 Annex C) </text_editor> </panel> </tab_container> diff --git a/indra/newview/skins/default/xui/zh/floater_about_land.xml b/indra/newview/skins/default/xui/zh/floater_about_land.xml index 9dbc7dbdd76fae7458f69183d5df624708a3f94a..76db62195102a830468b7e47209a0e5873239c9f 100644 --- a/indra/newview/skins/default/xui/zh/floater_about_land.xml +++ b/indra/newview/skins/default/xui/zh/floater_about_land.xml @@ -129,15 +129,15 @@ æµé‡ï¼š </text> <text name="DwellText"> - 0 + 載入ä¸... </text> <button label="購買土地" name="Buy Land..."/> + <button label="Linden 出售" name="Linden Sale..." tool_tip="åœŸåœ°å¿…é ˆæœ‰äººæ“æœ‰ã€å·²è¨æœ‰å…§å®¹ï¼Œä¸¦ä¸”ä¸åœ¨æ‹è³£ä¸ã€‚"/> <button label="腳本資訊" name="Scripts..."/> <button label="為群組購買" name="Buy For Group..."/> <button label="購買通行權" name="Buy Pass..." tool_tip="通行權å…è¨±ä½ æš«æ™‚å¯å‡ºå…¥é€™å¡ŠåœŸåœ°ã€‚"/> <button label="放棄土地" name="Abandon Land..."/> <button label="收回土地" name="Reclaim Land..."/> - <button label="Linden 出售" name="Linden Sale..." tool_tip="åœŸåœ°å¿…é ˆæœ‰äººæ“æœ‰ã€å·²è¨æœ‰å…§å®¹ï¼Œä¸¦ä¸”ä¸åœ¨æ‹è³£ä¸ã€‚"/> </panel> <panel label="契約" name="land_covenant_panel"> <panel.string name="can_resell"> @@ -212,19 +212,19 @@ 地å€ç‰©ä»¶è² è·å€æ•¸ï¼š[BONUS] </text> <text name="Simulator primitive usage:"> - 幾何元件使用: + 地å€å®¹ç´é‡ï¼š </text> <text name="objects_available"> 使用 [MAX] ä¸çš„ [COUNT] (剩餘 [AVAILABLE] å¯ç”¨ï¼‰ </text> <text name="Primitives parcel supports:"> - 地段所æä¾›çš„幾何元件數: + 地段土地容ç´é‡ï¼š </text> <text name="object_contrib_text"> [COUNT] </text> <text name="Primitives on parcel:"> - 地段上的幾何元件數: + åœ°æ®µåœŸåœ°è¡æ“Šé‡ï¼š </text> <text name="total_objects_text"> [COUNT] @@ -309,8 +309,10 @@ <text name="allow_label"> å…許其他居民去: </text> - <check_box label="編輯地形" name="edit land check" tool_tip="如果勾é¸ï¼Œä»»ä½•人皆å¯ä½¿ä½ 的土地變形。 最好ä¸å‹¾é¸ï¼Œå› ç‚ºä½ éš¨æ™‚å¯ä»¥è‡ªè¡Œç·¨è¼¯ä½ 自己的土地。"/> - <check_box label="飛行" name="check fly" tool_tip="如果勾é¸ï¼Œå±…æ°‘å¯åœ¨ä½ 土地上飛行。 如果ä¸å‹¾é¸ï¼Œå±…民僅å¯é£›è¶Šä½ 土地。"/> + <text name="allow_label0"> + 飛行: + </text> + <check_box label="任何人" name="check fly" tool_tip="如果勾é¸ï¼Œå±…æ°‘å¯åœ¨ä½ 土地上飛行。 如果ä¸å‹¾é¸ï¼Œå±…民僅å¯é£›è¶Šä½ 土地。"/> <text name="allow_label2"> å»ºé€ ï¼š </text> @@ -326,9 +328,6 @@ </text> <check_box label="任何人" name="check other scripts"/> <check_box label="群組" name="check group scripts"/> - <text name="land_options_label"> - 土地é¸é …: - </text> <check_box label="安全(無傷害)" name="check safe" tool_tip="若勾é¸ï¼Œå°‡æŠŠåœŸåœ°è¨ç‚ºå®‰å…¨ï¼Œç¦çµ•傷害性的戰鬥。 若未勾é¸ï¼Œå‰‡å…許傷害性的戰鬥。"/> <check_box label="ç¦æ¢æŽ¨æ’ž" name="PushRestrictCheck" tool_tip="ç¦æ¢ä½¿ç”¨è…³æœ¬æŽ¨æ’žã€‚ 勾é¸é€™é¸é …坿œ‰æ•ˆé˜²æ¢ä½ åœŸåœ°ä¸Šå‡ºç¾æ»‹äº‹è¡Œç‚ºã€‚"/> <check_box label="將地點刊登顯示在æœå°‹ä¸ï¼ˆL$30 / æ¯é€±ï¼‰" name="ShowDirectoryCheck" tool_tip="讓其他人å¯ä»¥åœ¨æœå°‹çµæžœä¸çœ‹åˆ°é€™å¡Šåœ°æ®µ"/> @@ -368,6 +367,10 @@ 快照: </text> <texture_picker name="snapshot_ctrl" tool_tip="點按以挑é¸åœ–片"/> + <text name="allow_label5"> + 其他地段的化身å¯ä»¥çœ‹è¦‹æœ¬åœ°æ®µè£¡çš„化身,並與之交談 + </text> + <check_box label="察看化身" name="SeeAvatarsCheck" tool_tip="å…è¨±å…¶ä»–åœ°æ®µçš„åŒ–èº«çœ‹åˆ°æœ¬åœ°æ®µåŒ…æ‹¬ä½ åœ¨å…§çš„åŒ–èº«ï¼Œä¸¦å¯äº’相交談。"/> <text name="landing_point"> 登陸點:[LANDING] </text> @@ -424,6 +427,11 @@ è²éŸ³ï¼š </text> <check_box label="將姿勢和物件的è²éŸ³é™åˆ¶æ–¼æ¤åœ°æ®µ" name="check sound local"/> + <text name="Avatar Sounds:"> + 化身è²éŸ³ï¼š + </text> + <check_box label="任何人" name="all av sound check"/> + <check_box label="群組" name="group av sound check"/> <text name="Voice settings:"> 語音: </text> @@ -435,20 +443,15 @@ <panel.string name="access_estate_defined"> ï¼ˆç”±é ˜åœ°å®šç¾©ï¼‰ </panel.string> - <panel.string name="allow_public_access"> - å…許公開出入([MATURITY])(注æ„:若未勾é¸ï¼Œå°‡è¨ç«‹ç¦è¶Šç·šï¼‰ - </panel.string> <panel.string name="estate_override"> 至少一個é¸é …åœ¨é ˜åœ°çš„å±¤ç´šè¨å®š </panel.string> - <text name="Limit access to this parcel to:"> - æ¤åœ°æ®µå‡ºå…¥æ¬Š - </text> + <check_box label="å…許公開出入(若未勾é¸ï¼Œå°‡è¨ç«‹ç¦è¶Šç·šï¼‰" name="public_access"/> <text name="Only Allow"> - 僅å…許經éŽå¦‚下驗è‰çš„居民出入: + 僅å…許符åˆä»¥ä¸‹æ¢ä»¶çš„居民進入: </text> - <check_box label="é 留付款資料 [ESTATE_PAYMENT_LIMIT]" name="limit_payment" tool_tip="ç¦çµ•èº«ä»½ä¸æ˜Žå±…民。"/> - <check_box label="å¹´é½¡é©—è‰ [ESTATE_AGE_LIMIT]" name="limit_age_verified" tool_tip="ç¦æ¢å°šæœªé©—è‰å¹´é½¡çš„居民。 åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> + <check_box label="å·²é 留付款資料 [ESTATE_PAYMENT_LIMIT]" name="limit_payment" tool_tip="å±…æ°‘å¿…é ˆæä¾›ä»˜æ¬¾è³‡æ–™æ‰èƒ½é€²å…¥é€™åœ°æ®µã€‚ åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> + <check_box label="年滿 18 æ² [ESTATE_AGE_LIMIT]" name="limit_age_verified" tool_tip="å±…æ°‘å¿…é ˆå¹´æ»¿ 18 æ²æ‰èƒ½é€²å…¥é€™åœ°æ®µã€‚ åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> <check_box label="å…許出入的群組:[GROUP]" name="GroupCheck" tool_tip="è¨å®šç¾¤çµ„於一般é 籤。"/> <check_box label="出售通行權給:" name="PassCheck" tool_tip="å…許暫時出入這個地段"/> <combo_box name="pass_combo"> diff --git a/indra/newview/skins/default/xui/zh/floater_animation_anim_preview.xml b/indra/newview/skins/default/xui/zh/floater_animation_anim_preview.xml new file mode 100644 index 0000000000000000000000000000000000000000..76cb9079c49ff6dd069b9522d3fe4fdd81261ad8 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_animation_anim_preview.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Anim Preview" title="ANIMATION.ANIM"> + <text name="name_label"> + å稱: + </text> + <text name="description_label"> + æè¿°ï¼š + </text> + <button label="上傳(L$[AMOUNT])" name="ok_btn"/> + <button label="å–æ¶ˆ" label_selected="å–æ¶ˆ" name="cancel_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_animation_bvh_preview.xml b/indra/newview/skins/default/xui/zh/floater_animation_bvh_preview.xml new file mode 100644 index 0000000000000000000000000000000000000000..ffb0de8a688adf4b6efacf8cedf416f856def5e5 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_animation_bvh_preview.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Animation Preview"> + <floater.string name="failed_to_initialize"> + 動作åˆå§‹åŒ–失敗 + </floater.string> + <floater.string name="anim_too_long"> + 動作檔長度為 [LENGTH] 秒。 + +動作檔長度最多å¯ç‚º [MAX_LENGTH] 秒。 + </floater.string> + <floater.string name="failed_file_read"> + 無法讀å–動作檔。 + +[STATUS] + </floater.string> + <floater.string name="E_ST_OK"> + 確定 + </floater.string> + <floater.string name="E_ST_EOF"> + 檔案çµå°¾ä¸æ£å¸¸ã€‚ + </floater.string> + <floater.string name="E_ST_NO_CONSTRAINT"> + 無法讀å–ç´„æŸå®šç¾©ã€‚ + </floater.string> + <floater.string name="E_ST_NO_FILE"> + 無法開啟 BVH 檔案。 + </floater.string> + <floater.string name="E_ST_NO_HIER"> + HIERARCHY 檔é 無效。 + </floater.string> + <floater.string name="E_ST_NO_JOINT"> + 找ä¸åˆ° ROOT 或 JOINT。 + </floater.string> + <floater.string name="E_ST_NO_NAME"> + 無法å–å¾— JOINT å稱。 + </floater.string> + <floater.string name="E_ST_NO_OFFSET"> + 無法尋找ä½ç§»ã€‚ + </floater.string> + <floater.string name="E_ST_NO_CHANNELS"> + 找ä¸åˆ°é »é“。 + </floater.string> + <floater.string name="E_ST_NO_ROTATION"> + 無法å–得旋轉åºã€‚ + </floater.string> + <floater.string name="E_ST_NO_AXIS"> + 無法å–得旋轉軸。 + </floater.string> + <floater.string name="E_ST_NO_MOTION"> + 找ä¸åˆ°å‹•作。 + </floater.string> + <floater.string name="E_ST_NO_FRAMES"> + 無法å–得幀數。 + </floater.string> + <floater.string name="E_ST_NO_FRAME_TIME"> + 無法å–得幀時間。 + </floater.string> + <floater.string name="E_ST_NO_POS"> + 無法å–å¾—ä½ç½®å€¼ã€‚ + </floater.string> + <floater.string name="E_ST_NO_ROT"> + 無法å–得旋轉值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_FILE"> + 無法開啟平移檔案。 + </floater.string> + <floater.string name="E_ST_NO_XLT_HEADER"> + 無法讀å–平移檔é 。 + </floater.string> + <floater.string name="E_ST_NO_XLT_NAME"> + 無法讀å–平移å稱。 + </floater.string> + <floater.string name="E_ST_NO_XLT_IGNORE"> + 無法讀å–平移忽略值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_RELATIVE"> + 無法讀å–平移相å°å€¼ã€‚ + </floater.string> + <floater.string name="E_ST_NO_XLT_OUTNAME"> + 無法讀å–平移輸出å稱。 + </floater.string> + <floater.string name="E_ST_NO_XLT_MATRIX"> + 無法讀å–平移矩陣。 + </floater.string> + <floater.string name="E_ST_NO_XLT_MERGECHILD"> + 無法å–å¾— mergechild å稱。 + </floater.string> + <floater.string name="E_ST_NO_XLT_MERGEPARENT"> + 無法å–å¾— mergeparent å稱。 + </floater.string> + <floater.string name="E_ST_NO_XLT_PRIORITY"> + 無法å–得優先值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_LOOP"> + 無法å–得迴圈值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_EASEIN"> + 無法å–å¾— easeIn 值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_EASEOUT"> + 無法å–å¾— easeOut 值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_HAND"> + 無法å–å¾— hand morph 值。 + </floater.string> + <floater.string name="E_ST_NO_XLT_EMOTE"> + 無法讀å–表情符號å稱。 + </floater.string> + <floater.string name="E_ST_BAD_ROOT"> + root joint åç¨±ä¸æ£ç¢ºï¼Œä½¿ç”¨ "hip"。 + </floater.string> + <text name="name_label"> + å稱: + </text> + <text name="description_label"> + æè¿°ï¼š + </text> + <spinner label="優先度" name="priority" tool_tip="其他動作的控制å¯è¢«æ¤å‹•作強行å–代"/> + <check_box label="連續" name="loop_check" tool_tip="讓æ¤å‹•ä½œä¸æ–·é‡è¦†æ¼”ç¹¹"/> + <spinner label="å…¥(%)" name="loop_in_point" tool_tip="è¨å®šå‹•作ä¸é‡è¦†æ¼”繹的起始點"/> + <spinner label="出(%)" name="loop_out_point" tool_tip="è¨å®šå‹•作ä¸é‡è¦†æ¼”ç¹¹çš„çµæŸé»ž"/> + <text name="hand_label"> + 手部姿勢 + </text> + <combo_box name="hand_pose_combo" tool_tip="控制動作演繹時雙手的姿勢"> + <combo_box.item label="張開" name="Spread"/> + <combo_box.item label="放鬆" name="Relaxed"/> + <combo_box.item label="雙手伸出指é " name="PointBoth"/> + <combo_box.item label="拳é " name="Fist"/> + <combo_box.item label="左邊放鬆" name="RelaxedLeft"/> + <combo_box.item label="左邊伸指" name="PointLeft"/> + <combo_box.item label="å·¦é‚Šæ¡æ‹³" name="FistLeft"/> + <combo_box.item label="å³é‚Šæ”¾é¬†" name="RelaxedRight"/> + <combo_box.item label="å³é‚Šä¼¸æŒ‡" name="PointRight"/> + <combo_box.item label="å³é‚Šæ¡æ‹³" name="FistRight"/> + <combo_box.item label="峿‰‹æ•¬ç¦®" name="SaluteRight"/> + <combo_box.item label="打å—" name="Typing"/> + <combo_box.item label="峿‰‹æ¯”出和平手勢" name="PeaceRight"/> + </combo_box> + <text name="emote_label"> + 表情 + </text> + <combo_box name="emote_combo" tool_tip="控制動作演繹時臉部的姿態"> + <item label="(無)" name="[None]" value=""/> + <item label="å¼µå£å¶å–Šè²Œ" name="Aaaaah" value="å¼µå£å¶å–Šè²Œ"/> + <item label="害怕" name="Afraid" value="害怕"/> + <item label="生氣" name="Angry" value="生氣"/> + <item label="燦爛笑容" name="BigSmile" value="燦爛笑容"/> + <item label="ç„¡èŠ" name="Bored" value="ç„¡èŠ"/> + <item label="哿³£" name="Cry" value="哿³£"/> + <item label="鄙視" name="Disdain" value="鄙視"/> + <item label="å°·å°¬" name="Embarrassed" value="å°·å°¬"/> + <item label="皺眉" name="Frown" value="皺眉"/> + <item label="親å»" name="Kiss" value="親å»"/> + <item label="笑" name="Laugh" value="笑"/> + <item label="嫌惡貌" name="Plllppt" value="嫌惡貌"/> + <item label="作å™" name="Repulsed" value="作å™"/> + <item label="傷心" name="Sad" value="傷心"/> + <item label="è³è³è‚©" name="Shrug" value="è³è³è‚©"/> + <item label="微笑" name="Smile" value="微笑"/> + <item label="驚喜" name="Surprise" value="驚喜"/> + <item label="眨眼" name="Wink" value="眨眼"/> + <item label="擔心" name="Worry" value="擔心"/> + </combo_box> + <text name="preview_label"> + é 覽… + </text> + <combo_box name="preview_base_anim" tool_tip="ç”¨é€™å€‹ä¾†æ¸¬è©¦ä½ çš„åŒ–èº«å¾žä¸€èˆ¬å‹•ä½œè½‰å…¥å‹•ä½œæ¼”ç¹¹æ™‚çš„æƒ…æ³ã€‚"> + <item label="站立" name="Standing" value="站立"/> + <item label="æ¥è¡Œä¸" name="Walking" value="æ¥è¡Œä¸"/> + <item label="åè‘—" name="Sitting" value="åè‘—"/> + <item label="飛行" name="Flying" value="飛行"/> + </combo_box> + <spinner label="淡入(秒)" name="ease_in_time" tool_tip="動作攙混æ¤å…¥çš„æ™‚間長度(秒)"/> + <spinner label="淡出(秒)" name="ease_out_time" tool_tip="動作攙混淡出的時間長度(秒)"/> + <button name="play_btn" tool_tip="æ’æ”¾ä½ 的動作"/> + <button name="pause_btn" tool_tip="æš«åœä½ 的動åš"/> + <button name="stop_btn" tool_tip="åœæ¢æ’放動作"/> + <text name="bad_animation_text"> + 無法讀å–動作檔。 + +æˆ‘å€‘å»ºè°æŽ¡ç”¨ç”± Poser 4 匯出的 BVH æª”æ¡ˆæ ¼å¼ã€‚ + </text> + <button label="上傳(L$[AMOUNT])" name="ok_btn"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_autoreplace.xml b/indra/newview/skins/default/xui/zh/floater_autoreplace.xml new file mode 100644 index 0000000000000000000000000000000000000000..4ee07e629594f92202674d3c3698bbb0c7e4819a --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_autoreplace.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="autoreplace_floater" title="自動å–代è¨å®š"> + <check_box label="啟用自動å–代" name="autoreplace_enable" tool_tip="在輸入èŠå¤©å…§å®¹çš„åŒæ™‚,將輸入的關éµå—代æ›ç‚ºç›¸æ‡‰çš„å–代文å—。"/> + <button label="匯入清單…" name="autoreplace_import_list" tool_tip="從檔案載入先å‰åŒ¯å‡ºéŽçš„æ¸…單。"/> + <button label="匯出清單…" name="autoreplace_export_list" tool_tip="å°‡æ‰€é¸æ¸…單儲å˜åˆ°æª”案以便和他人分享。"/> + <button label="新的清單…" name="autoreplace_new_list" tool_tip="新建一個清單。"/> + <button label="刪除清單" name="autoreplace_delete_list" tool_tip="åˆªé™¤æ‰€é¸æ¸…單。"/> + <button name="autoreplace_list_up" tool_tip="æé«˜æ¤æ¸…單的優先次åºã€‚"/> + <button name="autoreplace_list_down" tool_tip="é™ä½Žæ¤æ¸…單的優先次åºã€‚"/> + <scroll_list name="autoreplace_list_replacements"> + <scroll_list.columns label="é—œéµå—" name="keyword"/> + <scroll_list.columns label="å–代文å—" name="replacement"/> + </scroll_list> + <button label="æ·»åŠ ..." name="autoreplace_add_entry"/> + <button label="移除" name="autoreplace_delete_entry"/> + <button label="儲å˜é …ç›®" name="autoreplace_save_entry" tool_tip="å„²å˜æ¤é …目。"/> + <button label="儲å˜è®Šæ›´" name="autoreplace_save_changes" tool_tip="å„²å˜æ‰€æœ‰è®Šæ›´ã€‚"/> + <button label="å–æ¶ˆ" name="autoreplace_cancel" tool_tip="放棄所有變更。"/> +</floater> +<!-- + <text + top_pad="10" + left="10" + height="16" + width="260" + follows="left|top" + halign="center" + mouse_opaque="true" + name="autoreplace_text2"> + Entries + </text> +--> diff --git a/indra/newview/skins/default/xui/zh/floater_avatar.xml b/indra/newview/skins/default/xui/zh/floater_avatar.xml new file mode 100644 index 0000000000000000000000000000000000000000..55b1a95a410a3043c48dce130e5fe034c7f62133 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_avatar.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Avatar" title="鏿“‡ä¸€å€‹åŒ–身"/> diff --git a/indra/newview/skins/default/xui/zh/floater_build_options.xml b/indra/newview/skins/default/xui/zh/floater_build_options.xml index b37cbdaa33c3ba26a91ae35fc1f128c71423a555..29f36b461c9cd08468e04bd0fded9826594f6597 100644 --- a/indra/newview/skins/default/xui/zh/floater_build_options.xml +++ b/indra/newview/skins/default/xui/zh/floater_build_options.xml @@ -1,7 +1,30 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater name="build options floater" title="æ ¼ç·šé¸é …"> - <spinner label="æ ¼ç·šå–®ä½ï¼ˆå…¬å°ºï¼‰" name="GridResolution"/> - <spinner label="æ ¼ç·šå»¶ä¼¸ç¯„åœï¼ˆå…¬å°ºï¼‰" name="GridDrawSize"/> + <floater.string name="grid_screen_text"> + 螢幕 + </floater.string> + <floater.string name="grid_local_text"> + 本地 + </floater.string> + <floater.string name="grid_world_text"> + 世界 + </floater.string> + <floater.string name="grid_reference_text"> + åƒè€ƒ + </floater.string> + <floater.string name="grid_attachment_text"> + 附件 + </floater.string> + <text name="grid_mode_label" tool_tip="æ ¼ç·šä¸é€æ˜Žåº¦"> + æ¨¡å¼ + </text> + <combo_box name="combobox grid mode" tool_tip="鏿“‡ç‰©ä»¶å®šä½åƒè€ƒçš„æ ¼ç·šå°ºåº¦é¡žåž‹"> + <combo_box.item label="ä¸–ç•Œæ ¼ç·š" name="World"/> + <combo_box.item label="åœ°æ–¹æ ¼ç·š" name="Local"/> + <combo_box.item label="åƒè€ƒæ ¼ç·š" name="Reference"/> + </combo_box> + <spinner label="å–®ä½ï¼ˆå…¬å°ºï¼‰" name="GridResolution"/> + <spinner label="範åœï¼ˆå…¬å°ºï¼‰" name="GridDrawSize"/> <check_box label="貼齊至åå–®ä½" name="GridSubUnit"/> <check_box label="檢視橫剖é¢" name="GridCrossSection"/> <text name="grid_opacity_label" tool_tip="æ ¼ç·šä¸é€æ˜Žåº¦"> diff --git a/indra/newview/skins/default/xui/zh/floater_buy_currency.xml b/indra/newview/skins/default/xui/zh/floater_buy_currency.xml index c2cfe5c8808859c81c3cc0ad8fa8d07aa8c8fe5c..fcf2800728d445708a039bac7e13ef01c5d8bb34 100644 --- a/indra/newview/skins/default/xui/zh/floater_buy_currency.xml +++ b/indra/newview/skins/default/xui/zh/floater_buy_currency.xml @@ -46,7 +46,7 @@ L$ [AMT] </text> <text name="currency_links"> - [http://www.secondlife.com/my/account/payment_method_management.php 付費方法] | [http://www.secondlife.com/my/account/currency.php 幣種] | [http://www.secondlife.com/my/account/exchange_rates.php 匯率] + [http://www.secondlife.com/my/account/payment_method_management.php 付費方å¼] | [http://www.secondlife.com/my/account/currency.php 幣種] </text> <text name="exchange_rate_note"> 釿–°è¼¸å…¥é‡‘é¡å³å¯å¯Ÿçœ‹æœ€æ–°çš„匯率。 diff --git a/indra/newview/skins/default/xui/zh/floater_camera.xml b/indra/newview/skins/default/xui/zh/floater_camera.xml index 4319d96e89a605ec6763b7def4187fd7fd0612a7..becb7b9546ce1c4ff4891f3f1df9c9e09e2a299c 100644 --- a/indra/newview/skins/default/xui/zh/floater_camera.xml +++ b/indra/newview/skins/default/xui/zh/floater_camera.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="camera_floater"> +<floater name="camera_floater" title="æ”影機控制"> <floater.string name="rotate_tooltip"> 繞著焦點轉動æ”影機 </floater.string> diff --git a/indra/newview/skins/default/xui/zh/floater_chat_bar.xml b/indra/newview/skins/default/xui/zh/floater_chat_bar.xml new file mode 100644 index 0000000000000000000000000000000000000000..f1a69a76880c13e8dfbf40e9d55f0f9d1dd73bad --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_chat_bar.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="chat_bar" title="附近的èŠå¤©"> + <panel name="bottom_panel"> + <line_editor label="點按æ¤è™•é–‹å§‹èŠå¤©ã€‚" name="chat_box" tool_tip="按下 Enter éµä¾†èªªæˆ–按下 Ctrl+Enter 來喊å«"/> + <button name="show_nearby_chat" tool_tip="顯示 / éš±è— é™„è¿‘çš„èŠå¤©ç´€éŒ„"/> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_delete_env_preset.xml b/indra/newview/skins/default/xui/zh/floater_delete_env_preset.xml new file mode 100644 index 0000000000000000000000000000000000000000..4aafb319521455a5f1c6ed36c2ce5d42494ddab2 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_delete_env_preset.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<floater name="Delete Env Preset" title="刪除環境自訂é…ç½®"> + <string name="title_water"> + 刪除水的自訂é…ç½® + </string> + <string name="title_sky"> + 刪除天空自訂é…ç½® + </string> + <string name="title_day_cycle"> + 刪除日循環 + </string> + <string name="label_water"> + 自訂é…置: + </string> + <string name="label_sky"> + 自訂é…置: + </string> + <string name="label_day_cycle"> + 日循環: + </string> + <string name="msg_confirm_deletion"> + 確定è¦åˆªé™¤æ‰€é¸è‡ªè¨‚é…置? + </string> + <string name="msg_sky_is_referenced"> + 無法刪除日循環有所指涉的自訂é…置。 + </string> + <string name="combo_label"> + -鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®- + </string> + <text name="label"> + 自訂é…置: + </text> + <button label="刪除" name="delete"/> + <button label="å–æ¶ˆ" name="cancel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_destinations.xml b/indra/newview/skins/default/xui/zh/floater_destinations.xml new file mode 100644 index 0000000000000000000000000000000000000000..f50a6a631af5f24bf0d2f8b69d161622778d2472 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_destinations.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Destinations" title="目的地"/> diff --git a/indra/newview/skins/default/xui/zh/floater_edit_day_cycle.xml b/indra/newview/skins/default/xui/zh/floater_edit_day_cycle.xml new file mode 100644 index 0000000000000000000000000000000000000000..b84a4027eabf7823166b039a684478db557f1ef4 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_edit_day_cycle.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Edit Day cycle" title="編輯日循環"> + <string name="title_new"> + 新建一個日循環 + </string> + <string name="title_edit"> + 編輯日循環 + </string> + <string name="hint_new"> + 為日循環定å,調整å„é …æŽ§åˆ¶ç¢ºå®šç´°ç¯€ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="hint_edit"> + è‹¥è¦ç·¨è¼¯ä½ 的日循環,請調整下方å„é …æŽ§åˆ¶ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="combo_label"> + -鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®- + </string> + <text name="label"> + 自訂é…ç½®å稱: + </text> + <text name="note"> + 注æ„:更改自訂é…置的å稱將會新建一個自訂é…ç½®ï¼Œä¸æœƒæ”¹è®ŠåŽŸæœ‰çš„è‡ªè¨‚é…置。 + </text> + <text name="hint_item1"> + - 點按一個é 籤,編輯特定的天空è¨å®šå’Œæ™‚間。 + </text> + <text name="hint_item2"> + - 點按並拖曳å„個é 籤,å³å¯è¨å®šéŽæ¸¡æ™‚間。 + </text> + <text name="hint_item3"> + - 使用 scrubber é è¦½ä½ çš„æ—¥å¾ªç’°ã€‚ + </text> + <panel name="day_cycle_slider_panel"> + <multi_slider initial_value="0" name="WLTimeSlider"/> + <multi_slider initial_value="0" name="WLDayCycleKeys"/> + <button label="新增éµ" label_selected="新增éµ" name="WLAddKey"/> + <button label="刪除éµ" label_selected="刪除éµ" name="WLDeleteKey"/> + <text name="WL12am"> + åˆå¤œ 12 點 + </text> + <text name="WL3am"> + 凌晨 3 點 + </text> + <text name="WL6am"> + ä¸Šåˆ 6 點 + </text> + <text name="WL9amHash"> + ä¸Šåˆ 9 點 + </text> + <text name="WL12pmHash"> + ä¸åˆ 12 點 + </text> + <text name="WL3pm"> + ä¸‹åˆ 3 點 + </text> + <text name="WL6pm"> + ä¸‹åˆ 6 點 + </text> + <text name="WL9pm"> + ä¸‹åˆ 9 點 + </text> + <text name="WL12am2"> + åˆå¤œ 12 點 + </text> + <text name="WL12amHash"> + | + </text> + <text name="WL3amHash"> + I + </text> + <text name="WL6amHash"> + | + </text> + <text name="WL9amHash2"> + I + </text> + <text name="WL12pmHash2"> + | + </text> + <text name="WL3pmHash"> + I + </text> + <text name="WL6pmHash"> + | + </text> + <text name="WL9pmHash"> + I + </text> + <text name="WL12amHash2"> + | + </text> + </panel> + <text name="WLCurKeyPresetText"> + 天空è¨å®šï¼š + </text> + <combo_box label="é è¨å€¼" name="WLSkyPresets"/> + <text name="WLCurKeyTimeText"> + 時間: + </text> + <time name="time" value="ä¸Šåˆ 6 點"/> + <check_box label="æ ¹æ“šé€™è¨å®šè®Šæ›´æˆ‘的日循環" name="make_default_cb"/> + <button label="儲å˜" name="save"/> + <button label="å–æ¶ˆ" name="cancel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_edit_sky_preset.xml b/indra/newview/skins/default/xui/zh/floater_edit_sky_preset.xml new file mode 100644 index 0000000000000000000000000000000000000000..1ff832cdc46b6ae44d0f122c0181cb3ad4dbce2d --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_edit_sky_preset.xml @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Edit Sky Preset" title="編輯天空自訂é…ç½®"> + <string name="title_new"> + 建立新的天空自訂é…ç½® + </string> + <string name="title_edit"> + 編輯天空自訂é…ç½® + </string> + <string name="hint_new"> + 為自訂é…置定å,調整å„é …æŽ§åˆ¶ç¢ºå®šé…置細節,完æˆå¾Œé»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="hint_edit"> + è‹¥è¦ç·¨è¼¯ä½ 的天空自訂é…置,請調整å„é …æŽ§åˆ¶ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="combo_label"> + -鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®- + </string> + <text name="hint"> + è‹¥è¦ç·¨è¼¯ä½ 的自訂é…置,請調整å„é …æŽ§åˆ¶ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </text> + <text name="label"> + 自訂é…ç½®å稱: + </text> + <text name="note"> + 注æ„:更改自訂é…置的å稱將會新建一個自訂é…ç½®ï¼Œä¸æœƒæ”¹è®ŠåŽŸæœ‰çš„è‡ªè¨‚é…置。 + </text> + <tab_container name="WindLight Tabs"> + <panel label="大氣" name="Atmosphere"> + <text name="BHText"> + è—天水平線 + </text> + <text name="BDensText"> + 陰霾水平線 + </text> + <text name="BDensText2"> + è—天密度 + </text> + <text name="HDText"> + 陰霾密度 + </text> + <text name="DensMultText"> + 密度å€å¢ž + </text> + <text name="WLDistanceMultText"> + è·é›¢å€å¢ž + </text> + <text name="MaxAltText"> + 最大高度 + </text> + </panel> + <panel label="照明" name="Lighting"> + <text name="SLCText"> + æ—¥ï¼æœˆ é¡è‰² + </text> + <text name="WLAmbientText"> + 環境 + </text> + <text name="SunGlowText"> + å¤ªé™½å…‰è¼ + </text> + <slider label="èšç„¦" name="WLGlowB"/> + <slider label="尺寸" name="WLGlowR"/> + <text name="WLStarText"> + 星空亮度 + </text> + <text name="SceneGammaText"> + å ´æ™¯ Gamma 值 + </text> + <text name="TODText"> + æ—¥ï¼æœˆ ä½ç½® + </text> + <multi_slider initial_value="0" name="WLSunPos"/> + <text name="WL12amHash"> + | + </text> + <text name="WL6amHash"> + | + </text> + <text name="WL12pmHash2"> + | + </text> + <text name="WL6pmHash"> + | + </text> + <text name="WL12amHash2"> + | + </text> + <text name="WL12am"> + åˆå¤œ 12 點 + </text> + <text name="WL6am"> + ä¸Šåˆ 6 點 + </text> + <text name="WL12pmHash"> + ä¸åˆ 12 點 + </text> + <text name="WL6pm"> + ä¸‹åˆ 6 點 + </text> + <text name="WL12am2"> + åˆå¤œ 12 點 + </text> + <time name="WLDayTime" value="ä¸Šåˆ 6 點"/> + <text name="WLEastAngleText"> + æ±å‡è§’度 + </text> + </panel> + <panel label="雲彩" name="Clouds"> + <text name="WLCloudColorText"> + 雲彩é¡è‰² + </text> + <text name="WLCloudColorText2"> + 雲彩 XY 軸 / 密度 + </text> + <slider label="X" name="WLCloudX"/> + <slider label="Y" name="WLCloudY"/> + <slider label="D" name="WLCloudDensity"/> + <text name="WLCloudCoverageText"> + 雲彩覆蓋 + </text> + <text name="WLCloudScaleText"> + é›²å½©è¦æ¨¡ + </text> + <text name="WLCloudDetailText"> + 雲彩細節(XY 軸 / 密度) + </text> + <slider label="X" name="WLCloudDetailX"/> + <slider label="Y" name="WLCloudDetailY"/> + <slider label="D" name="WLCloudDetailDensity"/> + <text name="WLCloudScrollXText"> + 雲彩 X 滾軸 + </text> + <check_box label="鎖定" name="WLCloudLockX"/> + <text name="WLCloudScrollYText"> + 雲彩 Y 滾軸 + </text> + <check_box label="鎖定" name="WLCloudLockY"/> + </panel> + </tab_container> + <check_box label="æ ¹æ“šé€™è‡ªè¨‚é…置變更我的天空è¨å®š" name="make_default_cb"/> + <button label="儲å˜" name="save"/> + <button label="å–æ¶ˆ" name="cancel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_edit_water_preset.xml b/indra/newview/skins/default/xui/zh/floater_edit_water_preset.xml new file mode 100644 index 0000000000000000000000000000000000000000..7943866e726a9d1ae47c2df09fa00da6e739ba56 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_edit_water_preset.xml @@ -0,0 +1,72 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Edit Water Preset" title="編輯水的自訂é…ç½®"> + <string name="title_new"> + 新建水的自訂é…ç½® + </string> + <string name="title_edit"> + 編輯水的自訂é…ç½® + </string> + <string name="hint_new"> + 為自訂é…置定å,調整å„é …æŽ§åˆ¶ç¢ºå®šé…置細節,完æˆå¾Œé»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="hint_edit"> + è‹¥è¦ç·¨è¼¯æ°´çš„自訂é…置,請調整å„é …æŽ§åˆ¶ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </string> + <string name="combo_label"> + -鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®- + </string> + <text name="hint"> + è‹¥è¦ç·¨è¼¯ä½ 的自訂é…置,請調整å„é …æŽ§åˆ¶ï¼Œå†é»žæŒ‰ã€Œå„²å˜ã€ã€‚ + </text> + <text name="label"> + 自訂é…ç½®å稱: + </text> + <text name="note"> + 注æ„:更改自訂é…置的å稱將會新建一個自訂é…ç½®ï¼Œä¸æœƒæ”¹è®ŠåŽŸæœ‰çš„è‡ªè¨‚é…置。 + </text> + <panel name="panel_water_preset"> + <text name="water_color_label"> + 水霧é¡è‰² + </text> + <text name="water_fog_density_label"> + 霧密度指數 + </text> + <text name="underwater_fog_modifier_label"> + 水底霧修飾元 + </text> + <text name="BHText"> + å¤§æ³¢æµªæ–¹å‘ + </text> + <slider label="X" name="WaterWave1DirX"/> + <slider label="Y" name="WaterWave1DirY"/> + <text name="BDensText"> + åå°„åæ³¢æ¯”例 + </text> + <text name="HDText"> + è²æ¶…耳比例 + </text> + <text name="FresnelOffsetText"> + è²æ¶…耳åè· + </text> + <text name="BHText2"> + å°æ³¢æµªæ–¹å‘ + </text> + <slider label="X" name="WaterWave2DirX"/> + <slider label="Y" name="WaterWave2DirY"/> + <text name="DensMultText"> + 上折射比例 + </text> + <text name="WaterScaleBelowText"> + 下折射比例 + </text> + <text name="MaxAltText"> + æ¨¡ç³Šå€æ•¸ + </text> + <text name="BHText3"> + æ£å¸¸åœ°åœ– + </text> + </panel> + <check_box label="æ ¹æ“šé€™è‡ªè¨‚é…置變更我水的è¨å®š" name="make_default_cb"/> + <button label="儲å˜" name="save"/> + <button label="å–æ¶ˆ" name="cancel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_environment_settings.xml b/indra/newview/skins/default/xui/zh/floater_environment_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..1c6f2f936dddca0bb7be022edec890c570dd234a --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_environment_settings.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Environment Editor Floater" title="環境è¨å®š"> + <text name="note"> + 使用以下的é¸é …è‡ªè¨‚ä½ Viewer 的環境è¨å®šã€‚ + </text> + <radio_group name="region_settings_radio_group"> + <radio_item label="使用地å€è¨å®š" name="use_region_settings"/> + <radio_item label="自訂我的環境" name="use_my_settings"/> + </radio_group> + <panel name="user_environment_settings"> + <text name="note"> + 注æ„ï¼šä½ çš„è‡ªè¨‚è¨å®šä¸æœƒè¢«å…¶ä»–使用者看見。 + </text> + <text name="water_settings_title"> + æ°´çš„è¨å®š + </text> + <combo_box name="water_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + <text name="sky_dayc_settings_title"> + 天空 / 日循環 + </text> + <radio_group name="sky_dayc_settings_radio_group"> + <radio_item label="固定天空" name="my_sky_settings"/> + <radio_item label="日循環" name="my_dayc_settings"/> + </radio_group> + <combo_box name="sky_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + <combo_box name="dayc_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + </panel> + <button label="確定" name="ok_btn"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_fast_timers.xml b/indra/newview/skins/default/xui/zh/floater_fast_timers.xml new file mode 100644 index 0000000000000000000000000000000000000000..871849305c2bf20aa429e44eac26f62cc36c071a --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_fast_timers.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="fast_timers"> + <string name="pause"> + æš«åœ + </string> + <string name="run"> + è·‘æ¥ + </string> + <button label="æš«åœ" name="pause_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_hardware_settings.xml b/indra/newview/skins/default/xui/zh/floater_hardware_settings.xml index 00d7590b9b037c9429ff9828a488f3387d509859..9e3bb88ac0de76329c3ee336e453026b030336ce 100644 --- a/indra/newview/skins/default/xui/zh/floater_hardware_settings.xml +++ b/indra/newview/skins/default/xui/zh/floater_hardware_settings.xml @@ -4,7 +4,7 @@ éŽæ¿¾ï¼š </text> <check_box label="å„å‘ç•°æ€§éŽæ¿¾ï¼ˆè‹¥å•Ÿç”¨æœƒè®Šæ…¢ï¼‰" name="ani"/> - <text name="Antialiasing:"> + <text name="antialiasing label"> 消除鋸齒: </text> <combo_box label="消除鋸齒" name="fsaa"> @@ -25,6 +25,10 @@ å•Ÿç”¨é ‚é»žç·©è¡ç‰©ä»¶(VBO): </text> <check_box initial_value="true" label="啟用 OpenGL é ‚é»žç·©è¡ç‰©ä»¶(VBO)" name="vbo" tool_tip="åœ¨è¼ƒæ–°ç¡¬é«”ä¸Šå•Ÿç”¨ï¼Œå¯æå‡æ•ˆèƒ½ã€‚ 但是,較舊硬體的 VBO 實作ä¸ä½³ï¼Œè‹¥å•Ÿç”¨å¯èƒ½å°Žè‡´ç•¶æ©Ÿã€‚"/> + <text name="tc label"> + 啟用 S3TC: + </text> + <check_box initial_value="true" label="啟用æè³ªå£“ç¸®ï¼ˆé ˆé‡æ–°å•Ÿå‹•)" name="texture compression" tool_tip="在影片記憶體ä¸å£“縮æè³ªï¼Œè®“高解æžåº¦æè³ªå¯ä»¥è¼‰å…¥ï¼Œä½†è‰²å½©å“質ç¨å·®ã€‚"/> <slider label="æè³ªè¨˜æ†¶é«”(MB):" name="GraphicsCardTextureMemory" tool_tip="é…置給æè³ªä½¿ç”¨çš„記憶體é‡ã€‚ é è¨ç‚ºé¡¯åƒå¡è¨˜æ†¶é«”。 é™ä½Žæ¤å€¼å¯ä»¥æå‡æ•ˆèƒ½ï¼Œä½†æè³ªä¹Ÿæœƒè®Šæ¨¡ç³Šã€‚"/> <spinner label="霧è·é›¢æ¯”率:" name="fog"/> <button label="確定" label_selected="確定" name="OK"/> diff --git a/indra/newview/skins/default/xui/zh/floater_how_to.xml b/indra/newview/skins/default/xui/zh/floater_how_to.xml new file mode 100644 index 0000000000000000000000000000000000000000..e033327165d83afed942eae1869033811cfac558 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_how_to.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_how_to" title="簡易教å¸"/> diff --git a/indra/newview/skins/default/xui/zh/floater_map.xml b/indra/newview/skins/default/xui/zh/floater_map.xml index de39fc635d3a8e660e5ce628d8449f613009867d..8a030b3b3f8d8b70f690bb3d8414be22a6dedc77 100644 --- a/indra/newview/skins/default/xui/zh/floater_map.xml +++ b/indra/newview/skins/default/xui/zh/floater_map.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="Map" title=""> +<floater name="Map" title="è¿·ä½ åœ°åœ–"> <floater.string name="ToolTipMsg"> [REGION](雙擊以開啟地圖,按下 shift 鵿‹–曳來平移) </floater.string> diff --git a/indra/newview/skins/default/xui/zh/floater_merchant_outbox.xml b/indra/newview/skins/default/xui/zh/floater_merchant_outbox.xml new file mode 100644 index 0000000000000000000000000000000000000000..6b6126c8e0c6b23016ac18f2f1b43d9189953775 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_merchant_outbox.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_merchant_outbox" title="商家發件匣"> + <string name="OutboxFolderCount1"> + 1 個資料夾 + </string> + <string name="OutboxFolderCountN"> + [NUM] 個資料夾 + </string> + <string name="OutboxImporting"> + æ£åœ¨å‚³é€è³‡æ–™å¤¾â€¦ + </string> + <string name="OutboxInitializing"> + æ£åœ¨åˆå§‹åŒ–… + </string> + <panel label=""> + <panel> + <panel name="outbox_inventory_placeholder_panel"> + <text name="outbox_inventory_placeholder_title"> + 載入ä¸... + </text> + </panel> + </panel> + <panel> + <button label="é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集" name="outbox_import_btn" tool_tip="推到我第二人生購物市集的店é¢"/> + </panel> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_model_preview.xml b/indra/newview/skins/default/xui/zh/floater_model_preview.xml index 970e9e6f4fdc35eca766a3a71281ac780205887d..22b3d3b06527b4a29a4d0581ea754a77fc589955 100644 --- a/indra/newview/skins/default/xui/zh/floater_model_preview.xml +++ b/indra/newview/skins/default/xui/zh/floater_model_preview.xml @@ -1,7 +1,11 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater name="Model Preview" title="上傳模型"> - <string name="status_idle"> - é–’ç½®ä¸ + <string name="status_idle"/> + <string name="status_parse_error"> + éŒ¯èª¤ï¼šå‰–æž dae 時出錯,詳見記錄檔。 + </string> + <string name="status_material_mismatch"> + éŒ¯èª¤ï¼šæ¨¡åž‹ææ–™ä¸¦éžåƒè€ƒæ¨¡åž‹çš„å集åˆã€‚ </string> <string name="status_reading_file"> 載入ä¸... @@ -12,6 +16,9 @@ <string name="status_vertex_number_overflow"> éŒ¯èª¤ï¼šé ‚é»žæ•¸ç›®è¶…éŽ 65534,程åºå·²ä¸æ¢ï¼ </string> + <string name="bad_element"> + 錯誤:無效的元件 + </string> <string name="high"> 高 </string> @@ -45,6 +52,9 @@ <string name="mesh_status_missing_lod"> 缺ä¹éœ€è¦çš„細節層次。 </string> + <string name="mesh_status_invalid_material_list"> + ç´°ç¯€å±¤æ¬¡ææ–™ä¸¦éžåƒè€ƒæ¨¡åž‹çš„å集åˆã€‚ + </string> <string name="layer_all"> 全部 </string> @@ -54,178 +64,249 @@ <string name="simplifying"> 簡化ä¸â€¦ </string> - <text name="name_label"> - å稱: - </text> - <text name="lod_label"> - é 覽: - </text> - <combo_box name="preview_lod_combo" tool_tip="è¦åœ¨å‘ˆåƒé 覽ä¸å¯Ÿçœ‹çš„細節層次"> - <combo_item name="high"> - 細節層次:高度 - </combo_item> - <combo_item name="medium"> - 細節層次:ä¸åº¦ - </combo_item> - <combo_item name="low"> - 細節層次:低度 - </combo_item> - <combo_item name="lowest"> - 細節層次:最低 - </combo_item> - </combo_box> - <panel> - <text name="streaming cost"> - 資æºèŠ±è²»ï¼š[COST] - </text> - <text name="physics cost"> - 物ç†èŠ±è²»ï¼š[COST] - </text> - <text name="upload fee"> - 上傳費:無 - </text> - </panel> - <text name="status"> - [STATUS] - </text> - <button label="é è¨å€¼" name="reset_btn" tool_tip="é‡è¨ç‚ºé è¨å€¼"/> - <button label="上傳" name="ok_btn" tool_tip="上傳至模擬器"/> - <button label="å–æ¶ˆ" name="cancel_btn"/> - <tab_container name="import_tab"> - <panel label="細節層次" name="lod_panel"> - <text name="lod_table_header"> - 鏿“‡ç´°ç¯€å±¤æ¬¡ï¼š - </text> - <text name="high_label" value="高"/> - <text name="high_triangles" value="0"/> - <text name="high_vertices" value="0"/> - <text name="medium_label" value="ä¸"/> - <text name="medium_triangles" value="0"/> - <text name="medium_vertices" value="0"/> - <text name="low_label" value="低"/> - <text name="low_triangles" value="0"/> - <text name="low_vertices" value="0"/> - <text name="lowest_label" value="最低"/> - <text name="lowest_triangles" value="0"/> - <text name="lowest_vertices" value="0"/> - <text name="lod_table_footer"> - 細節層次:[DETAIL] - </text> - <radio_group name="lod_file_or_limit" value="lod_from_file"> - <radio_item label="從檔案載入" name="lod_from_file"/> - <radio_item label="自動生æˆ" name="lod_auto_generate"/> - <radio_item label="ç„¡" name="lod_none"/> - </radio_group> - <button label="ç€è¦½â€¦" name="lod_browse"/> - <combo_box name="lod_mode"> - <combo_item name="triangle_limit"> - ä¸‰è§’å½¢ä¸Šé™ - </combo_item> - <combo_item name="error_threshold"> - 錯誤門檻 - </combo_item> - </combo_box> - <text name="build_operator_text"> - 建製æ“作元: - </text> - <text name="queue_mode_text"> - 佇列模å¼ï¼š - </text> - <combo_box name="build_operator"> - <combo_item name="edge_collapse"> - å´é‚Šæ‘ºç–Š - </combo_item> - <combo_item name="half_edge_collapse"> - åŠå´é‚Šæ‘ºç–Š - </combo_item> - </combo_box> - <combo_box name="queue_mode"> - <combo_item name="greedy"> - å„˜é‡æœ€å¤§ - </combo_item> - <combo_item name="lazy"> - å„˜é‡æœ€å° - </combo_item> - <combo_item name="independent"> - ç¨ç«‹ - </combo_item> - </combo_box> - <text name="border_mode_text"> - 邊界模å¼ï¼š + <string name="tbd"> + (未定) + </string> + <panel name="left_panel"> + <panel name="model_name_representation_panel"> + <text name="name_label"> + 模型å稱: </text> - <text name="share_tolderance_text"> - 分享容å¿ï¼š + <text name="model_category_label"> + 這模型代表… </text> - <combo_box name="border_mode"> - <combo_item name="border_unlock"> - 解鎖 - </combo_item> - <combo_item name="border_lock"> - 鎖定 - </combo_item> + <combo_box name="model_category_combo"> + <combo_item label="鏿“‡ä¸€é ……" name="Choose one"/> + <combo_item label="化身形狀" name="Avatar shape"/> + <combo_item label="化身附件" name="Avatar attachment"/> + <combo_item label="會移動的物件(車輛ã€å‹•物)" name="Moving object (vehicle, animal)"/> + <combo_item label="建製元件" name="Building Component"/> + <combo_item label="大型ã€ä¸æœƒç§»å‹•ç‰é¡žåž‹" name="Large, non moving etc"/> + <combo_item label="較å°åž‹ã€ä¸æœƒç§»å‹•ç‰é¡žåž‹" name="Smaller, non-moving etc"/> + <combo_item label="並éžå…¶ä¸ä»»ä½•一個" name="Not really any of these"/> </combo_box> - <text name="crease_label"> - 皺褶角度: - </text> - <spinner name="crease_angle" value="75"/> </panel> - <panel label="物ç†" name="physics_panel"> - <panel name="physics geometry"> - <radio_group name="physics_load_radio" value="physics_load_from_file"> - <radio_item label="檔案:" name="physics_load_from_file"/> - <radio_item label="使用細節層次:" name="physics_use_lod"/> - </radio_group> - <combo_box name="physics_lod_combo" tool_tip="物ç†å½¢ç‹€æ‰€æŽ¡ç”¨çš„細節層次"> - <combo_item name="physics_lowest"> - 最低 - </combo_item> - <combo_item name="physics_low"> - 低 - </combo_item> - <combo_item name="physics_medium"> - ä¸ - </combo_item> - <combo_item name="physics_high"> - 高 - </combo_item> + <tab_container name="import_tab"> + <panel label="細節層次" name="lod_panel" title="細節層次"> + <text initial_value="來æº" name="source" value="來æº"/> + <text initial_value="三角形" name="triangles" value="三角形"/> + <text initial_value="é ‚é»ž" name="vertices" value="é ‚é»ž"/> + <text initial_value="高" name="high_label" value="高"/> + <combo_box name="lod_source_high"> + <item name="Load from file" value="從檔案載入"/> + <item name="Generate" value="產生"/> </combo_box> - <button label="ç€è¦½â€¦" name="physics_browse"/> - </panel> - <panel name="physics analysis"> - <slider label="平滑:" name="Smooth"/> - <check_box label="關閉洞å£ï¼ˆæ…¢ï¼‰" name="Close Holes (Slow)"/> - <button label="分æž" name="Decompose"/> - <button label="å–æ¶ˆ" name="decompose_cancel"/> + <button label="ç€è¦½â€¦" name="lod_browse_high"/> + <combo_box name="lod_mode_high"> + <item name="Triangle Limit" value="三角形上é™"/> + <item name="Error Threshold" value="錯誤門檻"/> + </combo_box> + <text initial_value="0" name="high_triangles" value="0"/> + <text initial_value="0" name="high_vertices" value="0"/> + <text initial_value="ä¸" name="medium_label" value="ä¸"/> + <combo_box name="lod_source_medium"> + <item name="Load from file" value="從檔案載入"/> + <item name="Generate" value="產生"/> + <item name="Use LoD above" value="以上使用低階細節"/> + </combo_box> + <button label="ç€è¦½â€¦" name="lod_browse_medium"/> + <combo_box name="lod_mode_medium"> + <item name="Triangle Limit" value="三角形上é™"/> + <item name="Error Threshold" value="錯誤門檻"/> + </combo_box> + <text initial_value="0" name="medium_triangles" value="0"/> + <text initial_value="0" name="medium_vertices" value="0"/> + <text initial_value="低" name="low_label" value="低"/> + <combo_box name="lod_source_low"> + <item name="Load from file" value="從檔案載入"/> + <item name="Generate" value="產生"/> + <item name="Use LoD above" value="以上使用低階細節"/> + </combo_box> + <button label="ç€è¦½â€¦" name="lod_browse_low"/> + <combo_box name="lod_mode_low"> + <item name="Triangle Limit" value="三角形上é™"/> + <item name="Error Threshold" value="錯誤門檻"/> + </combo_box> + <text initial_value="0" name="low_triangles" value="0"/> + <text initial_value="0" name="low_vertices" value="0"/> + <text initial_value="最低" name="lowest_label" value="最低"/> + <combo_box name="lod_source_lowest"> + <item name="Load from file" value="從檔案載入"/> + <item name="Generate" value="產生"/> + <item name="Use LoD above" value="以上使用低階細節"/> + </combo_box> + <button label="ç€è¦½â€¦" name="lod_browse_lowest"/> + <combo_box name="lod_mode_lowest"> + <item name="Triangle Limit" value="三角形上é™"/> + <item name="Error Threshold" value="錯誤門檻"/> + </combo_box> + <text initial_value="0" name="lowest_triangles" value="0"/> + <text initial_value="0" name="lowest_vertices" value="0"/> + <check_box label="產生法線" name="gen_normals"/> + <text initial_value="皺褶角度:" name="crease_label" value="皺褶角度:"/> + <spinner name="crease_angle" value="75"/> </panel> - <panel name="physics simplification"> - <slider label="階段數:" name="Combine Quality"/> - <slider label="細節比例:" name="Detail Scale"/> - <slider label="ä¿ç•™ï¼š" name="Retain%"/> - <button label="簡化" name="Simplify"/> - <button label="å–æ¶ˆ" name="simplify_cancel"/> + <panel label="物ç†" name="physics_panel"> + <panel name="physics geometry"> + <text name="first_step_name"> + æ¥é©Ÿ 1:細節層次 + </text> + <combo_box name="physics_lod_combo" tool_tip="物ç†å½¢ç‹€æ‰€æŽ¡ç”¨çš„細節層次"> + <combo_item name="choose_one"> + 鏿“‡ä¸€é …… + </combo_item> + <combo_item name="physics_high"> + 高 + </combo_item> + <combo_item name="physics_medium"> + ä¸ + </combo_item> + <combo_item name="physics_low"> + 低 + </combo_item> + <combo_item name="physics_lowest"> + 最低 + </combo_item> + <combo_item name="load_from_file"> + 來自檔案 + </combo_item> + </combo_box> + <button label="ç€è¦½â€¦" name="physics_browse"/> + </panel> + <panel name="physics analysis"> + <text name="method_label"> + æ¥é©Ÿ 2ï¼šåˆ†æž + </text> + <text name="analysis_method_label"> + 方法: + </text> + <text name="quality_label"> + å“質: + </text> + <text name="smooth_method_label"> + 平滑: + </text> + <check_box label="關閉洞å£" name="Close Holes (Slow)"/> + <button label="分æž" name="Decompose"/> + <button label="å–æ¶ˆ" name="decompose_cancel"/> + </panel> + <panel name="physics simplification"> + <text name="second_step_label"> + æ¥é©Ÿ 3:簡化 + </text> + <text name="simp_method_header"> + 方法: + </text> + <text name="pass_method_header"> + 階段數: + </text> + <text name="Detail Scale label"> + 細節比例: + </text> + <text name="Retain%_label"> + ä¿ç•™ï¼š + </text> + <combo_box name="Combine Quality" value="1"/> + <button label="簡化" name="Simplify"/> + <button label="å–æ¶ˆ" name="simplify_cancel"/> + </panel> + <panel name="physics info"> + <text name="results_text"> + çµæžœï¼š + </text> + <text name="physics_triangles"> + 三角形:[TRIANGLES], + </text> + <text name="physics_points"> + é ‚é»žï¼š[POINTS], + </text> + <text name="physics_hulls"> + 殼é¢ï¼š[HULLS] + </text> + </panel> </panel> - <panel name="physics info"> - <slider label="é 覽伸展:" name="physics_explode"/> - <text name="physics_triangles"> - 三角形:[TRIANGLES] + <panel label="上傳é¸é …" name="modifiers_panel"> + <text name="scale_label"> + 比例(1 = 原比例): </text> - <text name="physics_points"> - é ‚é»žï¼š[POINTS] + <spinner name="import_scale" value="1.0"/> + <text name="dimensions_label"> + è¦æ ¼ï¼š </text> - <text name="physics_hulls"> - 殼é¢ï¼š[HULLS] + <text name="import_dimensions"> + [X] X [Y] X [Z] </text> + <check_box label="åŒ…å«æè³ª" name="upload_textures"/> + <text name="include_label"> + 僅é™åŒ–身模型: + </text> + <check_box label="包å«è¡¨çš®é‡é‡" name="upload_skin"/> + <check_box label="åŒ…å«æŽ¥é»žä½ç½®" name="upload_joints"/> + <text name="pelvis_offset_label"> + Z åè·ï¼ˆå‡é«˜æˆ–é™ä½ŽåŒ–身): + </text> + <spinner name="pelvis_offset" value="0.0"/> </panel> - </panel> - <panel label="修飾器" name="modifiers_panel"> - <spinner name="import_scale" value="1.0"/> - <text name="import_dimensions"> - [X] x [Y] x [Z] 公尺 + </tab_container> + <panel name="weights_and_warning_panel"> + <button label="計算é‡é‡å’Œè²»ç”¨" name="calculate_btn" tool_tip="計算é‡é‡å’Œè²»ç”¨"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> + <button label="上傳" name="ok_btn" tool_tip="上傳至模擬器"/> + <button label="清除è¨å®šä¸¦é‡è¨å½¢å¼" name="reset_btn"/> + <text name="upload_fee"> + 上傳費用:L$ [FEE] + </text> + <text name="prim_weight"> + åœŸåœ°è¡æ“Šé‡ï¼š[EQ] + </text> + <text name="download_weight"> + 下載:[ST] + </text> + <text name="physics_weight"> + 物ç†ï¼š[PH] + </text> + <text name="server_weight"> + 伺æœå™¨ï¼š[SIM] + </text> + <text name="warning_title"> + 附註: + </text> + <text name="warning_message"> + ä½ ç„¡æ¬Šä¸Šå‚³ç¶²é¢æ¨¡åž‹ã€‚ [[VURL] çžè§£å¦‚何]通éŽèªè‰ã€‚ + </text> + <text name="status"> + [STATUS] </text> - <check_box label="æè³ª" name="upload_textures"/> - <check_box label="表皮é‡é‡" name="upload_skin"/> - <check_box label="接點ä½ç½®" name="upload_joints"/> - <spinner name="pelvis_offset" value="0.0"/> </panel> - </tab_container> + </panel> + <text name="lod_label"> + é 覽: + </text> + <panel name="right_panel"> + <combo_box name="preview_lod_combo" tool_tip="è¦åœ¨å‘ˆåƒé 覽ä¸å¯Ÿçœ‹çš„細節層次"> + <combo_item name="high"> + 高 + </combo_item> + <combo_item name="medium"> + ä¸ + </combo_item> + <combo_item name="low"> + 低 + </combo_item> + <combo_item name="lowest"> + 最低 + </combo_item> + </combo_box> + <text name="label_display"> + 顯示… + </text> + <check_box label="邊" name="show_edges"/> + <check_box label="物ç†" name="show_physics"/> + <check_box label="æè³ª" name="show_textures"/> + <check_box label="表皮é‡é‡" name="show_skin_weight"/> + <check_box label="接點" name="show_joint_positions"/> + <text name="physics_explode_label"> + é 覽伸展: + </text> + </panel> </floater> diff --git a/indra/newview/skins/default/xui/zh/floater_moveview.xml b/indra/newview/skins/default/xui/zh/floater_moveview.xml index 18ef63d00e4889ad2ccf4139b247831aad526589..0b267e8fae43b1b64bc109e3aca03e2c89064ceb 100644 --- a/indra/newview/skins/default/xui/zh/floater_moveview.xml +++ b/indra/newview/skins/default/xui/zh/floater_moveview.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="move_floater"> +<floater name="move_floater" title="行走 / è·‘æ¥ / 飛行"> <string name="walk_forward_tooltip"> å‘å‰èµ°ï¼ˆæŒ‰ä¸‹å‘上ç®é 或 W éµï¼‰ </string> diff --git a/indra/newview/skins/default/xui/zh/floater_my_appearance.xml b/indra/newview/skins/default/xui/zh/floater_my_appearance.xml new file mode 100644 index 0000000000000000000000000000000000000000..217ea5d1f950dcc369d848aa3039804c1c1cba37 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_my_appearance.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_my_appearance" title="外觀"> + <panel label="編輯外觀" name="main_panel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_my_inventory.xml b/indra/newview/skins/default/xui/zh/floater_my_inventory.xml new file mode 100644 index 0000000000000000000000000000000000000000..187597f4eba69bc08ef55334c501b35815da7f12 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_my_inventory.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_my_inventory" title="æ”¶ç´å€"/> diff --git a/indra/newview/skins/default/xui/zh/floater_object_weights.xml b/indra/newview/skins/default/xui/zh/floater_object_weights.xml new file mode 100644 index 0000000000000000000000000000000000000000..d2875b24b474a22e8049c78f0a2148ebd47239a8 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_object_weights.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="object_weights" title="進階"> + <floater.string name="nothing_selected" value="--"/> + <text name="selected_text" value="å·²é¸"/> + <text name="objects" value="--"/> + <text name="objects_label" value="物件"/> + <text name="prims" value="--"/> + <text name="prims_label" value="幾何元件"/> + <text name="weights_of_selected_text" value="所é¸ç‰©é‡é‡"/> + <text name="download" value="--"/> + <text name="download_label" value="下載"/> + <text name="physics" value="--"/> + <text name="physics_label" value="物ç†"/> + <text name="server" value="--"/> + <text name="server_label" value="伺æœå™¨"/> + <text name="display" value="--"/> + <text name="display_label" value="顯示"/> + <text name="land_impacts_text" value="åœŸåœ°è¡æ“Šé‡"/> + <text name="selected" value="--"/> + <text name="selected_label" value="鏿“‡"/> + <text name="rezzed_on_land" value="--"/> + <text name="rezzed_on_land_label" value="已產生到土地上"/> + <text name="remaining_capacity" value="--"/> + <text name="remaining_capacity_label" value="剩餘容ç´é‡"/> + <text name="total_capacity" value="--"/> + <text name="total_capacity_label" value="總容ç´é‡"/> + <text name="help_SLURL" value="[secondlife:///app/help/object_weights 這是什麼?]"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_pathfinding_characters.xml b/indra/newview/skins/default/xui/zh/floater_pathfinding_characters.xml new file mode 100644 index 0000000000000000000000000000000000000000..e6971d111fc8f9651e32a6ad1b69082c7896d76f --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_pathfinding_characters.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_pathfinding_characters" title="尋徑角色"> + <floater.string name="messaging_get_inprogress"> + 尋徑角色查詢ä¸â€¦ + </floater.string> + <floater.string name="messaging_get_error"> + 查詢尋徑角色時出錯。 + </floater.string> + <floater.string name="messaging_complete_none_found"> + 沒有尋徑角色。 + </floater.string> + <floater.string name="messaging_complete_available"> + 從 [NUM_TOTAL] 個角色ä¸é¸å–了 [NUM_SELECTED] 個。 + </floater.string> + <floater.string name="messaging_not_enabled"> + 這地å€ä¸¦æœªå•Ÿç”¨å°‹å¾‘。 + </floater.string> + <floater.string name="character_cpu_time"> + [CPU_TIME] 微秒 + </floater.string> + <floater.string name="character_owner_loading"> + [Loading] + </floater.string> + <floater.string name="character_owner_unknown"> + [Unknown] + </floater.string> + <floater.string name="character_owner_group"> + [group] + </floater.string> + <panel> + <scroll_list name="objects_scroll_list"> + <scroll_list.columns label="å稱" name="name"/> + <scroll_list.columns label="æè¿°" name="description"/> + <scroll_list.columns label="所有人" name="owner"/> + <scroll_list.columns label="ä¸å¤®è™•ç†å™¨" name="cpu_time"/> + <scroll_list.columns label="高度" name="altitude"/> + </scroll_list> + <text name="messaging_status"> + 角色: + </text> + <button label="刷新清單" name="refresh_objects_list"/> + <button label="å…¨é¸" name="select_all_objects"/> + <button label="全都ä¸é¸" name="select_none_objects"/> + </panel> + <panel> + <text name="actions_label"> + 所é¸è§’色所採動作: + </text> + <check_box label="顯示指標" name="show_beacon"/> + <check_box label="顯示物ç†å›Š" name="show_physics_capsule"/> + <button label="å–å¾—" name="take_objects"/> + <button label="æ‹¿å–副本" name="take_copy_objects"/> + <button label="çž¬é–“å‚³é€æˆ‘到那裡" name="teleport_me_to_object" tool_tip="åªåœ¨é¸å–了一個角色時啟用。"/> + <button label="退回" name="return_objects"/> + <button label="刪除" name="delete_objects"/> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_pathfinding_console.xml b/indra/newview/skins/default/xui/zh/floater_pathfinding_console.xml new file mode 100644 index 0000000000000000000000000000000000000000..be009b54d88e38a44ad18deac2a6b52f8d051163 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_pathfinding_console.xml @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_pathfinding_console" title="尋徑察看 / 測試"> + <floater.string name="navmesh_viewer_status_library_not_implemented"> + 找ä¸åˆ°å°‹å¾‘函å¼åº«å¯¦ä½œã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_region_not_enabled"> + 這地å€ä¸¦æœªå•Ÿç”¨å°‹å¾‘。 + </floater.string> + <floater.string name="navmesh_viewer_status_region_loading"> + ç‰å¾…地å€å®Œæˆè¼‰å…¥ã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_checking_version"> + æ£åœ¨æª¢æŸ¥å°Žèˆªç¶²é¢ç‹€æ…‹ã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_downloading"> + æ£åœ¨ä¸‹è¼‰å°Žèˆªç¶²é¢ã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_updating"> + 伺æœå™¨ä¸Šçš„導航網é¢å·²è®Šæ›´ã€‚ æ£åœ¨ä¸‹è¼‰æœ€æ–°çš„導航網é¢ã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_has_navmesh"> + 最新的導航網é¢ä¸‹è¼‰å®Œæˆã€‚ + </floater.string> + <floater.string name="navmesh_viewer_status_error"> + 無法完æˆå°Žèˆªç¶²é¢ä¸‹è¼‰ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_pending"> + å°Žèˆªç¶²é¢æœ‰è®Šæ›´å¾…å˜ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_building"> + æ£åœ¨å»ºæ§‹å°Žèˆªç¶²é¢ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_some_pending"> + æŸäº›å°Žèˆªç¶²é¢åœ°å€æœ‰è®Šæ›´å¾…å˜ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_some_building"> + æ£åœ¨å»ºæ§‹æŸäº›å°Žèˆªç¶²é¢åœ°å€ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_pending_and_building"> + æŸäº›å°Žèˆªç¶²é¢åœ°å€æœ‰è®Šæ›´å¾…å˜ï¼Œå…¶ä»–çš„æ£åœ¨å»ºæ§‹ä¸ã€‚ + </floater.string> + <floater.string name="navmesh_simulator_status_complete"> + 導航網é¢å·²å…¨é¢æ›´æ–°ã€‚ + </floater.string> + <floater.string name="pathing_library_not_implemented"> + 找ä¸åˆ°å°‹å¾‘函å¼åº«å¯¦ä½œã€‚ + </floater.string> + <floater.string name="pathing_region_not_enabled"> + 這地å€ä¸¦æœªå•Ÿç”¨å°‹å¾‘。 + </floater.string> + <floater.string name="pathing_choose_start_and_end_points"> + è«‹é¸æ“‡èµ·é»žå’Œçµ‚點。 + </floater.string> + <floater.string name="pathing_choose_start_point"> + è«‹é¸æ“‡èµ·é»žã€‚ + </floater.string> + <floater.string name="pathing_choose_end_point"> + è«‹é¸æ“‡çµ‚點。 + </floater.string> + <floater.string name="pathing_path_valid"> + 路徑以橘色顯示。 + </floater.string> + <floater.string name="pathing_path_invalid"> + 在所é¸çš„點之間找ä¸åˆ°è·¯å¾‘。 + </floater.string> + <floater.string name="pathing_error"> + 產生路徑時出錯。 + </floater.string> + <tab_container name="view_test_tab_container"> + <panel label="視角" name="view_panel"> + <text name="show_label"> + 顯示: + </text> + <check_box label="世界" name="show_world"/> + <check_box label="僅é™å¯ç§»å‹•çš„" name="show_world_movables_only"/> + <check_box label="導航網é¢" name="show_navmesh"/> + <text name="show_walkability_label"> + 顯示å¯è¡Œèµ°åœ°åœ–: + </text> + <combo_box name="show_heatmap_mode"> + <combo_box.item label="ä¸é¡¯ç¤º" name="show_heatmap_mode_none"/> + <combo_box.item label="類型 A 角色" name="show_heatmap_mode_a"/> + <combo_box.item label="類型 B 角色" name="show_heatmap_mode_b"/> + <combo_box.item label="類型 C 角色" name="show_heatmap_mode_c"/> + <combo_box.item label="類型 D 角色" name="show_heatmap_mode_d"/> + </combo_box> + <check_box label="å¯è¡Œèµ°çš„" name="show_walkables"/> + <check_box label="實質體ç©" name="show_material_volumes"/> + <check_box label="éœæ…‹éšœç¤™" name="show_static_obstacles"/> + <check_box label="排除體ç©" name="show_exclusion_volumes"/> + <check_box label="æ°´å¹³é¢" name="show_water_plane"/> + <check_box label="å…· X å…‰é€è¦–力" name="show_xray"/> + </panel> + <panel label="測試路徑" name="test_panel"> + <text name="ctrl_click_label"> + æŒ‰ä½ Ctrl 並點按å³å¯é¸æ“‡èµ·é»žã€‚ + </text> + <text name="shift_click_label"> + æŒ‰ä½ Shift 並點按å³å¯é¸æ“‡çµ‚點。 + </text> + <text name="character_width_label"> + 角色寬度 + </text> + <slider name="character_width" value="1"/> + <text name="character_width_unit_label"> + 公尺 + </text> + <text name="character_type_label"> + 角色類型 + </text> + <combo_box name="path_character_type"> + <combo_box.item label="ç„¡" name="path_character_type_none"/> + <combo_box.item label="A" name="path_character_type_a"/> + <combo_box.item label="B" name="path_character_type_b"/> + <combo_box.item label="C" name="path_character_type_c"/> + <combo_box.item label="D" name="path_character_type_d"/> + </combo_box> + <button label="清除路徑" name="clear_path"/> + </panel> + </tab_container> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_pathfinding_linksets.xml b/indra/newview/skins/default/xui/zh/floater_pathfinding_linksets.xml new file mode 100644 index 0000000000000000000000000000000000000000..22e5d2e8462e3aa58decfe1659207645625d865f --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_pathfinding_linksets.xml @@ -0,0 +1,167 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_pathfinding_linksets" title="尋徑è¯çµé›†"> + <floater.string name="messaging_get_inprogress"> + 尋徑è¯çµé›†æŸ¥è©¢ä¸â€¦ + </floater.string> + <floater.string name="messaging_get_error"> + 查詢尋徑è¯çµé›†æ™‚出錯。 + </floater.string> + <floater.string name="messaging_set_inprogress"> + æ£åœ¨ä¿®æ”¹æ‰€é¸å°‹å¾‘è¯çµé›†â€¦ + </floater.string> + <floater.string name="messaging_set_error"> + 修改所é¸å°‹å¾‘è¯çµé›†æ™‚出錯。 + </floater.string> + <floater.string name="messaging_complete_none_found"> + 沒有尋徑è¯çµé›†ã€‚ + </floater.string> + <floater.string name="messaging_complete_available"> + 從 [NUM_TOTAL] 個è¯çµé›†ä¸é¸å–了 [NUM_SELECTED] 個。 + </floater.string> + <floater.string name="messaging_not_enabled"> + 這地å€ä¸¦æœªå•Ÿç”¨å°‹å¾‘。 + </floater.string> + <floater.string name="linkset_terrain_name"> + [Terrain] + </floater.string> + <floater.string name="linkset_terrain_description"> + -- + </floater.string> + <floater.string name="linkset_terrain_owner"> + -- + </floater.string> + <floater.string name="linkset_terrain_scripted"> + -- + </floater.string> + <floater.string name="linkset_terrain_land_impact"> + -- + </floater.string> + <floater.string name="linkset_terrain_dist_from_you"> + -- + </floater.string> + <floater.string name="linkset_owner_loading"> + [Loading] + </floater.string> + <floater.string name="linkset_owner_unknown"> + [Unknown] + </floater.string> + <floater.string name="linkset_owner_group"> + [group] + </floater.string> + <floater.string name="linkset_is_scripted"> + 是 + </floater.string> + <floater.string name="linkset_is_not_scripted"> + å¦ + </floater.string> + <floater.string name="linkset_is_unknown_scripted"> + 未知 + </floater.string> + <floater.string name="linkset_use_walkable"> + å¯è¡Œèµ°çš„ + </floater.string> + <floater.string name="linkset_use_static_obstacle"> + éœæ…‹éšœç¤™ + </floater.string> + <floater.string name="linkset_use_dynamic_obstacle"> + å¯ç§»å‹•障礙 + </floater.string> + <floater.string name="linkset_use_material_volume"> + å¯¦è³ªé«”ç© + </floater.string> + <floater.string name="linkset_use_exclusion_volume"> + æŽ’é™¤é«”ç© + </floater.string> + <floater.string name="linkset_use_dynamic_phantom"> + å¯ç§»å‹•幻影 + </floater.string> + <floater.string name="linkset_is_terrain"> + [unmodifiable] + </floater.string> + <floater.string name="linkset_is_restricted_state"> + [restricted] + </floater.string> + <floater.string name="linkset_is_non_volume_state"> + [concave] + </floater.string> + <floater.string name="linkset_is_restricted_non_volume_state"> + [restricted,concave] + </floater.string> + <floater.string name="linkset_choose_use"> + 鏿“‡è¯çµé›†çš„使用… + </floater.string> + <panel> + <combo_box name="filter_by_linkset_use"> + <combo_box.item label="按è¯çµé›†çš„ä½¿ç”¨ä¾†éŽæ¿¾â€¦" name="filter_by_linkset_use_none"/> + <combo_box.item label="å¯è¡Œèµ°çš„" name="filter_by_linkset_use_walkable"/> + <combo_box.item label="éœæ…‹éšœç¤™" name="filter_by_linkset_use_static_obstacle"/> + <combo_box.item label="å¯ç§»å‹•障礙" name="filter_by_linkset_use_dynamic_obstacle"/> + <combo_box.item label="實質體ç©" name="filter_by_linkset_use_material_volume"/> + <combo_box.item label="排除體ç©" name="filter_by_linkset_use_exclusion_volume"/> + <combo_box.item label="å¯ç§»å‹•幻影" name="filter_by_linkset_use_dynamic_phantom"/> + </combo_box> + <button label="套用" name="apply_filters"/> + <button label="清除" name="clear_filters"/> + <scroll_list name="objects_scroll_list"> + <scroll_list.columns label="åç¨±ï¼ˆæ ¹å¹¾ä½•å…ƒä»¶ï¼‰" name="name"/> + <scroll_list.columns label="æè¿°ï¼ˆæ ¹å¹¾ä½•元件)" name="description"/> + <scroll_list.columns label="所有人" name="owner"/> + <scroll_list.columns label="有腳本" name="scripted"/> + <scroll_list.columns label="è¡æ“Š" name="land_impact"/> + <scroll_list.columns label="è·é›¢" name="dist_from_you"/> + <scroll_list.columns label="è¯çµé›†çš„使用" name="linkset_use"/> + <scroll_list.columns label="A %" name="a_percent"/> + <scroll_list.columns label="B %" name="b_percent"/> + <scroll_list.columns label="C %" name="c_percent"/> + <scroll_list.columns label="D %" name="d_percent"/> + </scroll_list> + <text name="messaging_status"> + è¯çµé›†ï¼š + </text> + <button label="刷新清單" name="refresh_objects_list"/> + <button label="å…¨é¸" name="select_all_objects"/> + <button label="全都ä¸é¸" name="select_none_objects"/> + </panel> + <panel> + <check_box label="顯示指標" name="show_beacon"/> + <button label="å–å¾—" name="take_objects"/> + <button label="æ‹¿å–副本" name="take_copy_objects"/> + <button label="çž¬é–“å‚³é€æˆ‘到那裡" name="teleport_me_to_object"/> + <button label="退回" name="return_objects"/> + <button label="刪除" name="delete_objects"/> + </panel> + <panel> + <text name="walkability_coefficients_label"> + å¯è¡Œèµ°æ€§ï¼š + </text> + <text name="edit_a_label"> + A + </text> + <line_editor name="edit_a_value" tool_tip="A 類型角色的å¯è¡Œèµ°æ€§ã€‚以類人類為範例角色類型。"/> + <text name="edit_b_label"> + B + </text> + <line_editor name="edit_b_value" tool_tip="B 類型角色的å¯è¡Œèµ°æ€§ã€‚以ç¸é¡žç‚ºç¯„例角色類型。"/> + <text name="edit_c_label"> + C + </text> + <line_editor name="edit_c_value" tool_tip="C 類型角色的å¯è¡Œèµ°æ€§ã€‚以機械類為範例角色類型。"/> + <text name="edit_d_label"> + D + </text> + <line_editor name="edit_d_value" tool_tip="D 類型角色的å¯è¡Œèµ°æ€§ã€‚以其他種類為範例角色類型。"/> + <button label="套用變更" name="apply_edit_values"/> + <text name="suggested_use_a_label"> + (類人類) + </text> + <text name="suggested_use_b_label"> + (ç¸é¡žï¼‰ + </text> + <text name="suggested_use_c_label"> + (機械類) + </text> + <text name="suggested_use_d_label"> + (其他) + </text> + </panel> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_people.xml b/indra/newview/skins/default/xui/zh/floater_people.xml new file mode 100644 index 0000000000000000000000000000000000000000..f629f2f1845a1a4891930934c700f41ca12c26c6 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_people.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_people" title="人群"> + <panel_container name="main_panel"> + <panel label="群組檔案" name="panel_group_info_sidetray"/> + <panel label="被å°éŽ–çš„å±…æ°‘èˆ‡ç‰©ä»¶" name="panel_block_list_sidetray"/> + </panel_container> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_picks.xml b/indra/newview/skins/default/xui/zh/floater_picks.xml new file mode 100644 index 0000000000000000000000000000000000000000..a8bfcd99e3cf9ae46531130470b0412bda84d58b --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_picks.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_picks" title="ç²¾é¸åœ°é»ž"/> diff --git a/indra/newview/skins/default/xui/zh/floater_places.xml b/indra/newview/skins/default/xui/zh/floater_places.xml new file mode 100644 index 0000000000000000000000000000000000000000..f6ef1e2141101d2ab76dcb779eaf393dceb8009a --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_places.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_places" title="地點"> + <panel label="地點" name="main_panel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_preferences_proxy.xml b/indra/newview/skins/default/xui/zh/floater_preferences_proxy.xml new file mode 100644 index 0000000000000000000000000000000000000000..f91d5c5cdb3afd6e022e1b2ebf1d15176d3bf4a6 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_preferences_proxy.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Proxy Settings Floater" title="代ç†ä¼ºæœå™¨è¨å®š"> + <check_box initial_value="false" label="使用 HTTP 代ç†ä¼ºæœå™¨ç€è¦½ç¶²é " name="web_proxy_enabled"/> + <text name="http_proxy_label"> + HTTP 代ç†ä¼ºæœå™¨ï¼š + </text> + <line_editor name="web_proxy_editor" tool_tip="ä½ å¸Œæœ›ä½¿ç”¨çš„ HTTP 代ç†ä¼ºæœå™¨çš„ DNS å稱或 IP ä½å€ã€‚"/> + <spinner label="åŸ è™Ÿï¼š" name="web_proxy_port" tool_tip="ä½ å¸Œæœ›ä½¿ç”¨çš„ HTTP 代ç†ä¼ºæœå™¨çš„åŸ è™Ÿã€‚"/> + <check_box label="使用 SOCKS 5 代ç†ä¼ºæœå™¨è™•ç† UDP 資料æµ" name="socks_proxy_enabled"/> + <text name="socks5_proxy_label"> + SOCKS 5 代ç†ä¼ºæœå™¨ï¼š + </text> + <line_editor name="socks_proxy_editor" tool_tip="ä½ å¸Œæœ›ä½¿ç”¨çš„ SOCKS 5 代ç†ä¼ºæœå™¨çš„ DNS å稱或 IP ä½å€ã€‚"/> + <spinner label="åŸ è™Ÿï¼š" name="socks_proxy_port" tool_tip="ä½ å¸Œæœ›ä½¿ç”¨çš„ SOCKS 5 代ç†ä¼ºæœå™¨çš„åŸ è™Ÿã€‚"/> + <text name="socks_auth_label"> + SOCKS é‘’èªï¼š + </text> + <radio_group name="socks5_auth_type"> + <radio_item label="ç„¡é‘’èª" name="Socks5NoAuth" tool_tip="Socks5 代ç†ä¼ºæœå™¨ç„¡éœ€é‘’èªã€‚" value="ç„¡"/> + <radio_item label="使用者å稱 / 密碼" name="Socks5UserPass" tool_tip="Socks5 代ç†ä¼ºæœå™¨éœ€è¦é€éŽä½¿ç”¨è€…å稱 / 密碼鑒èªã€‚" value="UserPass"/> + </radio_group> + <text name="socks5_username_label"> + 使用者å稱: + </text> + <text name="socks5_password_label"> + 密碼: + </text> + <line_editor name="socks5_username" tool_tip="ä½ çš„ SOCKS 5 伺æœå™¨é‘’èªæ‰€ç”¨çš„使用者å稱"/> + <line_editor name="socks5_password" tool_tip="ä½ çš„ SOCKS 5 伺æœå™¨é‘’èªæ‰€ç”¨çš„密碼"/> + <text name="other_proxy_label"> + å…¶ä»– HTTP 資料æµä»£ç†ä¼ºæœå™¨ï¼š + </text> + <radio_group name="other_http_proxy_type"> + <radio_item label="ä¸ä½¿ç”¨ä»£ç†ä¼ºæœå™¨" name="OtherNoProxy" tool_tip="éžç¶²é çš„ HTTP 資料æµä¸æœƒè¢«å°Žå‘任何代ç†ä¼ºæœå™¨ã€‚" value="ç„¡"/> + <radio_item label="使用 HTTP 代ç†ä¼ºæœå™¨" name="OtherHTTPProxy" tool_tip="éžç¶²é çš„ HTTP 資料æµå°‡å°Žå‘所è¨çš„ç¶²é 代ç†ä¼ºæœå™¨ã€‚" value="ç¶²é "/> + <radio_item label="使用 SOCKS 5 代ç†ä¼ºæœå™¨" name="OtherSocksProxy" tool_tip="éžç¶²é çš„ HTTP 資料æµå°‡å°Žå‘所è¨çš„ Socks 5 代ç†ä¼ºæœå™¨ã€‚" value="網路套接"/> + </radio_group> + <button label="確定" label_selected="確定" name="OK"/> + <button label="å–æ¶ˆ" label_selected="å–æ¶ˆ" name="Cancel"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_preview_animation.xml b/indra/newview/skins/default/xui/zh/floater_preview_animation.xml index 3be6f7a35e34a8836189741c9aec546fb1d5b61e..b94d67b7eff1656c736c1529ed20f7a14b301b92 100644 --- a/indra/newview/skins/default/xui/zh/floater_preview_animation.xml +++ b/indra/newview/skins/default/xui/zh/floater_preview_animation.xml @@ -6,6 +6,6 @@ <text name="desc txt"> æè¿°ï¼š </text> - <button label="åœ¨è™›æ“¬ä¸–ç•Œæ’æ”¾" label_selected="åœæ¢" name="Anim play btn" tool_tip="æ’æ”¾æ¤å‹•作讓他人看見"/> - <button label="åœ¨æœ¬åœ°æ’æ”¾" label_selected="åœæ¢" name="Anim audition btn" tool_tip="æ’æ”¾æ¤å‹•作,åªçµ¦è‡ªå·±çœ‹"/> + <button label="åœ¨è™›æ“¬ä¸–ç•Œæ’æ”¾" label_selected="åœæ¢" name="Inworld" tool_tip="æ’æ”¾æ¤å‹•作讓他人看見"/> + <button label="åœ¨æœ¬åœ°æ’æ”¾" label_selected="åœæ¢" name="Locally" tool_tip="æ’æ”¾æ¤å‹•作,åªçµ¦è‡ªå·±çœ‹"/> </floater> diff --git a/indra/newview/skins/default/xui/zh/floater_search.xml b/indra/newview/skins/default/xui/zh/floater_search.xml index d5abf37b8619096d3de571c9696de8df7ab92e3b..3e85a529ae3c82aa65b4d5653efccfebffcfcb30 100644 --- a/indra/newview/skins/default/xui/zh/floater_search.xml +++ b/indra/newview/skins/default/xui/zh/floater_search.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="floater_search" title="尋找"> +<floater name="floater_search" title=""> <floater.string name="loading_text"> 載入ä¸... </floater.string> diff --git a/indra/newview/skins/default/xui/zh/floater_snapshot.xml b/indra/newview/skins/default/xui/zh/floater_snapshot.xml index cfdaeeedb0d1aa15933bfa7c3a2c84bf01b3aead..e86e20829f971d7e2d979843d7097e9e7ad0d995 100644 --- a/indra/newview/skins/default/xui/zh/floater_snapshot.xml +++ b/indra/newview/skins/default/xui/zh/floater_snapshot.xml @@ -3,72 +3,63 @@ <floater.string name="unknown"> 未知 </floater.string> - <radio_group label="快照類型" name="snapshot_type_radio"> - <radio_item label="電郵" name="postcard"/> - <radio_item label="我的收ç´å€ï¼ˆL$[AMOUNT])" name="texture"/> - <radio_item label="儲å˜åˆ°é›»è…¦ä¸Š" name="local"/> - </radio_group> + <string name="postcard_progress_str"> + æ£åœ¨ç™¼é€é›»éƒµ + </string> + <string name="profile_progress_str"> + 發佈 + </string> + <string name="inventory_progress_str"> + 儲å˜åˆ°æ”¶ç´å€ + </string> + <string name="local_progress_str"> + æ£åœ¨å˜åˆ°é›»è…¦ + </string> + <string name="profile_succeeded_str"> + 圖åƒå·²ä¸Šå‚³ + </string> + <string name="postcard_succeeded_str"> + é›»éƒµç™¼é€æˆåŠŸï¼ + </string> + <string name="inventory_succeeded_str"> + æˆåŠŸå˜å…¥æ”¶ç´å€ï¼ + </string> + <string name="local_succeeded_str"> + æˆåŠŸå˜å…¥é›»è…¦ï¼ + </string> + <string name="profile_failed_str"> + 上傳圖åƒåˆ°ä½ 的檔案訊æ¯ç™¼ä½ˆæ™‚出錯。 + </string> + <string name="postcard_failed_str"> + 電郵傳é€å¤±æ•—。 + </string> + <string name="inventory_failed_str"> + 無法å˜å…¥æ”¶ç´å€ã€‚ + </string> + <string name="local_failed_str"> + 無法儲入電腦。 + </string> + <button name="advanced_options_btn" tool_tip="進階é¸é …"/> + <text name="image_res_text"> + [WIDTH] x [HEIGHT] åƒç´ + </text> <text name="file_size_label"> [SIZE] KB </text> - <button label="é€å‡º" name="send_btn"/> - <button label="儲å˜ï¼ˆL$[AMOUNT])" name="upload_btn"/> - <flyout_button label="儲å˜" name="save_btn" tool_tip="儲å˜åœ–åƒåˆ°æª”案"> - <flyout_button.item label="儲å˜" name="save_item"/> - <flyout_button.item label="å¦å˜..." name="saveas_item"/> - </flyout_button> - <button label="更多" name="more_btn" tool_tip="進階é¸é …"/> - <button label="æ›´å°‘" name="less_btn" tool_tip="進階é¸é …"/> - <button label="å–æ¶ˆ" name="discard_btn"/> - <text name="type_label2"> - 尺寸 - </text> - <text name="format_label"> - æ ¼å¼ - </text> - <combo_box label="è§£æžåº¦" name="postcard_size_combo"> - <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> - <combo_box.item label="640x480" name="640x480"/> - <combo_box.item label="800x600" name="800x600"/> - <combo_box.item label="1024x768" name="1024x768"/> - <combo_box.item label="自訂" name="Custom"/> - </combo_box> - <combo_box label="è§£æžåº¦" name="texture_size_combo"> - <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> - <combo_box.item label="å°ï¼ˆ128x128)" name="Small(128x128)"/> - <combo_box.item label="ä¸ï¼ˆ256x256)" name="Medium(256x256)"/> - <combo_box.item label="大(512x512)" name="Large(512x512)"/> - <combo_box.item label="自訂" name="Custom"/> - </combo_box> - <combo_box label="è§£æžåº¦" name="local_size_combo"> - <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> - <combo_box.item label="320x240" name="320x240"/> - <combo_box.item label="640x480" name="640x480"/> - <combo_box.item label="800x600" name="800x600"/> - <combo_box.item label="1024x768" name="1024x768"/> - <combo_box.item label="1280x1024" name="1280x1024"/> - <combo_box.item label="1600x1200" name="1600x1200"/> - <combo_box.item label="自訂" name="Custom"/> - </combo_box> - <combo_box label="æ ¼å¼" name="local_format_combo"> - <combo_box.item label="PNG" name="PNG"/> - <combo_box.item label="JPEG" name="JPEG"/> - <combo_box.item label="BMP" name="BMP"/> - </combo_box> - <spinner label="寬" name="snapshot_width"/> - <spinner label="高度" name="snapshot_height"/> - <check_box label="éŽ–ä½æ¯”例" name="keep_aspect_check"/> - <slider label="圖åƒå“質" name="image_quality_slider"/> - <text name="layer_type_label"> - æ“·å–快照: - </text> - <combo_box label="圖層" name="layer_types"> - <combo_box.item label="é¡è‰²" name="Colors"/> - <combo_box.item label="深度" name="Depth"/> - </combo_box> - <check_box label="介é¢" name="ui_check"/> - <check_box label="æ“¡é 顯示" name="hud_check"/> - <check_box label="儲å˜å¾Œä¿æŒæ‰“開狀態" name="keep_open_check"/> - <check_box label="將幀å‡çµï¼ˆå…¨èž¢å¹•)" name="freeze_frame_check"/> - <check_box label="自動刷新" name="auto_snapshot_check"/> + <panel name="advanced_options_panel"> + <text name="advanced_options_label"> + 進階é¸é … + </text> + <text name="layer_type_label"> + æ“·å–快照: + </text> + <combo_box label="圖層" name="layer_types"> + <combo_box.item label="é¡è‰²" name="Colors"/> + <combo_box.item label="深度" name="Depth"/> + </combo_box> + <check_box label="介é¢" name="ui_check"/> + <check_box label="æ“¡é 顯示" name="hud_check"/> + <check_box label="將幀å‡çµï¼ˆå…¨èž¢å¹•)" name="freeze_frame_check"/> + <check_box label="自動刷新" name="auto_snapshot_check"/> + </panel> </floater> diff --git a/indra/newview/skins/default/xui/zh/floater_spellcheck.xml b/indra/newview/skins/default/xui/zh/floater_spellcheck.xml new file mode 100644 index 0000000000000000000000000000000000000000..f5a6665844270df99f62294345655c0b8203236e --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_spellcheck.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="spellcheck_floater" title="æ‹¼å—æª¢æŸ¥è¨å®š"> + <check_box label="å•Ÿç”¨æ‹¼å—æª¢æŸ¥" name="spellcheck_enable"/> + <text name="spellcheck_main"> + 主è¦å—典: + </text> + <text label="記錄:" name="spellcheck_additional"> + é™„åŠ å—典: + </text> + <text name="spellcheck_available"> + å¯ç”¨çš„ + </text> + <text name="spellcheck_active"> + å¯ç”¨ + </text> + <button label="移除" name="spellcheck_remove_btn"/> + <button label="匯入…" name="spellcheck_import_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_spellcheck_import.xml b/indra/newview/skins/default/xui/zh/floater_spellcheck_import.xml new file mode 100644 index 0000000000000000000000000000000000000000..6094a3bbce4a93b291d7232d0a4b88e950e2df72 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_spellcheck_import.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="spellcheck_import" title="匯入å—å…¸"> + <button label="ç€è¦½" label_selected="ç€è¦½" name="dictionary_path_browse"/> + <button label="匯入" name="ok_btn"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_stats.xml b/indra/newview/skins/default/xui/zh/floater_stats.xml index 74a6fd1d9f6e959bccf334d2d0cfe0fc663d6eba..4af5684ec16f9da281174b25c147f3351d4cfc12 100644 --- a/indra/newview/skins/default/xui/zh/floater_stats.xml +++ b/indra/newview/skins/default/xui/zh/floater_stats.xml @@ -10,12 +10,15 @@ </stat_view> <stat_view label="進階" name="advanced"> <stat_view label="呈åƒ" name="render"> - <stat_bar label="KTris 繪出" name="ktrisframe"/> - <stat_bar label="KTris 繪出" name="ktrissec"/> + <stat_bar label="繪出的 KTris(æ¯å¹€ï¼‰" name="ktrisframe"/> + <stat_bar label="繪出的 KTris(æ¯ç§’)" name="ktrissec"/> <stat_bar label="物件總計" name="objs"/> <stat_bar label="新物件" name="newobjs"/> + <stat_bar label="物件快å–讀å–率" name="object_cache_hits"/> </stat_view> <stat_view label="æè³ª" name="texture"> + <stat_bar label="å¿«å–讀å–率" name="texture_cache_hits"/> + <stat_bar label="å¿«å–讀å–å»¶é²" name="texture_cache_read_latency"/> <stat_bar label="計數" name="numimagesstat"/> <stat_bar label="原始計數" name="numrawimagesstat"/> <stat_bar label="GL 記憶" name="gltexmemstat"/> @@ -50,7 +53,13 @@ <stat_bar label="物件" name="simobjects"/> <stat_bar label="使用ä¸ç‰©ä»¶" name="simactiveobjects"/> <stat_bar label="使用ä¸è…³æœ¬" name="simactivescripts"/> + <stat_bar label="腳本執行" name="simpctscriptsrun"/> <stat_bar label="腳本事件" name="simscripteps"/> + <stat_view label="尋徑" name="simpathfinding"> + <stat_bar label="人工智慧æ¥é©Ÿæ™‚é–“" name="simsimaistepmsec"/> + <stat_bar label="已略éŽè¼ªå»“æ¥é©Ÿ" name="simsimskippedsilhouettesteps"/> + <stat_bar label="角色已更新" name="simsimpctsteppedcharacters"/> + </stat_view> <stat_bar label="進入å°åŒ…" name="siminpps"/> <stat_bar label="出去å°åŒ…" name="simoutpps"/> <stat_bar label="擱置下載" name="simpendingdownloads"/> @@ -64,6 +73,14 @@ <stat_bar label="用戶時間" name="simagentmsec"/> <stat_bar label="åœ–åƒæ™‚é–“" name="simimagesmsec"/> <stat_bar label="腳本時間" name="simscriptmsec"/> + <stat_bar label="閒置時間" name="simsparemsec"/> + <stat_view label="時間細節(毫秒)" name="timedetails"> + <stat_bar label="ç‰©ç†æ¥é©Ÿ" name="simsimphysicsstepmsec"/> + <stat_bar label="更新物ç†å½¢ç‹€" name="simsimphysicsshapeupdatemsec"/> + <stat_bar label="物ç†ï¼ˆå…¶ä»–)" name="simsimphysicsothermsec"/> + <stat_bar label="ç¡çœ 時間" name="simsleepmsec"/> + <stat_bar label="Pump IO" name="simpumpiomsec"/> + </stat_view> </stat_view> </stat_view> </container_view> diff --git a/indra/newview/skins/default/xui/zh/floater_test_layout_stacks.xml b/indra/newview/skins/default/xui/zh/floater_test_layout_stacks.xml new file mode 100644 index 0000000000000000000000000000000000000000..68fcf3f7f752c4f8a8b190fb8db18fdee9214509 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_test_layout_stacks.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Test Floater" title="LAYOUTSTACK 測試"/> diff --git a/indra/newview/skins/default/xui/zh/floater_test_text_vertical_aligment.xml b/indra/newview/skins/default/xui/zh/floater_test_text_vertical_aligment.xml new file mode 100644 index 0000000000000000000000000000000000000000..83b6df6fe5a8b7114ac485aa79bf1fe73029a6b4 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_test_text_vertical_aligment.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Test Floater" title="測試浮動視窗"/> diff --git a/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml b/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml index 16b3409a826376a0c477ffc467096d10c0f762b4..e909a67e2c6661687665b5160ce5e5f76fdeb211 100644 --- a/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml +++ b/indra/newview/skins/default/xui/zh/floater_texture_ctrl.xml @@ -9,15 +9,27 @@ <text name="Multiple"> å¤šé‡æè³ª </text> + <radio_group name="mode_selection"> + <radio_item label="æ”¶ç´å€" name="inventory" value="0"/> + <radio_item label="本地" name="local" value="1"/> + </radio_group> <text name="unknown"> 尺寸:[DIMENSIONS] </text> <button label="é è¨" label_selected="é è¨" name="Default"/> - <button label="ç„¡" label_selected="ç„¡" name="None"/> <button label="空白" label_selected="空白" name="Blank"/> - <check_box initial_value="true" label="ç«‹å³å¥—用" name="apply_immediate_check"/> + <button label="ç„¡" label_selected="ç„¡" name="None"/> + <check_box initial_value="true" label="實時é 覽" name="apply_immediate_check"/> + <text name="preview_disabled" value="å·²åœç”¨é 覽"/> <filter_editor label="æè³ªéŽæ¿¾å™¨" name="inventory search editor"/> <check_box initial_value="false" label="顯示資料夾" name="show_folders_check"/> + <button label="æ·»åŠ " label_selected="æ·»åŠ " name="l_add_btn"/> + <button label="移除" label_selected="移除" name="l_rem_btn"/> + <button label="上傳" label_selected="上傳" name="l_upl_btn"/> + <scroll_list name="l_name_list"> + <column label="å稱" name="unit_name"/> + <column label="ID" name="unit_id_HIDDEN"/> + </scroll_list> <button label="確定" label_selected="確定" name="Select"/> <button label="å–æ¶ˆ" label_selected="å–æ¶ˆ" name="Cancel"/> </floater> diff --git a/indra/newview/skins/default/xui/zh/floater_texture_fetch_debugger.xml b/indra/newview/skins/default/xui/zh/floater_texture_fetch_debugger.xml new file mode 100644 index 0000000000000000000000000000000000000000..0dcac17a7564174cd2c95f3624b65b32863e0383 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_texture_fetch_debugger.xml @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="TexFetchDebugger" title="æè³ªæ“·å–除錯器"> + <text name="total_num_fetched_label"> + 1. å·²æ“·å–æè³ªç¸½æ•¸ï¼š[NUM] + </text> + <text name="total_num_fetching_requests_label"> + 2. 總擷å–請求數:[NUM] + </text> + <text name="total_num_cache_hits_label"> + 3. å¿«å–ç¸½è®€å–æ•¸ï¼š[NUM] + </text> + <text name="total_num_visible_tex_label"> + 4. å¯è¦‹æè³ªç¸½æ•¸ï¼š[NUM] + </text> + <text name="total_num_visible_tex_fetch_req_label"> + 5. å¯è¦‹æè³ªæ“·å–總請求數:[NUM] + </text> + <text name="total_fetched_data_label"> + 6. 總擷å–資料é‡ï¼š[SIZE1] KB,解碼資料é‡ï¼š[SIZE2] KB,[PIXEL] 百è¬åƒç´ + </text> + <text name="total_fetched_vis_data_label"> + 7. å¯è¦‹è³‡æ–™ç¸½é‡ï¼š[SIZE1] KB,解碼資料é‡ï¼š[SIZE2] KB + </text> + <text name="total_fetched_rendered_data_label"> + 8. 總呈åƒè³‡æ–™é‡ï¼š[SIZE1] KB,解碼資料é‡ï¼š[SIZE2] KB,[PIXEL] 百è¬åƒç´ + </text> + <text name="total_time_cache_read_label"> + 9. å¿«å–讀å–總時間:[TIME] ç§’ + </text> + <text name="total_time_cache_write_label"> + 10. å¿«å–寫入總時間:[TIME] ç§’ + </text> + <text name="total_time_decode_label"> + 11. 解碼總時間:[TIME] ç§’ + </text> + <text name="total_time_gl_label"> + 12. 建立 gl æè³ªç¸½æ™‚間:[TIME] ç§’ + </text> + <text name="total_time_http_label"> + 13. HTTP æ“·å–總時間:[TIME] ç§’ + </text> + <text name="total_time_fetch_label"> + 14. 所有擷å–動作總時間:[TIME] ç§’ + </text> + <text name="total_time_refetch_vis_cache_label"> + 15. 自快å–釿–°æ“·å–å¯è¦‹æè³ªï¼Œæ™‚間:[TIME] 秒,擷å–é‡ï¼š[SIZE] KB,[PIXEL] 百è¬åƒç´ + </text> + <text name="total_time_refetch_all_cache_label"> + 16. 從快å–釿–°æ“·å–所有æè³ªï¼Œæ™‚間:[TIME] 秒,擷å–é‡ï¼š[SIZE] KB,[PIXEL] 百è¬åƒç´ + </text> + <text name="total_time_refetch_vis_http_label"> + 17. 自 HTTP 釿–°æ“·å–å¯è¦‹æè³ªï¼Œæ™‚間:[TIME] 秒,擷å–é‡ï¼š[SIZE] KB,[PIXEL] 百è¬åƒç´ + </text> + <text name="total_time_refetch_all_http_label"> + 18. 自 HTTP 釿–°æ“·å–所有æè³ªï¼Œæ™‚間:[TIME] 秒,擷å–é‡ï¼š[SIZE] KB,[PIXEL] 百è¬åƒç´ + </text> + <spinner label="19. æè³ª/åƒç´ 比率:" name="texel_pixel_ratio"/> + <text name="texture_source_label"> + 20. æè³ªä¾†æºï¼š + </text> + <radio_group name="texture_source"> + <radio_item label="å¿«å– + HTTP" name="0"/> + <radio_item label="åƒ…é™ HTTP" name="1"/> + </radio_group> + <button label="é–‹å§‹" name="start_btn"/> + <button label="é‡è¨" name="clear_btn"/> + <button label="關閉" name="close_btn"/> + <button label="å¿«å–讀å–" name="cacheread_btn"/> + <button label="å¿«å–寫入" name="cachewrite_btn"/> + <button label="HTTP" name="http_btn"/> + <button label="解碼" name="decode_btn"/> + <button label="GL æè³ª" name="gl_btn"/> + <button label="å¿«å–é‡å–å¯è¦‹æè³ª" name="refetchviscache_btn"/> + <button label="釿–°æ“·å–所有快å–" name="refetchallcache_btn"/> + <button label="HTTP é‡å–å¯è¦‹æè³ª" name="refetchvishttp_btn"/> + <button label="釿–°æ“·å–所有 HTTP" name="refetchallhttp_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_tools.xml b/indra/newview/skins/default/xui/zh/floater_tools.xml index 62d1b6a82095a58d8b23d227e5352ef3182f195f..33c9ebeaf084a9809f1db64c0f5808c98e8a02d9 100644 --- a/indra/newview/skins/default/xui/zh/floater_tools.xml +++ b/indra/newview/skins/default/xui/zh/floater_tools.xml @@ -1,5 +1,20 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater name="toolbox floater" short_title="å»ºé€ å·¥å…·"> + <floater.string name="grid_screen_text"> + 螢幕 + </floater.string> + <floater.string name="grid_local_text"> + 本地 + </floater.string> + <floater.string name="grid_world_text"> + 世界 + </floater.string> + <floater.string name="grid_reference_text"> + åƒè€ƒ + </floater.string> + <floater.string name="grid_attachment_text"> + 附件 + </floater.string> <floater.string name="status_rotate"> 拖曳色æ¢å¯ä»¥æ—‹è½‰ç‰©ä»¶ </floater.string> @@ -24,20 +39,11 @@ <floater.string name="status_selectland"> 按ä½ä¸¦æ‹–曳,å¯ä»¥é¸å–土地 </floater.string> - <floater.string name="grid_screen_text"> - 螢幕 + <floater.string name="status_selectcount"> + é¸å–了 [OBJ_COUNT] å€‹ç‰©ä»¶ï¼ŒåœŸåœ°è¡æ“Šé‡ [LAND_IMPACT] </floater.string> - <floater.string name="grid_local_text"> - 本地 - </floater.string> - <floater.string name="grid_world_text"> - 世界 - </floater.string> - <floater.string name="grid_reference_text"> - åƒè€ƒ - </floater.string> - <floater.string name="grid_attachment_text"> - 附件 + <floater.string name="status_remaining_capacity"> + 剩餘容ç´é‡ [LAND_CAPACITY]。 </floater.string> <button name="button focus" tool_tip="èšç„¦"/> <button name="button move" tool_tip="移動"/> @@ -67,20 +73,17 @@ <check_box label="編輯è¯çµéƒ¨åˆ†" name="checkbox edit linked parts"/> <button label="è¯çµ" name="link_btn"/> <button label="å–æ¶ˆè¯çµ" name="unlink_btn"/> - <text name="RenderingCost" tool_tip="é¡¯ç¤ºé€™ç‰©ä»¶ç¶“è¨ˆç®—æ‰€å¾—çš„å‘ˆåƒæˆæœ¬"> - þ:[COUNT] - </text> <text label="åŒæ™‚伸展兩å´" name="checkbox uniform label"> åŒæ™‚ä¼¸å±•å…©å´ </text> <check_box initial_value="true" label="伸展æè³ª" name="checkbox stretch textures"/> - <check_box initial_value="true" label="è²¼é½Šæ ¼ç·š" name="checkbox snap to grid"/> + <check_box initial_value="true" label="Snap" name="checkbox snap to grid"/> <combo_box name="combobox grid mode" tool_tip="鏿“‡ç‰©ä»¶å®šä½åƒè€ƒçš„æ ¼ç·šå°ºåº¦é¡žåž‹"> - <combo_box.item label="ä¸–ç•Œæ ¼ç·š" name="World"/> - <combo_box.item label="åœ°æ–¹æ ¼ç·š" name="Local"/> - <combo_box.item label="åƒè€ƒæ ¼ç·š" name="Reference"/> + <combo_box.item label="世界" name="World"/> + <combo_box.item label="本地" name="Local"/> + <combo_box.item label="åƒè€ƒ" name="Reference"/> </combo_box> - <button name="Options..." tool_tip="å¯Ÿçœ‹æ›´å¤šæ ¼ç·šé¸é …"/> + <button label="" name="Options..." tool_tip="å¯Ÿçœ‹æ›´å¤šæ ¼ç·šé¸é …"/> <button name="ToolCube" tool_tip="立方體"/> <button name="ToolPrism" tool_tip="稜é¡é«”"/> <button name="ToolPyramid" tool_tip="金å—å¡”"/> @@ -121,23 +124,11 @@ </text> <slider_bar initial_value="0.00" name="slider force"/> <button label="套用" label_selected="套用" name="button apply to selection" tool_tip="ä¿®æ”¹æ‰€é¸æ“‡çš„土地"/> - <text name="obj_count"> - 物件: [COUNT] - </text> - <text name="prim_count"> - 幾何元件: [COUNT] - </text> - <text name="linked_set_count"> - è¯çµé›†åˆï¼š[COUNT] - </text> - <text name="linked_set_cost" tool_tip="ç›®å‰æ‰€é¸è¯çµå¹¾ä½• [prims],[physics complexity] 的費用"> - 費用:[COST] / [PHYSICS] - </text> - <text name="object_count"> - 物件: [COUNT] + <text name="selection_empty"> + 未åšä»»ä½•鏿“‡ã€‚ </text> - <text name="object_cost" tool_tip="ç›®å‰æ‰€é¸ç‰©ä»¶ [prims] / [physics complexity] 的費用"> - 費用:[COST] / [PHYSICS] + <text name="remaining_capacity"> + [CAPACITY_STRING] [secondlife:///app/openfloater/object_weights 詳情] </text> <tab_container name="Object Info Tabs"> <panel label="一般" name="General"> @@ -159,6 +150,12 @@ <panel.string name="text modify info 4"> ä½ ä¸èƒ½ä¿®æ”¹é€™äº›ç‰©ä»¶ </panel.string> + <panel.string name="text modify info 5"> + 無法跨地å€ä¿®æ”¹é€™å€‹ç‰©ä»¶ + </panel.string> + <panel.string name="text modify info 6"> + 無法跨地å€ä¿®æ”¹é€™äº›ç‰©ä»¶ + </panel.string> <panel.string name="text modify warning"> ä½ å¿…é ˆé¸å–整個物件以è¨å®šæ¬Šé™ </panel.string> @@ -208,12 +205,12 @@ <combo_box.item label="縮放" name="Zoom"/> </combo_box> <check_box label="出售ä¸ï¼š" name="checkbox for sale"/> + <spinner label="L$" name="Edit Cost"/> <combo_box name="sale type"> <combo_box.item label="æšåº¨" name="Copy"/> <combo_box.item label="內容" name="Contents"/> <combo_box.item label="原件" name="Original"/> </combo_box> - <spinner label="åƒ¹æ ¼ï¼š L$" name="Edit Cost"/> <check_box label="顯示在æœå°‹ä¸" name="search_check" tool_tip="讓其他人å¯ä»¥åœ¨æœå°‹çµæžœä¸å¯Ÿçœ‹åˆ°æ¤ç‰©ä»¶"/> <panel name="perms_build"> <text name="perm_modify"> @@ -249,6 +246,11 @@ F: </text> </panel> + <panel name="pathfinding_attrs_panel"> + <text name="pathfinding_attributes_label"> + 尋徑屬性: + </text> + </panel> </panel> <panel label="物件" name="Object"> <check_box label="已鎖ä½" name="checkbox locked" tool_tip="é¿å…物件被移動或刪除。 這在建製éŽç¨‹ä¸éžå¸¸æœ‰ç”¨ï¼Œå¯é¿å…æ„外的編輯更動。"/> @@ -350,12 +352,10 @@ 縫åˆé¡žåž‹ </text> <combo_box name="sculpt type control"> - <combo_box.item label="(無)" name="None"/> <combo_box.item label="çƒé«”" name="Sphere"/> <combo_box.item label="ç’°é¢" name="Torus"/> <combo_box.item label="å¹³é¢" name="Plane"/> <combo_box.item label="圓柱體" name="Cylinder"/> - <combo_box.item label="ç¶²é¢" name="Mesh"/> </combo_box> </panel> <panel label="特性" name="Features"> @@ -407,7 +407,7 @@ </combo_box> <spinner label="é‡åŠ›" name="Physics Gravity"/> <spinner label="摩擦" name="Physics Friction"/> - <spinner label="密度" name="Physics Density"/> + <spinner label="密度(100 公斤 / 立方公尺)" name="Physics Density"/> <spinner label="æ¢å¾©" name="Physics Restitution"/> </panel> <panel label="æè³ª" name="Texture"> diff --git a/indra/newview/skins/default/xui/zh/floater_top_objects.xml b/indra/newview/skins/default/xui/zh/floater_top_objects.xml index ad12ae6720f4b46c39cbe59b0f0d15f0110f66b5..6f50be085540092003d76e89a69e092eb9d524ec 100644 --- a/indra/newview/skins/default/xui/zh/floater_top_objects.xml +++ b/indra/newview/skins/default/xui/zh/floater_top_objects.xml @@ -9,9 +9,6 @@ <floater.string name="scripts_score_label"> 時間 </floater.string> - <floater.string name="scripts_mono_time_label"> - Mono 時間 - </floater.string> <floater.string name="top_colliders_title"> æœ€å¸¸ç¢°æ’žç‰©é … </floater.string> @@ -32,9 +29,10 @@ <scroll_list.columns label="å稱" name="name"/> <scroll_list.columns label="所有人" name="owner"/> <scroll_list.columns label="ä½ç½®" name="location"/> + <scroll_list.columns label="地段" name="parcel"/> <scroll_list.columns label="時間" name="time"/> - <scroll_list.columns label="Mono 時間" name="mono_time"/> <scroll_list.columns label="URL" name="URLs"/> + <scroll_list.columns label="記憶體 (KB)" name="memory"/> </scroll_list> <text name="id_text"> 物件 ID: @@ -48,6 +46,10 @@ 所有人: </text> <button label="éŽæ¿¾å™¨" name="filter_owner_btn"/> + <text name="parcel_name_text"> + 地段: + </text> + <button label="éŽæ¿¾å™¨" name="filter_parcel_btn"/> <button label="é€€å›žæ‰€é¸æ“‡çš„" name="return_selected_btn"/> <button label="全部退回" name="return_all_btn"/> <button label="åœç”¨æ‰€é¸çš„" name="disable_selected_btn"/> diff --git a/indra/newview/skins/default/xui/zh/floater_toybox.xml b/indra/newview/skins/default/xui/zh/floater_toybox.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc6b6ea6114caa5811e2f48d3cf4579629b480ba --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_toybox.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="Toybox" title="工具列按鈕"> + <text name="toybox label 1"> + 將按鈕拖入或拖離工具列,å³å¯æ–°å¢žæˆ–移除該按鈕。 + </text> + <text name="toybox label 2"> + æŒ‰éˆ•å°‡æ ¹æ“šæ¯ä¸€å€‹å·¥å…·åˆ—çš„è¨å®šï¼Œåƒ…é¡¯ç¤ºåœ–ç¤ºæˆ–ä¸€ä½µé¡¯ç¤ºæ–‡å—æ¨™ç±¤ã€‚ + </text> + <button label="清除所有工具列" label_selected="清除所有工具列" name="btn_clear_all"/> + <button label="還原é è¨å€¼" label_selected="還原é è¨å€¼" name="btn_restore_defaults"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_translation_settings.xml b/indra/newview/skins/default/xui/zh/floater_translation_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..fe848725576f031512cf510e692659f01aa90259 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/floater_translation_settings.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<floater name="floater_translation_settings" title="èŠå¤©å…§å®¹ç¿»è¯è¨å®š"> + <string name="bing_api_key_not_verified"> + Bing AppID 未通éŽé©—è‰ã€‚ è«‹å†è©¦ä¸€æ¬¡ã€‚ + </string> + <string name="google_api_key_not_verified"> + Google API éµå€¼æœªé€šéŽé©—è‰ã€‚ è«‹å†è©¦ä¸€æ¬¡ã€‚ + </string> + <string name="bing_api_key_verified"> + Bing AppID é©—è‰æˆåŠŸã€‚ + </string> + <string name="google_api_key_verified"> + Google API éµå€¼é©—è‰æˆåŠŸã€‚ + </string> + <check_box label="èŠå¤©æ™‚啟用機器翻è¯" name="translate_chat_checkbox"/> + <text name="translate_language_label"> + å°‡èŠå¤©å…§å®¹ç¿»è¯æˆï¼š + </text> + <combo_box name="translate_language_combo"> + <combo_box.item label="系統é è¨" name="System Default Language"/> + <combo_box.item label="英語" name="English"/> + <combo_box.item label="Dansk(丹麥語)" name="Danish"/> + <combo_box.item label="Deutsch(德語)" name="German"/> + <combo_box.item label="Español(西ç牙語)" name="Spanish"/> + <combo_box.item label="Français(法語)" name="French"/> + <combo_box.item label="Italiano(義大利語)" name="Italian"/> + <combo_box.item label="Magyar(匈牙利語)" name="Hungarian"/> + <combo_box.item label="Nederlands(è·è˜èªžï¼‰" name="Dutch"/> + <combo_box.item label="Polski(波è˜èªžï¼‰" name="Polish"/> + <combo_box.item label="Português(葡è„牙語)" name="Portugese"/> + <combo_box.item label="РуÑÑкий(俄羅斯語)" name="Russian"/> + <combo_box.item label="Türkçe(土耳其語)" name="Turkish"/> + <combo_box.item label="УкраїнÑька(çƒå…‹è˜èªžï¼‰" name="Ukrainian"/> + <combo_box.item label="䏿–‡ï¼ˆæ£é«”)" name="Chinese"/> + <combo_box.item label="日本語(日語)" name="Japanese"/> + <combo_box.item label="한êµì–´ï¼ˆæ¼¢èªžï¼‰" name="Korean"/> + </combo_box> + <text name="tip"> + 鏿“‡ç¿»è¯æœå‹™ï¼š + </text> + <radio_group name="translation_service_rg"> + <radio_item initial_value="bing" label="Bing ç¿»è¯å™¨" name="bing"/> + <radio_item initial_value="google" label="Google ç¿»è¯" name="google"/> + </radio_group> + <text name="bing_api_key_label"> + Bing [http://www.bing.com/developers/createapp.aspx AppID]: + </text> + <button label="é©—è‰" name="verify_bing_api_key_btn"/> + <text name="google_api_key_label"> + Google [http://code.google.com/apis/language/translate/v2/getting_started.html#auth API éµå€¼]: + </text> + <button label="é©—è‰" name="verify_google_api_key_btn"/> + <text name="google_links_text"> + [http://code.google.com/apis/language/translate/v2/pricing.html åƒ¹æ ¼è¡¨] | [https://code.google.com/apis/console 統計] + </text> + <button label="確定" name="ok_btn"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> +</floater> diff --git a/indra/newview/skins/default/xui/zh/floater_voice_controls.xml b/indra/newview/skins/default/xui/zh/floater_voice_controls.xml index fa724a4736b44489e4740498eb956cc9a09f669c..a3a7679957e47c4339b4b2bfdd62c5419cfdc022 100644 --- a/indra/newview/skins/default/xui/zh/floater_voice_controls.xml +++ b/indra/newview/skins/default/xui/zh/floater_voice_controls.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <floater name="floater_voice_controls" title="語音控制"> <string name="title_nearby"> - 附近的語音 + 語音è¨å®š </string> <string name="title_group"> 與 [GROUP] 進行群組通話 @@ -10,7 +10,7 @@ 多方通話 </string> <string name="title_peer_2_peer"> - 與 [NAME] 進行通話 + å’Œ [NAME] 通話 </string> <string name="no_one_near"> 附近沒有一人開啟語音 diff --git a/indra/newview/skins/default/xui/zh/floater_voice_effect.xml b/indra/newview/skins/default/xui/zh/floater_voice_effect.xml index 184ee6edd5f406d670f4954c8ab02469e9cfc332..81e0204262e185a5f3085e479b85e54385aa734e 100644 --- a/indra/newview/skins/default/xui/zh/floater_voice_effect.xml +++ b/indra/newview/skins/default/xui/zh/floater_voice_effect.xml @@ -12,6 +12,135 @@ <string name="new_voice_effect"> (新的ï¼ï¼‰ </string> + <string name="effect_Arena"> + ç«¶æŠ€å ´ + </string> + <string name="effect_Beast"> + é‡Žç¸ + </string> + <string name="effect_Buff"> + Buff + </string> + <string name="effect_Buzz"> + Buzz + </string> + <string name="effect_Camille"> + å¡èœœå…’ + </string> + <string name="effect_Creepy"> + 怪異 + </string> + <string name="effect_CreepyBot"> + 怪異機器人 + </string> + <string name="effect_Cyber"> + ç§‘å¹» + </string> + <string name="effect_DeepBot"> + 深沉機器人 + </string> + <string name="effect_Demon"> + é”鬼 + </string> + <string name="effect_Female Elf"> + 女性 - å°ç²¾éˆ + </string> + <string name="effect_Flirty"> + 調情 + </string> + <string name="effect_Foxy"> + 香豔 + </string> + <string name="effect_Halloween 2010 Bonus"> + 2010è¬è–ç¯€åŠ è´ˆ + </string> + <string name="effect_Helium"> + 氦氣 + </string> + <string name="effect_Husky"> + 沙啞 + </string> + <string name="effect_Husky Whisper"> + 沙啞耳語 + </string> + <string name="effect_Intercom"> + å°è¬›æ©Ÿ + </string> + <string name="effect_Julia"> + 茱莉亞 + </string> + <string name="effect_Lo Lilt"> + 輕微抑æšé “挫 + </string> + <string name="effect_Macho"> + 陽剛 + </string> + <string name="effect_Micro"> + Micro + </string> + <string name="effect_Mini"> + è¿·ä½ + </string> + <string name="effect_Model"> + 模型 + </string> + <string name="effect_Nano"> + Nano + </string> + <string name="effect_Nightmare"> + 惡夢 + </string> + <string name="effect_PopBot"> + PopBot + </string> + <string name="effect_Rachel"> + 瑞秋 + </string> + <string name="effect_Radio"> + 收音機 + </string> + <string name="effect_Robot"> + 機器人 + </string> + <string name="effect_Roxanne"> + è˜¿å§ + </string> + <string name="effect_Rumble"> + ä½Žæ²‰éš†éš†è² + </string> + <string name="effect_Sabrina"> + 薩賓娜 + </string> + <string name="effect_Samantha"> + å§æ›¼èŽŽ + </string> + <string name="effect_Sexy"> + 性感 + </string> + <string name="effect_Shorty"> + 矮個兒 + </string> + <string name="effect_Smaller"> + è¼ƒå° + </string> + <string name="effect_Sneaky"> + 鬼祟 + </string> + <string name="effect_Stallion"> + 種馬 + </string> + <string name="effect_Sultry"> + å‹¾é‚ + </string> + <string name="effect_Thunder"> + é›·è² + </string> + <string name="effect_Vixen"> + 潑婦 + </string> + <string name="effect_WhinyBot"> + å“鬧機器人 + </string> <text name="preview_text"> é 覽 </text> diff --git a/indra/newview/skins/default/xui/zh/floater_window_size.xml b/indra/newview/skins/default/xui/zh/floater_window_size.xml index 54b72afcccf77287c90b6b84f95a27fa51e0454a..3942f72d76c821ba5b69c39938e88ad91b29179d 100644 --- a/indra/newview/skins/default/xui/zh/floater_window_size.xml +++ b/indra/newview/skins/default/xui/zh/floater_window_size.xml @@ -7,10 +7,17 @@ è¨å®šè¦–窗尺寸大å°ï¼š </text> <combo_box name="window_size_combo" tool_tip="寬度 x 高度"> - <combo_box.item label="1000 x 700 (é è¨ï¼‰" name="item0"/> - <combo_box.item label="1024 x 768" name="item1"/> - <combo_box.item label="1280 x 720 (720p)" name="item2"/> - <combo_box.item label="1920 x 1080 (1080p)" name="item3"/> + <combo_box.item label="1000 x 700 (é è¨ï¼‰" name="item1"/> + <combo_box.item label="1024 x 768 (4:3 XGA)" name="item2"/> + <combo_box.item label="1280 x 720 (16:9 HDTV)" name="item3"/> + <combo_box.item label="1280 x 800 (5:8 WXGA)" name="item4"/> + <combo_box.item label="1280 x 1024 (5:4 SXGA)" name="item5"/> + <combo_box.item label="1440 x 900 (8:5 WSXGA)" name="item7"/> + <combo_box.item label="1600 x 900 (16:9 HD+)" name="item8"/> + <combo_box.item label="1600 x 1200 (4:3 UXGA)" name="item9"/> + <combo_box.item label="1680 x 1050 (8:5 WSXGA+)" name="item10"/> + <combo_box.item label="1920 x 1080 (16:9 HDTV)" name="item11"/> + <combo_box.item label="1920 x 1200 (8:5 WUXGA)" name="item12"/> </combo_box> <button label="è¨å®š" name="set_btn"/> <button label="å–æ¶ˆ" name="cancel_btn"/> diff --git a/indra/newview/skins/default/xui/zh/menu_hide_navbar.xml b/indra/newview/skins/default/xui/zh/menu_hide_navbar.xml index dbb8ececaac70851b0b0e053f88051a2d841d52f..3f6820cb324c75dd2a02f3b83eb36c29b101b89d 100644 --- a/indra/newview/skins/default/xui/zh/menu_hide_navbar.xml +++ b/indra/newview/skins/default/xui/zh/menu_hide_navbar.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <menu name="hide_navbar_menu"> - <menu_item_check label="顯示導覽列" name="ShowNavbarNavigationPanel"/> + <menu_item_check label="顯示導覽列和我的最愛" name="ShowNavbarNavigationPanel"/> <menu_item_check label="顯示最愛列" name="ShowNavbarFavoritesPanel"/> <menu_item_check label="é¡¯ç¤ºè¿·ä½ ä½ç½®åˆ—" name="ShowMiniLocationPanel"/> </menu> diff --git a/indra/newview/skins/default/xui/zh/menu_inspect_object_gear.xml b/indra/newview/skins/default/xui/zh/menu_inspect_object_gear.xml index cedc781fa26aad3ec5d0e11e8c083e624d1ce1f3..abff0f64ac137cbe3c7f83c18ab3f63c4cc81786 100644 --- a/indra/newview/skins/default/xui/zh/menu_inspect_object_gear.xml +++ b/indra/newview/skins/default/xui/zh/menu_inspect_object_gear.xml @@ -12,6 +12,7 @@ <menu_item_call label="æ·»åŠ " name="add"/> <menu_item_call label="å›žå ±" name="report"/> <menu_item_call label="å°éŽ–" name="block"/> + <menu_item_call label="解除å°éŽ–" name="unblock"/> <menu_item_call label="放大" name="zoom_in"/> <menu_item_call label="移除" name="remove"/> <menu_item_call label="更多資訊" name="more_info"/> diff --git a/indra/newview/skins/default/xui/zh/menu_inventory.xml b/indra/newview/skins/default/xui/zh/menu_inventory.xml index 63ec001e4a10095ba980166812ab9e53595f99a4..7f745ffaa7eeb8497ffad5aa596c1a3e3a98aec0 100644 --- a/indra/newview/skins/default/xui/zh/menu_inventory.xml +++ b/indra/newview/skins/default/xui/zh/menu_inventory.xml @@ -59,6 +59,7 @@ <menu_item_call label="屬性" name="Properties"/> <menu_item_call label="æ›´å" name="Rename"/> <menu_item_call label="覆製資產 UUID" name="Copy Asset UUID"/> + <menu_item_call label="剪下" name="Cut"/> <menu_item_call label="æšåº¨" name="Copy"/> <menu_item_call label="貼上" name="Paste"/> <menu_item_call label="ä»¥é€£çµæ ¼å¼è²¼ä¸Š" name="Paste As Link"/> @@ -67,6 +68,7 @@ <menu_item_call label="刪除系統資料夾" name="Delete System Folder"/> <menu_item_call label="發起多方通話" name="Conference Chat Folder"/> <menu_item_call label="æ’æ”¾" name="Sound Play"/> + <menu_item_call label="覆製 SLurl" name="url_copy"/> <menu_item_call label="æ·»åŠ åœ°æ¨™" name="About Landmark"/> <menu_item_call label="åœ¨è™›æ“¬ä¸–ç•Œæ’æ”¾" name="Animation Play"/> <menu_item_call label="åœ¨æœ¬åœ°æ’æ”¾" name="Animation Audition"/> @@ -83,5 +85,7 @@ <menu_item_call label="編輯" name="Wearable Edit"/> <menu_item_call label="æ·»åŠ " name="Wearable Add"/> <menu_item_call label="脫下" name="Take Off"/> + <menu_item_call label="複製到商家發件匣" name="Merchant Copy"/> + <menu_item_call label="é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集" name="Marketplace Send"/> <menu_item_call label="-- ç„¡é¸é … --" name="--no options--"/> </menu> diff --git a/indra/newview/skins/default/xui/zh/menu_login.xml b/indra/newview/skins/default/xui/zh/menu_login.xml index f7b2cd7b8ba9712debaf5612f59de9e95d6c79c9..c327b132a11e7b2bbcc3ad76273a8f34167da560 100644 --- a/indra/newview/skins/default/xui/zh/menu_login.xml +++ b/indra/newview/skins/default/xui/zh/menu_login.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <menu_bar name="Login Menu"> <menu label="我自己" name="File"> - <menu_item_call label="å好è¨å®š" name="Preferences..."/> + <menu_item_call label="å好è¨å®šâ€¦" name="Preferences..."/> <menu_item_call label="退出 [APP_NAME]" name="Quit"/> </menu> <menu label="幫助" name="Help"> @@ -17,8 +17,8 @@ <menu_item_call label="è¨å®šè¦–窗大å°..." name="Set Window Size..."/> <menu_item_call label="顯示 TOS" name="TOS"/> <menu_item_call label="顯示嚴é‡è¨Šæ¯" name="Critical"/> - <menu_item_call label="媒體ç€è¦½å™¨æ¸¬è©¦" name="Web Browser Test"/> - <menu_item_call label="ç¶²é 內容浮動視窗測試" name="Web Content Floater Test"/> + <menu_item_call label="ç¶²é 內容浮動視窗除錯測試" name="Web Content Floater Debug Test"/> + <menu label="è¨å®šè¨˜éŒ„細節" name="Set Logging Level"/> <menu_item_check label="é¡¯ç¤ºç¶²æ ¼æŒ‘é¸å™¨" name="Show Grid Picker"/> <menu_item_call label="顯示通知控制å°" name="Show Notifications Console"/> </menu> diff --git a/indra/newview/skins/default/xui/zh/menu_media_ctrl.xml b/indra/newview/skins/default/xui/zh/menu_media_ctrl.xml index bab1bbb8f998cb8afe2850e56d11fc4472afed5d..d222d7f658532cea8aa6d54f6361a77370775d52 100644 --- a/indra/newview/skins/default/xui/zh/menu_media_ctrl.xml +++ b/indra/newview/skins/default/xui/zh/menu_media_ctrl.xml @@ -3,4 +3,5 @@ <menu_item_call label="剪下" name="Cut"/> <menu_item_call label="æšåº¨" name="Copy"/> <menu_item_call label="貼上" name="Paste"/> + <menu_item_call label="開啟網é 嵿Ÿ¥å™¨" name="open_webinspector"/> </context_menu> diff --git a/indra/newview/skins/default/xui/zh/menu_object.xml b/indra/newview/skins/default/xui/zh/menu_object.xml index 94172eb53188fa545182bd83d41824cab7bd5a72..4282c1e1317ca17e9d7a524b797e5cc080b2a912 100644 --- a/indra/newview/skins/default/xui/zh/menu_object.xml +++ b/indra/newview/skins/default/xui/zh/menu_object.xml @@ -3,6 +3,8 @@ <menu_item_call label="觸碰" name="Object Touch"/> <menu_item_call label="編輯" name="Edit..."/> <menu_item_call label="å»ºé€ " name="Build"/> + <menu_item_call label="在è¯çµé›†è£¡é¡¯ç¤º" name="show_in_linksets"/> + <menu_item_call label="在角色ä¸é¡¯ç¤º" name="show_in_characters"/> <menu_item_call label="打開" name="Open"/> <menu_item_call label="å在這裡" name="Object Sit"/> <menu_item_call label="èµ·ç«‹" name="Object Stand Up"/> diff --git a/indra/newview/skins/default/xui/zh/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/zh/menu_people_nearby_view_sort.xml index 8716a4b89490ad134997df04594d09183d0b585a..6cb0ac3c8939c80a654c44ce70e42e0a3f6f4279 100644 --- a/indra/newview/skins/default/xui/zh/menu_people_nearby_view_sort.xml +++ b/indra/newview/skins/default/xui/zh/menu_people_nearby_view_sort.xml @@ -4,5 +4,6 @@ <menu_item_check label="ä¾å稱排åº" name="sort_name"/> <menu_item_check label="ä¾è·é›¢æŽ’åº" name="sort_distance"/> <menu_item_check label="察看人群圖示" name="view_icons"/> + <menu_item_check label="察看地圖" name="view_map"/> <menu_item_call label="顯示被å°éŽ–çš„å±…æ°‘èˆ‡ç‰©ä»¶" name="show_blocked_list"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/zh/menu_text_editor.xml b/indra/newview/skins/default/xui/zh/menu_text_editor.xml index be48a2ce49630790368b66893d411063758f8100..febc0b8b67414edda977f8207cd45d522ce1548a 100644 --- a/indra/newview/skins/default/xui/zh/menu_text_editor.xml +++ b/indra/newview/skins/default/xui/zh/menu_text_editor.xml @@ -1,5 +1,12 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <context_menu name="Text editor context menu"> + <menu_item_call label="(未知)" name="Suggestion 1"/> + <menu_item_call label="(未知)" name="Suggestion 2"/> + <menu_item_call label="(未知)" name="Suggestion 3"/> + <menu_item_call label="(未知)" name="Suggestion 4"/> + <menu_item_call label="(未知)" name="Suggestion 5"/> + <menu_item_call label="新增到å—å…¸" name="Add to Dictionary"/> + <menu_item_call label="新增到忽略清單" name="Add to Ignore"/> <menu_item_call label="剪下" name="Cut"/> <menu_item_call label="æšåº¨" name="Copy"/> <menu_item_call label="貼上" name="Paste"/> diff --git a/indra/newview/skins/default/xui/zh/menu_toolbars.xml b/indra/newview/skins/default/xui/zh/menu_toolbars.xml new file mode 100644 index 0000000000000000000000000000000000000000..d318992dd5948ade66ea909232fc337e9c3955d2 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/menu_toolbars.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<context_menu name="Toolbars Popup"> + <menu_item_call label="ç§»é™¤æ¤æŒ‰éˆ•" name="Remove button"/> + <menu_item_call label="工具列按鈕…" name="Choose Buttons"/> + <menu_item_check label="圖示和標籤" name="icons_with_text"/> + <menu_item_check label="僅用圖示" name="icons_only"/> +</context_menu> diff --git a/indra/newview/skins/default/xui/zh/menu_viewer.xml b/indra/newview/skins/default/xui/zh/menu_viewer.xml index 262cc2440cebd992b55c3571544517235f061caa..ac0e9e7e358e19c75d4ace097724b013b3cd8147 100644 --- a/indra/newview/skins/default/xui/zh/menu_viewer.xml +++ b/indra/newview/skins/default/xui/zh/menu_viewer.xml @@ -1,51 +1,60 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <menu_bar name="Main Menu"> <menu label="我自己" name="Me"> - <menu_item_call label="å好è¨å®š" name="Preferences"/> - <menu_item_call label="我的塗鴉牆" name="Manage My Account"/> - <menu_item_call label="購買 L$" name="Buy and Sell L$"/> - <menu_item_call label="我的個人檔案" name="Profile"/> - <menu_item_call label="我的外觀" name="ChangeOutfit"/> - <menu_item_check label="我的收ç´å€" name="Inventory"/> - <menu_item_check label="我的收ç´å€" name="ShowSidetrayInventory"/> - <menu_item_check label="我的姿勢" name="Gestures"/> - <menu_item_check label="我的è²éŸ³" name="ShowVoice"/> + <menu_item_call label="檔案..." name="Profile"/> + <menu_item_call label="外觀…" name="ChangeOutfit"/> + <menu_item_call label="鏿“‡ä¸€å€‹åŒ–身…" name="Avatar Picker"/> + <menu_item_check label="æ”¶ç´å€â€¦" name="Inventory"/> + <menu_item_call label="æ–°æ”¶ç´å€è¦–窗" name="NewInventoryWindow"/> + <menu_item_call label="地點…" name="Places"/> + <menu_item_call label="ç²¾é¸åœ°é»žâ€¦" name="Picks"/> + <menu_item_call label="æ”影機控制…" name="Camera Controls"/> <menu label="動作" name="Movement"> <menu_item_call label="å下" name="Sit Down Here"/> <menu_item_check label="飛行" name="Fly"/> <menu_item_check label="以跑代æ¥" name="Always Run"/> <menu_item_call label="åœæ¢æˆ‘身上的動作" name="Stop Animating My Avatar"/> + <menu_item_call label="行走 / è·‘æ¥ / 飛行…" name="Walk / run / fly"/> </menu> - <menu label="我的狀態" name="Status"> + <menu label="狀態" name="Status"> <menu_item_call label="離開" name="Set Away"/> <menu_item_call label="忙碌" name="Set Busy"/> </menu> - <menu_item_call label="è¦æ±‚管ç†å“¡ç‹€æ…‹" name="Request Admin Options"/> - <menu_item_call label="離開管ç†å“¡ç‹€æ…‹" name="Leave Admin Options"/> + <menu_item_call label="購買 L$…" name="Buy and Sell L$"/> + <menu_item_call label="商家發件匣…" name="MerchantOutbox"/> + <menu_item_call label="帳戶主控臺…" name="Manage My Account"/> + <menu_item_call label="å好è¨å®šâ€¦" name="Preferences"/> + <menu_item_call label="工具列按鈕…" name="Toolbars"/> + <menu_item_call label="éš±è—æ‰€æœ‰æŽ§åˆ¶" name="Hide UI"/> + <menu_item_check label="顯示 HUD 附件" name="Show HUD Attachments"/> <menu_item_call label="退出 [APP_NAME]" name="Quit"/> </menu> <menu label="æºé€š" name="Communicate"> - <menu_item_call label="我的朋å‹" name="My Friends"/> - <menu_item_call label="我的群組" name="My Groups"/> - <menu_item_check label="附近的èŠå¤©" name="Nearby Chat"/> + <menu_item_check label="èŠå¤©â€¦" name="Nearby Chat"/> + <menu_item_check label="說話" name="Speak"/> + <menu_item_check label="語音è¨å®šâ€¦" name="Nearby Voice"/> + <menu_item_check label="語音變è²â€¦" name="ShowVoice"/> + <menu_item_check label="姿勢…" name="Gestures"/> + <menu_item_call label="朋å‹" name="My Friends"/> + <menu_item_call label="群組" name="My Groups"/> <menu_item_call label="附近的人群" name="Active Speakers"/> + <menu_item_call label="å°éŽ–æ¸…å–®" name="Block List"/> </menu> <menu label="世界" name="World"> - <menu_item_check label="è¿·ä½ åœ°åœ–" name="Mini-Map"/> + <menu_item_call label="å°‡æ¤è™•記為地標" name="Create Landmark Here"/> + <menu_item_call label="目的地…" name="Destinations"/> <menu_item_check label="世界地圖" name="World Map"/> + <menu_item_check label="è¿·ä½ åœ°åœ–" name="Mini-Map"/> <menu_item_check label="æœå°‹" name="Search"/> + <menu_item_call label="瞬間傳é€å›žå®¶" name="Teleport Home"/> + <menu_item_call label="把這裡è¨ç‚ºæˆ‘的家" name="Set Home to Here"/> <menu_item_call label="å¿«ç…§" name="Take Snapshot"/> - <menu_item_call label="å°‡æ¤è™•記為地標" name="Create Landmark Here"/> - <menu label="地點檔案" name="Land"> - <menu_item_call label="地點檔案" name="Place Profile"/> - <menu_item_call label="土地資料" name="About Land"/> - <menu_item_call label="地å€/é ˜åœ°" name="Region/Estate"/> - </menu> + <menu_item_call label="åœ°é»žå°æª”案" name="Place Profile"/> + <menu_item_call label="土地資料" name="About Land"/> + <menu_item_call label="地å€/é ˜åœ°" name="Region/Estate"/> + <menu_item_call label="æˆ‘æ‰€æ“æœ‰çš„土地…" name="My Land"/> <menu_item_call label="購買這塊土地" name="Buy Land"/> - <menu_item_call label="我的土地" name="My Land"/> <menu label="顯示" name="LandShow"> - <menu_item_check label="移動控制" name="Movement Controls"/> - <menu_item_check label="視角控制" name="Camera Controls"/> <menu_item_check label="ç¦è¶Šç·š" name="Ban Lines"/> <menu_item_check label="指標" name="beacons"/> <menu_item_check label="地產邊界" name="Property Lines"/> @@ -54,15 +63,30 @@ <menu_item_check label="地段屬性" name="Parcel Properties"/> <menu_item_check label="進階é¸å–®" name="Show Advanced Menu"/> </menu> - <menu_item_call label="瞬間傳é€å›žå®¶" name="Teleport Home"/> - <menu_item_call label="è¨å®šå®¶åœ¨æ¤è™•" name="Set Home to Here"/> - <menu label="太陽" name="Environment Settings"> + <menu label="太陽" name="Sun"> <menu_item_call label="日出" name="Sunrise"/> <menu_item_call label="ä¸åˆ" name="Noon"/> <menu_item_call label="æ—¥è½" name="Sunset"/> <menu_item_call label="åˆå¤œ" name="Midnight"/> - <menu_item_call label="é ˜åœ°æ™‚é–“" name="Revert to Region Default"/> - <menu_item_call label="環境編輯器" name="Environment Editor"/> + <menu_item_call label="使用地å€è¨å®š" name="Use Region Settings"/> + </menu> + <menu label="環境編輯器" name="Environment Editor"> + <menu_item_call label="環境è¨å®šâ€¦" name="Environment Settings"/> + <menu label="水的自訂é…ç½®" name="Water Presets"> + <menu_item_call label="新的自訂é…置…" name="new_water_preset"/> + <menu_item_call label="編輯自訂é…置…" name="edit_water_preset"/> + <menu_item_call label="刪除自訂é…置…" name="delete_water_preset"/> + </menu> + <menu label="天空自訂é…ç½®" name="Sky Presets"> + <menu_item_call label="新的自訂é…置…" name="new_sky_preset"/> + <menu_item_call label="編輯自訂é…置…" name="edit_sky_preset"/> + <menu_item_call label="刪除自訂é…置…" name="delete_sky_preset"/> + </menu> + <menu label="日的自訂é…ç½®" name="Day Presets"> + <menu_item_call label="新的自訂é…置…" name="new_day_preset"/> + <menu_item_call label="編輯自訂é…置…" name="edit_day_preset"/> + <menu_item_call label="刪除自訂é…置…" name="delete_day_preset"/> + </menu> </menu> </menu> <menu label="å»ºé€ " name="BuildTools"> @@ -100,6 +124,11 @@ <menu_item_call label="è¨å®šè…³æœ¬ç‚ºåŸ·è¡Œä¸" name="Set Scripts to Running"/> <menu_item_call label="è¨å®šè…³æœ¬ç‚ºéžåŸ·è¡Œä¸" name="Set Scripts to Not Running"/> </menu> + <menu label="尋徑" name="Pathfinding"> + <menu_item_call label="è¯çµé›†â€¦" name="pathfinding_linksets_menu_item"/> + <menu_item_call label="角色…" name="pathfinding_characters_menu_item"/> + <menu_item_call label="察看 / 測試…" name="pathfinding_console_menu_item"/> + </menu> <menu label="é¸é …" name="Options"> <menu_item_check label="顯示進階權é™" name="DebugPermissions"/> <menu_item_check label="åªé¸å–我的物件" name="Select Only My Objects"/> @@ -119,7 +148,6 @@ <menu_item_call label="è²éŸ³ï¼ˆL$[COST])..." name="Upload Sound"/> <menu_item_call label="動作(L$[COST])..." name="Upload Animation"/> <menu_item_call label="模型…" name="Upload Model"/> - <menu_item_call label="模型精éˆâ€¦" name="Upload Model Wizard"/> <menu_item_call label="批é‡ï¼ˆæ¯æª”案 L$[COST] )..." name="Bulk Upload"/> <menu_item_call label="è¨å®šé è¨ä¸Šå‚³æ¬Šé™" name="perm prefs"/> </menu> @@ -127,8 +155,8 @@ <menu_item_call label="é‡åš" name="Redo"/> </menu> <menu label="幫助" name="Help"> + <menu_item_call label="簡易教å¸â€¦" name="How To"/> <menu_item_call label="[SECOND_LIFE] 幫助" name="Second Life Help"/> - <menu_item_check label="啟用æç¤º" name="Enable Hints"/> <menu_item_call label="é•è¦èˆ‰å ±" name="Report Abuse"/> <menu_item_call label="å›žå ±è‡èŸ²" name="Report Bug"/> <menu_item_call label="關於 [APP_NAME]" name="About Second Life"/> @@ -141,37 +169,35 @@ <menu_item_check label="å–æ¶ˆé¡é é™åˆ¶" name="Disable Camera Distance"/> <menu_item_check label="高解æžåº¦å¿«ç…§" name="HighResSnapshot"/> <menu_item_check label="éœéŸ³æ‹æ”快照到硬碟" name="QuietSnapshotsToDisk"/> - <menu_item_check label="壓縮快照å˜åˆ°ç¡¬ç¢Ÿ" name="CompressSnapshotsToDisk"/> <menu label="效能工具" name="Performance Tools"> <menu_item_call label="Lag 測é‡å™¨" name="Lag Meter"/> <menu_item_check label="統計列" name="Statistics Bar"/> - <menu_item_check label="é¡¯ç¤ºåŒ–èº«å‘ˆåƒæˆæœ¬" name="Avatar Rendering Cost"/> + <menu_item_check label="顯示化身的繪製é‡é‡" name="Avatar Rendering Cost"/> </menu> <menu label="高亮顯示與å¯è¦‹åº¦" name="Highlighting and Visibility"> <menu_item_check label="Cheesy 指標" name="Cheesy Beacon"/> <menu_item_check label="éš±è—ç²’åæ•ˆæžœ" name="Hide Particles"/> <menu_item_check label="éš±è—æ‰€é¸æ“‡çš„" name="Hide Selected"/> <menu_item_check label="é«˜äº®é¡¯ç¤ºé€æ˜Žç‰©ä»¶" name="Highlight Transparent"/> - <menu_item_check label="顯示 HUD 附件" name="Show HUD Attachments"/> <menu_item_check label="顯示第一人稱視角準星" name="ShowCrosshairs"/> </menu> <menu label="呈åƒé¡žåž‹" name="Rendering Types"> - <menu_item_check label="ç°¡å–®" name="Simple"/> - <menu_item_check label="åŠé€æ˜Ž" name="Alpha"/> - <menu_item_check label="樹木" name="Tree"/> - <menu_item_check label="化身" name="Character"/> - <menu_item_check label="表é¢ä¿®è£œ" name="Surface Patch"/> - <menu_item_check label="天空" name="Sky"/> - <menu_item_check label="æ°´æ–‡" name="Water"/> - <menu_item_check label="地é¢" name="Ground"/> - <menu_item_check label="é«”ç©" name="Volume"/> - <menu_item_check label="è‰åœ°" name="Grass"/> - <menu_item_check label="雲彩" name="Clouds"/> - <menu_item_check label="ç²’åæ•ˆæžœ" name="Particles"/> - <menu_item_check label="碰撞" name="Bump"/> + <menu_item_check label="ç°¡å–®" name="Rendering Type Simple"/> + <menu_item_check label="åŠé€æ˜Ž" name="Rendering Type Alpha"/> + <menu_item_check label="樹木" name="Rendering Type Tree"/> + <menu_item_check label="化身" name="Rendering Type Character"/> + <menu_item_check label="表é¢ä¿®è£œ" name="Rendering Type Surface Patch"/> + <menu_item_check label="天空" name="Rendering Type Sky"/> + <menu_item_check label="æ°´æ–‡" name="Rendering Type Water"/> + <menu_item_check label="地é¢" name="Rendering Type Ground"/> + <menu_item_check label="é«”ç©" name="Rendering Type Volume"/> + <menu_item_check label="è‰åœ°" name="Rendering Type Grass"/> + <menu_item_check label="雲彩" name="Rendering Type Clouds"/> + <menu_item_check label="ç²’åæ•ˆæžœ" name="Rendering Type Particles"/> + <menu_item_check label="碰撞" name="Rendering Type Bump"/> </menu> <menu label="呈åƒåŠŸèƒ½" name="Rendering Features"> - <menu_item_check label="使用者界é¢" name="UI"/> + <menu_item_check label="使用者界é¢" name="ToggleUI"/> <menu_item_check label="鏿“‡" name="Selected"/> <menu_item_check label="高亮顯示" name="Highlighted"/> <menu_item_check label="å‹•æ…‹æè³ª" name="Dynamic Textures"/> @@ -183,11 +209,8 @@ <menu_item_check label="使用外å¦è®€å–ç·’" name="Use Plugin Read Thread"/> <menu_item_call label="清除群組快å–資料" name="ClearGroupCache"/> <menu_item_check label="æ»‘é¼ å¹³æ»‘ç§»å‹•" name="Mouse Smoothing"/> + <menu_item_call label="釋出按éµ" name="Release Keys"/> <menu label="快速éµ" name="Shortcuts"> - <menu_item_call label="圖åƒï¼ˆL$[COST])..." name="Upload Image"/> - <menu_item_check label="æœå°‹" name="Search"/> - <menu_item_call label="釋出按éµ" name="Release Keys"/> - <menu_item_call label="以é è¨å€¼è¨å®šä½¿ç”¨è€…界é¢å¤§å°" name="Set UI Size to Default"/> <menu_item_check label="顯示進階é¸å–® ï¼ èˆŠç‰ˆæ·å¾‘" name="Show Advanced Menu - legacy shortcut"/> <menu_item_call label="關閉視窗" name="Close Window"/> <menu_item_call label="關閉全部視窗" name="Close All Windows"/> @@ -196,13 +219,6 @@ <menu_item_check label="æ–æ¡¿ç§»å‹•æ”影機" name="Joystick Flycam"/> <menu_item_call label="é‡è¨è¦–è§’" name="Reset View"/> <menu_item_call label="注視上一ä½èŠå¤©è€…" name="Look at Last Chatter"/> - <menu label="鏿“‡å»ºé€ 工具" name="Select Tool"> - <menu_item_call label="èšç„¦å·¥å…·" name="Focus"/> - <menu_item_call label="移動工具" name="Move"/> - <menu_item_call label="編輯工具" name="Edit"/> - <menu_item_call label="å‰µé€ å·¥å…·" name="Create"/> - <menu_item_call label="土地工具" name="Land"/> - </menu> <menu_item_call label="放大" name="Zoom In"/> <menu_item_call label="縮放æ¢å¾©é è¨" name="Zoom Default"/> <menu_item_call label="縮å°" name="Zoom Out"/> @@ -215,11 +231,10 @@ <menu_item_check label="æè³ªæŽ§åˆ¶å°" name="Texture Console"/> <menu_item_check label="除錯控制å°" name="Debug Console"/> <menu_item_call label="通知控制å°" name="Notifications"/> - <menu_item_check label="æè³ªå°ºå¯¸æŽ§åˆ¶å°" name="Texture Size"/> - <menu_item_check label="æè³ªåˆ†é¡žæŽ§åˆ¶å°" name="Texture Category"/> <menu_item_check label="快速碼錶" name="Fast Timers"/> <menu_item_check label="記憶體" name="Memory"/> <menu_item_check label="å ´æ™¯çµ±è¨ˆè³‡æ–™" name="Scene Statistics"/> + <menu_item_call label="æè³ªæ“·å–除錯控制å°" name="Texture Fetch Debug Console"/> <menu_item_call label="地å€è³‡è¨Šå‚³è‡³é™¤éŒ¯æŽ§åˆ¶å°" name="Region Info to Debug Console"/> <menu_item_call label="群組資訊至除錯控制å°" name="Group Info to Debug Console"/> <menu_item_call label="性能資訊傳至除錯控制å°" name="Capabilities Info to Debug Console"/> @@ -236,6 +251,7 @@ <menu_item_check label="顯示矩陣" name="Show Matrices"/> <menu_item_check label="游標下顯示é¡è‰²" name="Show Color Under Cursor"/> <menu_item_check label="顯示記憶體" name="Show Memory"/> + <menu_item_check label="顯示ç§å€è¨˜æ†¶é«”資訊" name="Show Private Mem Info"/> <menu_item_check label="顯示更新到物件" name="Show Updates"/> </menu> <menu label="強制錯誤" name="Force Errors"> @@ -272,7 +288,16 @@ <menu_item_check label="燈光" name="Lights"/> <menu_item_check label="碰撞骨架" name="Collision Skeleton"/> <menu_item_check label="光線投射" name="Raycast"/> + <menu_item_check label="風力å‘é‡" name="Wind Vectors"/> + <menu_item_check label="繪出複雜度" name="rendercomplexity"/> + <menu_item_check label="附件ä½å…ƒçµ„" name="attachment bytes"/> <menu_item_check label="雕刻" name="Sculpt"/> + <menu label="æè³ªå¯†åº¦" name="Texture Density"> + <menu_item_check label="ç„¡" name="None"/> + <menu_item_check label="ç›®å‰" name="Current"/> + <menu_item_check label="ç†æƒ³" name="Desired"/> + <menu_item_check label="完全" name="Full"/> + </menu> </menu> <menu label="呈åƒ" name="Rendering"> <menu_item_check label="軸" name="Axes"/> @@ -283,7 +308,6 @@ <menu_item_check label="光線和陰影" name="Lighting and Shadows"/> <menu_item_check label="來自日/月/投影物的陰影" name="Shadows from Sun/Moon/Projectors"/> <menu_item_check label="å±å¹•空間環境光é®è”½å’Œé™°å½±å¹³æ»‘技術" name="SSAO and Shadow Smoothing"/> - <menu_item_check label="全域照明(實驗性質)" name="Global Illumination"/> <menu_item_check label="GL 除錯" name="Debug GL"/> <menu_item_check label="管線除錯" name="Debug Pipeline"/> <menu_item_check label="自動åŠé€æ˜Žé®ç½©ï¼ˆéžå»¶ï¼‰" name="Automatic Alpha Masks (deferred)"/> @@ -291,7 +315,6 @@ <menu_item_check label="動作æè³ª" name="Animation Textures"/> <menu_item_check label="關閉æè³ª" name="Disable Textures"/> <menu_item_check label="全解æžåº¦æè³ª" name="Rull Res Textures"/> - <menu_item_check label="查驗æè³ª" name="Audit Textures"/> <menu_item_check label="æè³ªåœ–集(實驗性質)" name="Texture Atlas"/> <menu_item_check label="使附著燈光呈åƒ" name="Render Attached Lights"/> <menu_item_check label="ä½¿é™„è‘—ä¾‹åæ•ˆæžœå‘ˆåƒ" name="Render Attached Particles"/> @@ -314,9 +337,8 @@ <menu_item_call label="開始錄製" name="Start Record"/> <menu_item_call label="åœæ¢éŒ„製" name="Stop Record"/> </menu> - <menu label="世界" name="World"> + <menu label="世界" name="DevelopWorld"> <menu_item_check label="模擬器太陽è¨å®šè¦†è“‹" name="Sim Sun Override"/> - <menu_item_check label="Cheesy 指標" name="Cheesy Beacon"/> <menu_item_check label="固定天氣" name="Fixed Weather"/> <menu_item_call label="傾å°åœ°å€ç‰©ä»¶å¿«å–" name="Dump Region Object Cache"/> </menu> @@ -348,18 +370,17 @@ </menu> <menu label="化身" name="Character"> <menu label="抓å–已產出æè³ª" name="Grab Baked Texture"> - <menu_item_call label="虹膜" name="Iris"/> - <menu_item_call label="é 部" name="Head"/> - <menu_item_call label="上åŠèº«" name="Upper Body"/> - <menu_item_call label="下åŠèº«" name="Lower Body"/> - <menu_item_call label="裙å" name="Skirt"/> + <menu_item_call label="虹膜" name="Grab Iris"/> + <menu_item_call label="é 部" name="Grab Head"/> + <menu_item_call label="上åŠèº«" name="Grab Upper Body"/> + <menu_item_call label="下åŠèº«" name="Grab Lower Body"/> + <menu_item_call label="裙å" name="Grab Skirt"/> </menu> <menu label="å—元測試" name="Character Tests"> <menu_item_call label="å°‡å¤–è§€è½‰æˆ XML" name="Appearance To XML"/> <menu_item_call label="切æ›å—元幾何特性" name="Toggle Character Geometry"/> <menu_item_call label="男性測試" name="Test Male"/> <menu_item_call label="女性測試" name="Test Female"/> - <menu_item_call label="PG 切æ›" name="Toggle PG"/> <menu_item_check label="å…è¨±é¸æ“‡åŒ–身" name="Allow Select Avatar"/> </menu> <menu_item_call label="å¼·åˆ¶åƒæ•¸ç‚ºé è¨å€¼" name="Force Params to Default"/> @@ -380,15 +401,23 @@ <menu_item_check label="HTTP æè³ª" name="HTTP Textures"/> <menu_item_check label="HTTP æ”¶ç´å€" name="HTTP Inventory"/> <menu_item_call label="壓縮圖åƒ" name="Compress Images"/> + <menu_item_call label="啟用記憶體洩æ¼è¦–è¦ºåµæ¸¬å™¨" name="Enable Visual Leak Detector"/> <menu_item_check label="輸出除錯å°åž‹å‚¾å°" name="Output Debug Minidump"/> <menu_item_check label="下次執行時顯示控制å°è¦–窗" name="Console Window"/> + <menu label="è¨å®šè¨˜éŒ„細節" name="Set Logging Level"> + <menu_item_check label="除錯" name="Debug"/> + <menu_item_check label="資訊" name="Info"/> + <menu_item_check label="è¦å‘Š" name="Warning"/> + <menu_item_check label="錯誤" name="Error"/> + <menu_item_check label="ç„¡" name="None"/> + </menu> <menu_item_call label="è¦æ±‚管ç†å“¡ç‹€æ…‹" name="Request Admin Options"/> <menu_item_call label="離開管ç†å“¡ç‹€æ…‹" name="Leave Admin Options"/> <menu_item_check label="顯示管ç†å“¡é¸å–®" name="View Admin Options"/> </menu> <menu label="管ç†å“¡" name="Admin"> - <menu label="Object"> - <menu_item_call label="å–得副本" name="Take Copy"/> + <menu label="物件" name="AdminObject"> + <menu_item_call label="å–得副本" name="Admin Take Copy"/> <menu_item_call label="將所有人強è¨ç‚ºæˆ‘自己" name="Force Owner To Me"/> <menu_item_call label="å¼·è¨ç‚ºã€Œå‡†è¨±æ‰€æœ‰äººã€" name="Force Owner Permissive"/> <menu_item_call label="刪除" name="Delete"/> @@ -424,7 +453,7 @@ <menu_item_call label="身體物ç†" name="Physics"/> <menu_item_call label="全部衣æœ" name="All Clothes"/> </menu> - <menu label="幫助" name="Help"> + <menu label="幫助" name="DeprecatedHelp"> <menu_item_call label="æž—ç™»å®˜æ–¹éƒ¨è½æ ¼" name="Official Linden Blog"/> <menu_item_call label="腳本門戶" name="Scripting Portal"/> <menu label="è‡èŸ²å›žå ±" name="Bug Reporting"> diff --git a/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml b/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml index d9f4acb27b28afac3b5c27d2286f344875bffb10..6184f956d1479d45cb95dd740fdfc566eb26603e 100644 --- a/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml +++ b/indra/newview/skins/default/xui/zh/menu_wearing_gear.xml @@ -2,4 +2,5 @@ <toggleable_menu name="Gear Wearing"> <menu_item_call label="ç·¨è¼¯è£æ‰®" name="edit"/> <menu_item_call label="脫下" name="takeoff"/> + <menu_item_call label="è¤‡è£½è£æ‰®æ¸…單到剪貼簿" name="copy"/> </toggleable_menu> diff --git a/indra/newview/skins/default/xui/zh/notifications.xml b/indra/newview/skins/default/xui/zh/notifications.xml index 97a1bd6c84fc23553701e990d1a90cf5f4c23604..9fecf2c104dc37e9cca55869654e0ae2911a2cba 100644 --- a/indra/newview/skins/default/xui/zh/notifications.xml +++ b/indra/newview/skins/default/xui/zh/notifications.xml @@ -37,6 +37,12 @@ <button name="Help" text="$helptext"/> </form> </template> + <template name="okhelpignore"> + <form> + <button name="OK_okhelpignore" text="$yestext"/> + <button name="Help_okhelpignore" text="$helptext"/> + </form> + </template> <template name="yesnocancelbuttons"> <form> <button name="Yes" text="$yestext"/> @@ -85,6 +91,40 @@ 除å˜è®Šæ›´åˆ°ç›®å‰çš„è¡£æœ/身體部ä½ï¼Ÿ <usetemplate canceltext="å–æ¶ˆ" name="yesnocancelbuttons" notext="ä¸è¦å„²å˜" yestext="儲å˜"/> </notification> + <notification name="ConfirmNoCopyToOutbox"> + ä½ ç„¡æ¬Šå°‡è‡³å°‘ä¸€ä»¶ç‰©é …è¤‡è£½åˆ°ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚é›†çš„ç™¼ä»¶åŒ£ã€‚ ä½ å¯ä»¥ç§»å‹•é€™äº›ç‰©é …ï¼Œæˆ–ä¿ç•™ä¸å‹•。 + <usetemplate name="okcancelbuttons" notext="ä¿ç•™ä¸ç§»å‹•ç‰©é …" yestext="ç§»å‹•ç‰©é …"/> + </notification> + <notification name="OutboxFolderCreated"> + å·²ç¶“ç‚ºä½ æ‰€è½‰ç§»åˆ°å•†å®¶ç™¼ä»¶åŒ£é ‚å±¤çš„æ¯ä¸€å€‹ç‰©é …,å„自建立了一個新資料夾。 + <usetemplate ignoretext="商家發件匣裡æˆåŠŸå»ºç«‹äº†ä¸€å€‹æ–°è³‡æ–™å¤¾" name="okignore" yestext="確定"/> + </notification> + <notification name="OutboxImportComplete"> + æˆåŠŸ + +所有資料夾已æˆåŠŸé€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集。 + <usetemplate ignoretext="所有資料夾已é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集" name="okignore" yestext="確定"/> + </notification> + <notification name="OutboxImportHadErrors"> + 一部分的資料夾無法轉移 + +將一部分資料夾é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集時出錯。 這些資料夾ä»åœ¨ä½ 的商家發件匣。 + +詳情請åƒé–±[[MARKETPLACE_IMPORTS_URL]錯誤記錄]。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="OutboxImportFailed"> + 轉移失敗 + +未將任何資料夾é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集,系統或網路出錯。 è«‹ç¨å€™å†è©¦ä¸€æ¬¡ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="OutboxInitFailed"> + 第二人生購物市集åˆå§‹åŒ–失敗 + +第二人生購物市集åˆå§‹åŒ–失敗,系統或網路出錯。 è«‹ç¨å€™å†è©¦ä¸€æ¬¡ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> <notification name="CompileQueueSaveText"> ä¸Šå‚³è…³æœ¬æ–‡å—æ™‚出å•é¡Œï¼ŒåŽŸå› ï¼š[REASON]。 è«‹ç¨å€™å†è©¦ä¸€æ¬¡ã€‚ </notification> @@ -326,13 +366,19 @@ 進入 [SECOND_LIFE] 需è¦ä¸€å€‹å¸³è™Ÿã€‚ ä½ ç¾åœ¨è¦ä¸è¦æ–°å»ºä¸€å€‹ï¼Ÿ <url name="url"> - http://join.secondlife.com/ + [create_account_url] </url> <usetemplate name="okcancelbuttons" notext="å†è©¦ä¸€æ¬¡" yestext="å‰µé€ æ–°å¸³æˆ¶"/> </notification> <notification name="InvalidCredentialFormat"> ä½ å¿…é ˆåœ¨ã€Œä½¿ç”¨è€…åç¨±ã€æ¬„ä½è£¡è¼¸å…¥ä½¿ç”¨è€…å稱,或輸入化身的å和姓,然後å†ç™»å…¥ã€‚ </notification> + <notification name="InvalidGrid"> + '[GRID]' 䏿˜¯æœ‰æ•ˆçš„ç¶²æ ¼è¾¨è˜å…ƒã€‚ + </notification> + <notification name="InvalidLocationSLURL"> + ä½ çš„é–‹å§‹ä½ç½®æ‰€æŒ‡å®šçš„ç¶²æ ¼ç„¡æ•ˆã€‚ + </notification> <notification name="DeleteClassified"> 刪除個人廣告「[NAME]ã€ï¼Ÿ 已付費用æ•ä¸é€€å›žã€‚ @@ -438,7 +484,7 @@ 儲å˜ç·¨è¯è…³æœ¬æ™‚出å•é¡Œï¼ŒåŽŸå› ï¼š[REASON]。 è«‹ç¨å¾Œå†å˜—試儲å˜è…³æœ¬ã€‚ </notification> <notification name="StartRegionEmpty"> - ç³Ÿç³•ï¼Œä½ çš„èµ·å§‹å€åŸŸå°šæœªå®šç¾©ã€‚ + ä½ çš„èµ·å§‹åœ°å€å°šæœªå®šç¾©ã€‚ 請在「開始ä½ç½®ã€æ¡†è£¡è¼¸å…¥å€åŸŸåï¼Œæˆ–é¸æ“‡ã€Œæˆ‘上一次ä½ç½®ã€æˆ–「我的家ã€ä½œç‚ºé–‹å§‹ä½ç½®ã€‚ <usetemplate name="okbutton" yestext="確定"/> </notification> @@ -461,6 +507,15 @@ </url> <usetemplate ignoretext="æˆ‘çš„é›»è…¦ç¡¬é«”ä¸¦ä¸æ”¯æ´" name="okcancelignore" notext="å¦" yestext="是"/> </notification> + <notification name="IntelOldDriver"> + ä½ çš„é¡¯ç¤ºå¡å¾ˆå¯èƒ½æœ‰æ–°ç‰ˆçš„驅動程å¼ã€‚ æ›´æ–°é¡¯ç¤ºé©…å‹•ç¨‹å¼æœƒå¤§å¹…改善性能。 + + å‰å¾€ [_URL] å¯Ÿçœ‹æ˜¯å¦æœ‰æ–°ç‰ˆé©…動程å¼ï¼Ÿ + <url name="url"> + http://www.intel.com/p/en_US/support/detect/graphics + </url> + <usetemplate ignoretext="我的顯示驅動程å¼å¤ªè€èˆŠ" name="okcancelignore" notext="å¦" yestext="是"/> + </notification> <notification name="UnknownGPU"> ä½ çš„ç³»çµ±å«æœ‰ä¸€å€‹ [APP_NAME] 無法辨èªçš„顯åƒå¡ã€‚ åŽŸå› å¾ˆå¯èƒ½æ˜¯ [APP_NAME] 尚未é‡å°æ–°ç¡¬é«”å®Œæˆæ¸¬è©¦ã€‚ é€™å¤§æ¦‚ä¸æœƒå‡ºå•é¡Œï¼Œä½†ä½ å¯èƒ½éœ€è¦èª¿æ•´é¡¯åƒè¨å®šã€‚ @@ -557,6 +612,9 @@ 請確定沒有物件被鎖ä½ï¼Œä¸¦ç¢ºå®šä½ æ“æœ‰æ‰€æœ‰ç‰©ä»¶ã€‚ </notification> + <notification name="CannotLinkPermanent"> + 物件無法跨越地å€ç•Œé™é€²è¡Œè¯çµã€‚ + </notification> <notification name="CannotLinkDifferentOwners"> 無法è¯çµï¼›æœ‰äº›ç‰©ä»¶çš„æ‰€æœ‰äººä¸åŒã€‚ @@ -636,7 +694,7 @@ 無法建立輸出檔:[FILE] </notification> <notification name="DoNotSupportBulkAnimationUpload"> - [APP_NAME] ç›®å‰å°šä¸æ”¯æ´å‹•作檔批é‡ä¸Šå‚³ã€‚ + [APP_NAME] ç›®å‰å°šä¸æ”¯æ´ BVH æ ¼å¼çš„動作檔批é‡ä¸Šå‚³ã€‚ </notification> <notification name="CannotUploadReason"> 無法上傳 [FILE]ï¼ŒåŽŸå› ï¼š[REASON] @@ -935,6 +993,40 @@ <button name="Cancel" text="å–æ¶ˆ"/> </form> </notification> + <notification label="新增自動å–代清單" name="AddAutoReplaceList"> + 新清單å稱: + <form name="form"> + <button name="SetName" text="確定"/> + </form> + </notification> + <notification label="更改自動å–代清單的å稱" name="RenameAutoReplaceList"> + 「[DUPNAME]ã€å稱已有人使用。 + 輸入å¦ä¸€å€‹ç¨ç‰¹çš„å稱: + <form name="form"> + <button name="ReplaceList" text="å–代目å‰çš„æ¸…å–®"/> + <button name="SetName" text="使用新å稱"/> + </form> + </notification> + <notification name="InvalidAutoReplaceEntry"> + é—œéµå—å¿…é ˆæ˜¯ä¸€å€‹å—,å–代å—ä¸å¾—空白。 + </notification> + <notification name="InvalidAutoReplaceList"> + 該å–代清單無效。 + </notification> + <notification name="SpellingDictImportRequired"> + ä½ å¿…é ˆæŒ‡å®šä¸€å€‹æª”æ¡ˆã€ä¸€å€‹å稱和一種語言。 + </notification> + <notification name="SpellingDictIsSecondary"> + [DIC_NAME] å—典似乎沒有 "aff" 檔案,這表示該å—典是「次級ã€å—典。 +它å¯ç”¨åšé™„åŠ å—典,但ä¸å¾—作為主è¦å—典。 + +åƒè¦‹ https://wiki.secondlife.com/wiki/Adding_Spelling_Dictionaries + </notification> + <notification name="SpellingDictImportFailed"> + 無法複製 [FROM_NAME] + + 到 [TO_NAME] + </notification> <notification label="儲å˜è£æ‰®" name="SaveOutfitAs"> å„²å˜æˆ‘æ£åœ¨ç©¿çš„ç‚ºæ–°è£æ‰®ï¼š <form name="form"> @@ -966,7 +1058,7 @@ </form> </notification> <notification name="RemoveFromFriends"> - ä½ ç¢ºå®šè¦å¾žæœ‹å‹åå–®ä¸ç§»é™¤ [NAME] 嗎? + 確定è¦å¾žæœ‹å‹åå–®ä¸ç§»é™¤ <nolink>[NAME]</nolink>? <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> </notification> <notification name="RemoveMultipleFromFriends"> @@ -1091,8 +1183,13 @@ <notification name="DisplaySetToSafe"> å› ç‚ºä½ æŒ‡å®šäº† -safe é¸é …,已將顯示è¨ç‚ºå®‰å…¨ç‰ç´šã€‚ </notification> - <notification name="DisplaySetToRecommended"> - æ ¹æ“šä½ çš„ç³»çµ±è¨ç½®ï¼Œå·²å°‡é¡¯ç¤ºè¨å®šç‚ºæŽ¨è–¦æŽ¡ç”¨çš„ç‰ç´šã€‚ + <notification name="DisplaySetToRecommendedGPUChange"> + ç”±æ–¼ä½ çš„é¡¯åƒå¡æœ‰æ‰€è®Šæ›´ï¼Œå·²å°‡é¡¯ç¤ºç‹€æ…‹è¨ç‚ºæŽ¨è–¦æŽ¡ç”¨çš„ç‰ç´šã€‚ +原顯åƒå¡ï¼š[LAST_GPU] +新顯åƒå¡ï¼š[THIS_GPU] + </notification> + <notification name="DisplaySetToRecommendedFeatureChange"> + 由於呈åƒå系統有所變更,已將顯示狀態è¨ç‚ºæŽ¨è–¦çš„è¨å®šã€‚ </notification> <notification name="ErrorMessage"> [ERROR_MESSAGE] @@ -1103,7 +1200,7 @@ ä½ å·²è¢«ç§»å¾€ä¸€å€‹é„°è¿‘åœ°å€ã€‚ </notification> <notification name="AvatarMovedLast"> - ä½ çš„ä¸Šä¸€å€‹åœ°é»žç›®å‰ç„¡æ³•å‰å¾€ã€‚ + ä½ æ‰€è«‹æ±‚çš„åœ°é»žç›®å‰ç„¡æ³•å‰å¾€ã€‚ ä½ å·²è¢«ç§»å¾€ä¸€å€‹é„°è¿‘åœ°å€ã€‚ </notification> <notification name="AvatarMovedHome"> @@ -1122,8 +1219,7 @@ [APP_NAME] 安è£å®Œæˆã€‚ å¦‚æžœä½ æ˜¯ç¬¬ä¸€æ¬¡ä½¿ç”¨ [SECOND_LIFE]ï¼Œä½ å°‡éœ€è¦å»ºç«‹æ–°å¸³è™Ÿæ‰å¯ç™»å…¥ã€‚ -返回 [http://join.secondlife.com secondlife.com] 建立新帳號? - <usetemplate name="okcancelbuttons" notext="繼續" yestext="新帳戶..."/> + <usetemplate name="okcancelbuttons" notext="繼續" yestext="建立帳號…"/> </notification> <notification name="LoginPacketNeverReceived"> 連線出ç¾å•題。 å•題å¯èƒ½å‡ºåœ¨ä½ 的網路連線或 [SECOND_LIFE_GRID]。 @@ -1409,7 +1505,7 @@ SHA1 指紋:[MD5_DIGEST] <usetemplate ignoretext="在我退回物件給它們的所有人å‰ç¢ºèª" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> </notification> <notification name="GroupLeaveConfirmMember"> - ä½ ç›®å‰æ˜¯ [GROUP] 群組的æˆå“¡ã€‚ + ä½ ç›®å‰æ˜¯ <nolink>[GROUP]</nolink> 群組的æˆå“¡ã€‚ 是å¦è¦é›¢é–‹ç¾¤çµ„? <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> </notification> @@ -1544,6 +1640,11 @@ SHA1 指紋:[MD5_DIGEST] <button name="Cancel" text="å–æ¶ˆ"/> </form> </notification> + <notification name="TooManyTeleportOffers"> + ä½ è©¦åœ–é€å‡º [OFFERS] 個瞬間傳é€é‚€è«‹ã€‚ +è¶…éŽäº† [LIMIT] 個的上é™ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> <notification name="OfferTeleportFromGod"> 用神的權力把居民å¬åˆ°ä½ çš„ä½ç½®ï¼Ÿ <form name="form"> @@ -1633,83 +1734,128 @@ SHA1 指紋:[MD5_DIGEST] <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> </notification> <notification name="RegionEntryAccessBlocked"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ä¸èƒ½åˆ°è©²åœ°å€ã€‚ åŽŸå› å¯èƒ½æ˜¯æˆ‘å€‘æ²’æœ‰è¶³å¤ è³‡è¨Šé©—è‰ä½ 的年齡。 - -è«‹ç¢ºå®šä½ å·²å®‰è£æœ€æ–°ç‰ˆçš„ Viewer,並å‰å¾€çŸ¥è˜åº«çžè§£å¦‚何出入這種內容分級的å€åŸŸã€‚ + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰è¶…éŽä½ ç›®å‰å好的分級的內容。 ä½ å¯ä»¥åˆ°ã€Œæˆ‘自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ çš„å好è¨å®šã€‚ <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="RegionEntryAccessBlocked_KB"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ä¸èƒ½åˆ°è©²åœ°å€ã€‚ - -å‰å¾€çŸ¥è˜åº«é€²ä¸€æ¥çžè§£å…§å®¹åˆ†ç´šï¼Ÿ + <notification name="RegionEntryAccessBlocked_AdultsOnlyContent"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰ [REGIONMATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œåƒ…é™æˆäººã€‚ <url name="url"> http://wiki.secondlife.com/wiki/Linden_Lab_Official:Maturity_ratings:_an_overview </url> - <usetemplate ignoretext="礙於內容分級的é™åˆ¶ï¼Œæˆ‘無法進入這個地å€" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> + <usetemplate ignoretext="跨越地å€ï¼šä½ 所欲å‰å¾€çš„地å€å«æœ‰é™åˆ¶çµ¦æˆäººçš„內容。" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> </notification> <notification name="RegionEntryAccessBlocked_Notify"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ä¸èƒ½åˆ°è©²åœ°å€ã€‚ + ä½ æ‰€æ¬²å‰å¾€çš„地å€åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 + </notification> + <notification name="RegionEntryAccessBlocked_NotifyAdultsOnly"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰ [REGIONMATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œåƒ…é™æˆäººã€‚ </notification> <notification name="RegionEntryAccessBlocked_Change"> - æ ¹æ“šä½ æ‰€è¨å®šçš„å…§å®¹åˆ†ç´šï¼Œä½ ä¸èƒ½åˆ°è©²åœ°å€ã€‚ - -è¦é€²å…¥è©²åœ°å€ï¼Œè«‹æ›´æ”¹ä½ 的內容分級å好。 這將å…è¨±ä½ æœå°‹ä¸¦æŽ¥è§¸ [REGIONMATURITY] 內容。 è¦é‚„原任何變更,請到「我自己 > å好è¨å®š > 一般è¨å®šã€ã€‚ + ä½ æ‰€æ¬²å‰å¾€çš„地å€åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 ä½ å¯ä»¥è®Šæ›´ä½ çš„å好è¨å®šï¼Œæˆ–å–æ¶ˆå‰å¾€ã€‚ ä½ çš„å好è¨å®šè®Šæ›´å¾Œï¼Œä½ å¯ä»¥è©¦åœ–å†é€²å…¥è©²åœ°å€ã€‚ + <form name="form"> + <button name="OK" text="變更å好è¨å®š"/> + <button name="Cancel" text="å–æ¶ˆ"/> + <ignore name="ignore" text="跨越地å€ï¼šä½ 所欲å‰å¾€çš„地å€å«æœ‰è¢«ä½ ç›®å‰çš„å好è¨å®šæŽ’除的分級內容。"/> + </form> + </notification> + <notification name="RegionEntryAccessBlocked_PreferencesOutOfSync"> + 發生技術å•é¡Œï¼Œä½ çš„å好è¨å®šå’Œä¼ºæœå™¨ä¸Šçš„ä¸ä¸€è‡´ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="TeleportEntryAccessBlocked"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰è¶…éŽä½ ç›®å‰å好的分級的內容。 ä½ å¯ä»¥åˆ°ã€Œæˆ‘自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ çš„å好è¨å®šã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="TeleportEntryAccessBlocked_AdultsOnlyContent"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰ [REGIONMATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œåƒ…é™æˆäººã€‚ + <url name="url"> + http://wiki.secondlife.com/wiki/Linden_Lab_Official:Maturity_ratings:_an_overview + </url> + <usetemplate ignoretext="瞬間傳é€ï¼šä½ 所欲å‰å¾€çš„地å€å«æœ‰é™åˆ¶çµ¦æˆäººçš„內容。" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> + </notification> + <notification name="TeleportEntryAccessBlocked_Notify"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 + </notification> + <notification name="TeleportEntryAccessBlocked_NotifyAdultsOnly"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰ [REGIONMATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œåƒ…é™æˆäººã€‚ + </notification> + <notification name="TeleportEntryAccessBlocked_ChangeAndReTeleport"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 我們å¯ä»¥è®Šæ›´ä½ çš„å好è¨å®šå¥½è®“ä½ ç¹¼çºŒçž¬é–“å‚³é€ï¼Œä½ 也å¯å–消這動作。 + <form name="form"> + <button name="OK" text="變更後繼續"/> + <button name="Cancel" text="å–æ¶ˆ"/> + <ignore name="ignore" text="瞬間傳é€ï¼ˆå¯é‡å•Ÿï¼‰ï¼šä½ 所欲å‰å¾€çš„地å€å«æœ‰è¢«ä½ ç›®å‰çš„å好è¨å®šæŽ’除的分級內容。"/> + </form> + </notification> + <notification name="TeleportEntryAccessBlocked_Change"> + ä½ æ‰€æ¬²å‰å¾€çš„地å€åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 我們å¯ä»¥è®Šæ›´ä½ çš„å好è¨å®šï¼Œä½ 也å¯å–消瞬間傳é€ã€‚ ä½ çš„å好è¨å®šè®Šæ›´å¾Œï¼Œä½ å¯ä»¥å†å˜—試瞬間傳é€ã€‚ <form name="form"> <button name="OK" text="變更å好è¨å®š"/> - <button name="Cancel" text="關閉"/> - <ignore name="ignore" text="æˆ‘æ‰€é¸æ“‡çš„內容分級ä¸å…許我進入一個地å€"/> + <button name="Cancel" text="å–æ¶ˆ"/> + <ignore name="ignore" text="瞬間傳é€ï¼ˆä¸å¯é‡å•Ÿï¼‰ï¼šä½ 所欲å‰å¾€çš„地å€å«æœ‰è¢«ä½ ç›®å‰çš„å好è¨å®šæŽ’除的分級內容。"/> </form> </notification> + <notification name="TeleportEntryAccessBlocked_PreferencesOutOfSync"> + 發生技術å•é¡Œï¼Œä½ çš„å好è¨å®šå’Œä¼ºæœå™¨ä¸Šçš„ä¸ä¸€è‡´ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> <notification name="PreferredMaturityChanged"> - ä½ çš„å…§å®¹åˆ†ç´šå好ç¾åœ¨è¨ç‚º [RATING]。 + ä½ å°‡ä¸å†æ”¶åˆ°é€šçŸ¥ï¼Œå‘ŠçŸ¥ä½ å³å°‡é€²å…¥ä¸€å€‹ [RATING] 內容分級的地å€ã€‚ ä½ å¯ä»¥åˆ°é¸å–®åˆ—底下的「我自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ 的內容å好。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="MaturityChangeError"> + æˆ‘å€‘æ¤æ™‚ç„¡æ³•è®Šæ›´ä½ çš„å好è¨å®šï¼Œè®“ä½ è§€çœ‹ [PREFERRED_MATURITY] 分級的內容。 ä½ çš„å好è¨å®šå·²ç¶“é‡è¨ï¼Œå¯è§€çœ‹ [ACTUAL_MATURITY] 的分級內容。 ä½ å¯ä»¥åˆ°é¸å–®åˆ—的「我自己 > å好è¨å®š > 一般è¨å®šã€å†æ¬¡è®Šæ›´ä½ çš„å好。 + <usetemplate name="okbutton" yestext="確定"/> </notification> <notification name="LandClaimAccessBlocked"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•æ”¶å–這塊土地。 åŽŸå› å¯èƒ½æ˜¯æˆ‘å€‘æ²’æœ‰è¶³å¤ è³‡è¨Šé©—è‰ä½ 的年齡。 - -è«‹ç¢ºå®šä½ å·²å®‰è£æœ€æ–°ç‰ˆçš„ Viewer,並å‰å¾€çŸ¥è˜åº«çžè§£å¦‚何出入這種內容分級的å€åŸŸã€‚ + ä½ æ‰€æ¬²æ”¶å–çš„åœŸåœ°å«æœ‰è¶…éŽä½ ç›®å‰å好的分級內容。 ä½ å¯ä»¥åˆ°ã€Œæˆ‘自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ çš„å好è¨å®šã€‚ <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="LandClaimAccessBlocked_KB"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•æ”¶å–這塊土地。 - -å‰å¾€çŸ¥è˜åº«é€²ä¸€æ¥çžè§£å…§å®¹åˆ†ç´šï¼Ÿ + <notification name="LandClaimAccessBlocked_AdultsOnlyContent"> + åªæœ‰æˆäººæ‰èƒ½æ”¶å–這土地。 <url name="url"> http://wiki.secondlife.com/wiki/Linden_Lab_Official:Maturity_ratings:_an_overview </url> - <usetemplate ignoretext="礙於內容分級的é™åˆ¶ï¼Œæˆ‘無法收å–這塊土地" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> + <usetemplate ignoretext="åªæœ‰æˆäººæ‰èƒ½æ”¶å–這土地。" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> </notification> <notification name="LandClaimAccessBlocked_Notify"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•æ”¶å–這塊土地。 + ä½ æ‰€æ¬²æ”¶å–çš„åœŸåœ°åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 + </notification> + <notification name="LandClaimAccessBlocked_NotifyAdultsOnly"> + ä½ æ‰€æ¬²æ”¶å–çš„åœŸåœ°åŒ…å« [REGIONMATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œåƒ…é™æˆäººã€‚ </notification> <notification name="LandClaimAccessBlocked_Change"> - ç¤™æ–¼ä½ æ‰€è¨çš„內容分級åå¥½ï¼Œä½ ç„¡æ³•æ”¶å–這塊土地。 - -ä½ å¯ä»¥ç¾åœ¨é»žæŒ‰ã€Œæ›´æ”¹å好è¨å®šã€æå‡å…§å®¹åˆ†ç´šï¼Œå³å¯é€²å…¥ã€‚ 從ç¾åœ¨èµ·ï¼Œä½ å¯ä»¥æœå°‹ä¸¦æŽ¥è§¸ [REGIONMATURITY] 內容。 å¾€å¾Œå¦‚æžœä½ è¦é‚„åŽŸé€™é …è®Šæ›´ï¼Œè«‹åˆ°ã€Œæˆ‘è‡ªå·± > å好è¨å®š > 一般è¨å®šã€ã€‚ - <usetemplate ignoretext="æˆ‘æ‰€é¸æ“‡çš„內容分級ä¸å…許我收å–土地" name="okcancelignore" notext="關閉" yestext="變更å好è¨å®š"/> + ä½ æ‰€æ¬²æ”¶å–çš„åœŸåœ°åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 我們å¯ä»¥è®Šæ›´ä½ çš„åå¥½ï¼Œè®“ä½ å†è©¦åœ–æ”¶å–土地。 + <form name="form"> + <button name="OK" text="變更å好è¨å®š"/> + <button name="Cancel" text="å–æ¶ˆ"/> + <ignore name="ignore" text="ä½ æ‰€æ¬²æ”¶å–çš„åœŸåœ°å«æœ‰è¢«ä½ ç›®å‰çš„å好所排除的分級內容。"/> + </form> </notification> <notification name="LandBuyAccessBlocked"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•è³¼è²·é€™å¡ŠåœŸåœ°ã€‚ åŽŸå› å¯èƒ½æ˜¯æˆ‘å€‘æ²’æœ‰è¶³å¤ è³‡è¨Šé©—è‰ä½ 的年齡。 - -è«‹ç¢ºå®šä½ å·²å®‰è£æœ€æ–°ç‰ˆçš„ Viewer,並å‰å¾€çŸ¥è˜åº«çžè§£å¦‚何出入這種內容分級的å€åŸŸã€‚ + ä½ æ‰€æ¬²è³¼è²·çš„åœŸåœ°çš„å…§å®¹åˆ†ç´šè¶…éŽä½ ç›®å‰æ‰€è¨å好。 ä½ å¯ä»¥åˆ°ã€Œæˆ‘自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ çš„å好è¨å®šã€‚ <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="LandBuyAccessBlocked_KB"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•è³¼è²·é€™å¡ŠåœŸåœ°ã€‚ - -å‰å¾€çŸ¥è˜åº«é€²ä¸€æ¥çžè§£å…§å®¹åˆ†ç´šï¼Ÿ + <notification name="LandBuyAccessBlocked_AdultsOnlyContent"> + åªæœ‰æˆäººæ‰èƒ½è³¼è²·é€™åœŸåœ°ã€‚ <url name="url"> http://wiki.secondlife.com/wiki/Linden_Lab_Official:Maturity_ratings:_an_overview </url> - <usetemplate ignoretext="礙於內容分級的é™åˆ¶ï¼Œæˆ‘無法購買這塊土地" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> + <usetemplate ignoretext="åªæœ‰æˆäººæ‰èƒ½è³¼è²·é€™åœŸåœ°ã€‚" name="okcancelignore" notext="關閉" yestext="å‰å¾€çŸ¥è˜åº«"/> </notification> <notification name="LandBuyAccessBlocked_Notify"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•è³¼è²·é€™å¡ŠåœŸåœ°ã€‚ + ä½ æ‰€æ¬²è³¼è²·çš„åœŸåœ°åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 + </notification> + <notification name="LandBuyAccessBlocked_NotifyAdultsOnly"> + ä½ æ‰€æ¬²è³¼è²·çš„åœŸåœ°å«æœ‰ [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œåƒ…é™æˆäººã€‚ </notification> <notification name="LandBuyAccessBlocked_Change"> - ç¤™æ–¼ä½ æ‰€è¨çš„å…§å®¹åˆ†ç´šï¼Œä½ ç„¡æ³•è³¼è²·é€™å¡ŠåœŸåœ°ã€‚ - -ä½ å¯ä»¥ç¾åœ¨é»žæŒ‰ã€Œæ›´æ”¹å好è¨å®šã€æå‡å…§å®¹åˆ†ç´šï¼Œå³å¯é€²å…¥ã€‚ 從ç¾åœ¨èµ·ï¼Œä½ å¯ä»¥æœå°‹ä¸¦æŽ¥è§¸ [REGIONMATURITY] 內容。 å¾€å¾Œå¦‚æžœä½ è¦é‚„åŽŸé€™é …è®Šæ›´ï¼Œè«‹åˆ°ã€Œæˆ‘è‡ªå·± > å好è¨å®š > 一般è¨å®šã€ã€‚ - <usetemplate ignoretext="æˆ‘æ‰€é¸æ“‡çš„內容分級ä¸å…許我購買土地" name="okcancelignore" notext="關閉" yestext="變更å好è¨å®š"/> + ä½ æ‰€æ¬²è³¼è²·çš„åœŸåœ°åŒ…å« [REGIONMATURITY] åˆ†ç´šçš„å…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGIONMATURITY] 分級的內容。 我們å¯ä»¥è®Šæ›´ä½ çš„åå¥½ï¼Œè®“ä½ å†è©¦åœ–購買土地。 + <form name="form"> + <button name="OK" text="變更å好è¨å®š"/> + <button name="Cancel" text="å–æ¶ˆ"/> + <ignore name="ignore" text="ä½ æ‰€æ¬²è³¼è²·çš„åœŸåœ°å«æœ‰è¢«ä½ ç›®å‰çš„å好所排除的分級內容。"/> + </form> </notification> <notification name="TooManyPrimsSelected"> 鏿“‡äº†å¤ªå¤šé …的幾何元件。 è«‹è‡³å¤šé¸æ“‡ [MAX_PRIM_COUNT] é …å¹¾ä½•å…ƒä»¶ï¼Œå†è©¦ä¸€æ¬¡ã€‚ @@ -1763,10 +1909,9 @@ SHA1 指紋:[MD5_DIGEST] </form> </notification> <notification label="已變更地å€çš„內容分級" name="RegionMaturityChange"> - 地å€çš„內容分級已經更新。 + æ¤åœ°å€çš„內容分級已經變更。 å¯èƒ½éœ€è¦ç¨å€™ä¸€æ®µæ™‚é–“ï¼Œåœ°åœ–æ‰æœƒåæ˜ é€™å€‹è®Šæ›´ã€‚ - -å±…æ°‘è‹¥è¦é€²å…¥å®Œå…¨æˆäººåˆ†ç´šçš„地å€ï¼Œå¸³è™Ÿå¿…é ˆå…ˆé€šéŽå¹´é½¡é©—è‰æˆ–付款驗è‰ã€‚ + <usetemplate name="okbutton" yestext="確定"/> </notification> <notification label="è²éŸ³ç‰ˆæœ¬ä¸ç›¸ç¬¦" name="VoiceVersionMismatch"> 這版本的 [APP_NAME] 和本地å€çš„語音èŠå¤©åŠŸèƒ½ä¸ç›¸å®¹ã€‚ 想è¦èªžéŸ³èŠå¤©æ£å¸¸é‹ä½œï¼Œä½ å¿…é ˆæ›´æ–° [APP_NAME]。 @@ -1883,6 +2028,18 @@ SHA1 指紋:[MD5_DIGEST] ä½ ç¢ºå®šä½ è¦çµæŸé€€å‡ºï¼Ÿ <usetemplate ignoretext="ç•¶æˆ‘çµæŸé€€å‡ºæ™‚進行確èª" name="okcancelignore" notext="ä¸è¦çµæŸé€€å‡º" yestext="çµæŸé€€å‡º"/> </notification> + <notification name="ConfirmRestoreToybox"> + 這動作將會æ¢å¾©ä½ é è¨çš„æŒ‰éˆ•和工具列。 + +ä½ ä¸èƒ½å–消這動作。 + <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="ConfirmClearAllToybox"> + é€™å‹•ä½œå°‡æŠŠæ‰€æœ‰æŒ‰éˆ•æ”¶å…¥å·¥å…·ç®±ï¼Œä½ çš„å·¥å…·åˆ—å°‡æœƒæ¸…ç©ºã€‚ + +ä½ ä¸èƒ½å–消這動作。 + <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> + </notification> <notification name="DeleteItems"> [QUESTION] <usetemplate ignoretext="刪除物å“å‰ç¢ºèª" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> @@ -1963,6 +2120,10 @@ SHA1 指紋:[MD5_DIGEST] ä½ ç¢ºå®šè¦åˆªé™¤ä½ 的旅行ã€ç¶²é åŠæœå°‹æ·å²ç´€éŒ„嗎? <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> </notification> + <notification name="ConfirmClearCache"> + ç¢ºå®šè¦æ¸…é™¤ä½ Viewer 的快å–? + <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> + </notification> <notification name="ConfirmClearCookies"> ä½ ç¢ºå®šè¦æ¸…é™¤ä½ çš„ cookies 嗎? <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="是"/> @@ -1988,48 +2149,30 @@ SHA1 指紋:[MD5_DIGEST] ä½ è¦è¦†å¯«å·²å„²å˜çš„é è¨é…置嗎? <usetemplate name="okcancelbuttons" notext="å¦" yestext="是"/> </notification> - <notification name="WLDeletePresetAlert"> - ä½ è¦åˆªé™¤ [SKY]? - <usetemplate name="okcancelbuttons" notext="å¦" yestext="是"/> - </notification> <notification name="WLNoEditDefault"> ä½ ä¸èƒ½ç·¨è¼¯æˆ–刪除é è¨çš„è¨å®šã€‚ </notification> <notification name="WLMissingSky"> é€™å€‹ã€Œä¸€æ—¥å¾ªç’°ã€æª”案åƒè€ƒäº†ä¸€å€‹ä¸å˜åœ¨çš„天空檔案:[SKY]。 </notification> - <notification name="PPSaveEffectAlert"> - PostProcess 效果已經å˜åœ¨ã€‚ ä½ æ˜¯å¦ä»è¦æŠŠå®ƒè¦†å¯«æŽ‰ï¼Ÿ - <usetemplate name="okcancelbuttons" notext="å¦" yestext="是"/> + <notification name="WLRegionApplyFail"> + 抱æ‰ï¼Œè¨å®šç„¡æ³•套用到地å€ã€‚ 離開地å€å†è¿”回也許å¯ä»¥è§£æ±ºé€™å€‹å•題。 æ‰€å¾—çš„åŽŸå› ç‚ºï¼š[FAIL_REASON] </notification> - <notification name="NewSkyPreset"> - 請為新的天空定一個å稱 - <form name="form"> - <input name="message"> - æ–°é è¨é…é…ç½® - </input> - <button name="OK" text="確定"/> - <button name="Cancel" text="å–æ¶ˆ"/> - </form> - </notification> - <notification name="ExistsSkyPresetAlert"> - é è¨å€¼å·²ç¶“å˜åœ¨ï¼ + <notification name="EnvCannotDeleteLastDayCycleKey"> + ç„¡æ³•åˆªé™¤æ¤æ—¥å¾ªç’°çš„æœ€å¾Œä¸€çµ„è¨å®šï¼Œæ—¥å¾ªç’°ä¸å¾—為空白。 ä½ æ‡‰è©²ä¿®æ”¹æœ€å¾Œä¸€çµ„è³‡æ–™ï¼Œä¸è¦è©¦åœ–刪除,然後å†å»ºç«‹æ–°çš„。 + <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="NewWaterPreset"> - 請為水的新é è¨å€¼å®šä¸€å€‹å稱 - <form name="form"> - <input name="message"> - æ–°é è¨é…é…ç½® - </input> - <button name="OK" text="確定"/> - <button name="Cancel" text="å–æ¶ˆ"/> - </form> + <notification name="DayCycleTooManyKeyframes"> + ä½ ç„¡æ³•æ–°å¢žæ›´å¤šçš„ keyframe 到這個日循環。 [SCOPE] 範åœçš„æ—¥å¾ªç’°æœ€å¤šå…許 [MAX] 個 keyframe。 + <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="ExistsWaterPresetAlert"> - é è¨å€¼å·²ç¶“å˜åœ¨ï¼ + <notification name="EnvUpdateRate"> + ä½ è‡³å¤šåªèƒ½æ¯ [WAIT] 秒更新一次地å€çš„環境è¨å®šã€‚ è«‹ç‰åˆ°é€™æ®µæ™‚é–“éŽåŽ»äº†å†è©¦ä¸€æ¬¡ã€‚ + <usetemplate name="okbutton" yestext="確定"/> </notification> - <notification name="WaterNoEditDefault"> - ä½ ä¸èƒ½ç·¨è¼¯æˆ–刪除é è¨çš„è¨å®šã€‚ + <notification name="PPSaveEffectAlert"> + PostProcess 效果已經å˜åœ¨ã€‚ ä½ æ˜¯å¦ä»è¦æŠŠå®ƒè¦†å¯«æŽ‰ï¼Ÿ + <usetemplate name="okcancelbuttons" notext="å¦" yestext="是"/> </notification> <notification name="ChatterBoxSessionStartError"> 無法開始一個與 [RECIPIENT] 他的新èŠå¤©æœƒè©±ã€‚ @@ -2058,13 +2201,11 @@ SHA1 指紋:[MD5_DIGEST] <usetemplate ignoretext="ç·¨è¼¯å¤–è§€æ™‚èƒ½ç©¿ä¸Šæˆ‘æ‰€å‰µé€ çš„æœè£" name="okcancelignore" notext="å¦" yestext="是"/> </notification> <notification name="NotAgeVerified"> - ä½ å¿…é ˆé€šéŽå¹´é½¡é©—è‰æ‰èƒ½é€²å…¥é€™å€åŸŸã€‚ ä½ æ˜¯å¦è¦å‰å¾€ [SECOND_LIFE] 網站進行年齡驗è‰ï¼Ÿ - -[_URL] - <url name="url"> - https://secondlife.com/account/verification.php - </url> - <usetemplate ignoretext="我尚未驗è‰å¹´é½¡" name="okcancelignore" notext="å¦" yestext="是"/> + ä½ æ‰€æ¬²å‰å¾€çš„地點è¨é™çµ¦å¹´æ»¿ 18 æ²çš„居民進入。 + <usetemplate ignoretext="æˆ‘å¹´é½¡ä¸æ»¿è¦å®šï¼Œç„¡æ³•進入有年齡é™åˆ¶çš„å€åŸŸã€‚" name="okignore" yestext="確定"/> + </notification> + <notification name="NotAgeVerified_Notify"> + æ¤åœ°é»žé™åˆ¶ç‚ºå¹´æ»¿ 18 æ²ã€‚ </notification> <notification name="Cannot enter parcel: no payment info on file"> ä½ å¿…é ˆæä¾›ä»˜æ¬¾è³‡æ–™æ‰èƒ½é€²å…¥é€™å€åŸŸã€‚ ä½ æ˜¯å¦è¦å‰å¾€ [SECOND_LIFE] 網站è¨å®šä»˜æ¬¾è³‡æ–™ï¼Ÿ @@ -2100,10 +2241,10 @@ SHA1 指紋:[MD5_DIGEST] 主旨:[SUBJECT],訊æ¯ï¼š[MESSAGE] </notification> <notification name="FriendOnline"> - [NAME] 上線 + <nolink>[NAME]</nolink> ç›®å‰åœ¨ç·šä¸Š </notification> <notification name="FriendOffline"> - [NAME] 離線 + <nolink>[NAME]</nolink> ç›®å‰é›¢ç·š </notification> <notification name="AddSelfFriend"> é›–ç„¶ä½ äººå¾ˆå¥½ï¼Œä½ é‚„æ˜¯ä¸èƒ½æŠŠè‡ªå·±åŠ ç‚ºæœ‹å‹ã€‚ @@ -2125,7 +2266,7 @@ SHA1 指紋:[MD5_DIGEST] 地形 .raw 檔已下載 </notification> <notification name="GestureMissing"> - 呃… å§¿å‹¢ [NAME] 在資料庫ä¸éºå¤±ã€‚ + å§¿å‹¢ [NAME] 在資料庫ä¸éºå¤±ã€‚ </notification> <notification name="UnableToLoadGesture"> 無法載入姿勢 [NAME]。 @@ -2217,14 +2358,15 @@ SHA1 指紋:[MD5_DIGEST] ä½ çš„å片已被拒絕。 </notification> <notification name="TeleportToLandmark"> - 若想瞬間傳é€åˆ°ã€Œ[NAME]ã€ç‰ç‰åœ°é»žï¼Œä½ å¯ä»¥é–‹å•Ÿèž¢å¹•峿–¹çš„「地點ã€å´é‚Šæ¬„,å†é¸æ“‡ã€Œåœ°æ¨™ã€é 籤。 -用點按方å¼é¸æ“‡ä»»ä½•åœ°æ¨™ï¼Œå†æŒ‰å´é‚Šæ¬„底下的「瞬間傳é€ã€ã€‚ -ï¼ˆä½ é‚„å¯ä»¥ç›´æŽ¥æŒ‰å…©ä¸‹é‚£å€‹åœ°æ¨™ï¼Œæˆ–æŒ‰æ»‘é¼ å³éµï¼Œé¸æ“‡ã€Œçž¬é–“傳é€ã€ã€‚) + è¦çž¬é–“傳é€åˆ°ã€Œ[NAME]ã€ç‰åœ°é»žï¼Œè«‹é»žæŒ‰ã€Œåœ°é»žã€æŒ‰éˆ•, + ç„¶å¾Œåœ¨é–‹å•Ÿçš„è¦–çª—è£¡ï¼Œé¸æ“‡ã€Œåœ°æ¨™ã€é 籤。 點按任何 + åœ°æ¨™åŠ ä»¥é¸æ“‡ï¼Œå†é»žæŒ‰è¦–窗底下的「瞬間傳é€ã€æŒ‰éˆ•。 + ï¼ˆä½ é‚„å¯ä»¥ç›´æŽ¥æŒ‰å…©ä¸‹é‚£å€‹åœ°æ¨™ï¼Œæˆ–æŒ‰æ»‘é¼ å³éµï¼Œé¸æ“‡ã€Œçž¬é–“傳é€ã€ã€‚) </notification> <notification name="TeleportToPerson"> - 若想è¯çµ¡ã€Œ[NAME]ã€ç‰ç‰å±…æ°‘ï¼Œä½ å¯ä»¥é–‹å•Ÿèž¢å¹•峿–¹çš„「人群ã€å´é‚Šæ¬„。 -å¾žæ¸…å–®é¸æ“‡ä¸€ä½å±…æ°‘ï¼Œå†æŒ‰å´é‚Šæ¬„底下的「IMã€ã€‚ -ï¼ˆä½ é‚„å¯ä»¥å¾žæ¸…單直接按兩下åå—ï¼Œæˆ–æŒ‰æ»‘é¼ å³éµï¼Œé¸æ“‡ã€ŒIMã€ã€‚) + è¦è¯çµ¡å¦‚「[NAME]ã€çš„任何一ä½å±…æ°‘ï¼Œè«‹é»žæŒ‰ã€Œäººç¾¤ã€æŒ‰éˆ•,從打開的視窗ä¸é¸æ“‡ä¸€ä½å±…民,å†é»žæŒ‰è¦–窗底下的「IMã€ã€‚ + + ï¼ˆä½ é‚„å¯ä»¥å¾žæ¸…單直接按兩下åå—ï¼Œæˆ–æŒ‰æ»‘é¼ å³éµï¼Œé¸æ“‡ã€ŒIMã€ã€‚) </notification> <notification name="CantSelectLandFromMultipleRegions"> ç„¡æ³•é¸æ“‡è¶…出伺æœå™¨é‚Šç•Œçš„土地。 @@ -2245,6 +2387,9 @@ SHA1 指紋:[MD5_DIGEST] <notification name="PaymentSent"> [MESSAGE] </notification> + <notification name="PaymentFailure"> + [MESSAGE] + </notification> <notification name="EventNotification"> 活動通知: @@ -2321,6 +2466,26 @@ SHA1 指紋:[MD5_DIGEST] <notification name="NoBuild"> 這å€åŸŸç¦æ¢å»ºé€ 物件。 ä½ ä¸èƒ½åœ¨æ¤å»ºé€ 或產生物件。 </notification> + <notification name="PathfindingDirty"> + 這地å€çš„尋徑功能有所變更,待儲å˜ã€‚ å¦‚æžœä½ æœ‰å»ºè£½æ¬Šï¼Œä½ å¯ä»¥é»žæŒ‰ã€Œé‡æ–°ç”¢å‡ºåœ°å€ã€æŒ‰éˆ•釿–°ç”¢å‡ºåœ°å€ã€‚ + </notification> + <notification name="DynamicPathfindingDisabled"> + 這地å€ä¸¦æœªå•Ÿç”¨å‹•態尋徑。 使用尋徑 LSL 呼å«çš„帶腳本物件,在æ¤åœ°å€å¯èƒ½ç„¡æ³•æ£å¸¸é‹ä½œã€‚ + </notification> + <notification name="PathfindingRebakeNavmesh"> + 更改本地å€çš„æŸäº›ç‰©ä»¶å°‡å°Žè‡´å…¶ä»–ç§»å‹•ç‰©ä»¶çš„é‹ä½œç™¼ç”Ÿå•題。 è¦ä½¿ç§»å‹•物件æ£å¸¸é‹ä½œï¼Œè«‹é»žæŒ‰ã€Œé‡æ–°ç”¢å‡ºåœ°å€ã€æŒ‰éˆ•。 欲ç²çŸ¥è©³æƒ…è«‹é¸æ“‡ã€Œå¹«åŠ©ã€ã€‚ + <url name="url"> + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + </url> + <usetemplate helptext="幫助" ignoretext="更改本地å€çš„æŸäº›ç‰©ä»¶å°‡å°Žè‡´å…¶ä»–ç§»å‹•ç‰©ä»¶çš„é‹ä½œç™¼ç”Ÿå•題。" name="okhelpignore" yestext="確定"/> + </notification> + <notification name="PathfindingCannotRebakeNavmesh"> + 發生錯誤。 å•題å¯èƒ½å‡ºåœ¨ç¶²è·¯æˆ–伺æœå™¨ï¼Œä¹Ÿå¯èƒ½å› ç‚ºä½ ç„¡æ¬Šå»ºè£½ç‰©ä»¶ã€‚ 有時,åªè¦ç™»å‡ºå†ç™»å…¥å³èƒ½è§£æ±ºé€™é¡žå•題。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SeeAvatars"> + 本地段隱è—其內的化身和èŠå¤©æ–‡å—,其他地段看ä¸åˆ°ã€‚ ä½ çœ‹ä¸è¦‹åœ°æ®µå¤–的居民,他們也看ä¸è¦‹ä½ 。 é »é“ 0 çš„èŠå¤©æ–‡å—也被å°éŽ–ã€‚ + </notification> <notification name="ScriptsStopped"> æŸç®¡ç†å“¡å·²æš«æ™‚åœæ¢å€åŸŸè£¡çš„腳本。 </notification> @@ -2336,9 +2501,7 @@ SHA1 指紋:[MD5_DIGEST] ä½ åªèƒ½åœ¨ä½ 所處的å€åŸŸæ”¶å–公共土地。 </notification> <notification name="RegionTPAccessBlocked"> - ç”±æ–¼ä½ çš„å…§å®¹åˆ†ç´šï¼Œä½ ä¸èƒ½åˆ°è©²åœ°å€ã€‚ ä½ å¯èƒ½éœ€è¦é€šéŽå¹´é½¡é©—è‰ï¼Œä¸”/æˆ–å®‰è£æœ€æ–°ç‰ˆçš„ Viewer。 - -è«‹å‰å¾€çŸ¥è˜åº«çžè§£å¦‚何出入這種內容分級的å€åŸŸã€‚ + ä½ æ‰€æ¬²å‰å¾€çš„地å€å«æœ‰è¶…éŽä½ ç›®å‰å好的分級的內容。 ä½ å¯ä»¥åˆ°ã€Œæˆ‘自己 > å好è¨å®š > 一般è¨å®šã€è®Šæ›´ä½ çš„å好è¨å®šã€‚ </notification> <notification name="URBannedFromRegion"> 這個å€åŸŸç¦æ¢ä½ 進入。 @@ -2349,11 +2512,11 @@ SHA1 指紋:[MD5_DIGEST] <notification name="ImproperPaymentStatus"> ä½ æ²’æœ‰é©ç•¶çš„付款狀態,ä¸èƒ½é€²å…¥é€™å€åŸŸã€‚ </notification> - <notification name="MustGetAgeRgion"> - ä½ å¿…é ˆé€šéŽå¹´é½¡é©—è‰æ‰èƒ½é€²å…¥é€™åœ°å€ã€‚ + <notification name="MustGetAgeRegion"> + ä½ å¿…é ˆå¹´æ»¿ 18 æ²æ‰å¯é€²å…¥é€™åœ°å€ã€‚ </notification> <notification name="MustGetAgeParcel"> - ä½ å¿…é ˆé€šéŽå¹´é½¡é©—è‰ä»¥é€²å…¥é€™åœ°æ®µã€‚ + ä½ å¿…é ˆå¹´æ»¿ 18 æ²æ‰å¯é€²å…¥é€™åœ°æ®µã€‚ </notification> <notification name="NoDestRegion"> 找ä¸åˆ°ç›®çš„地地å€ã€‚ @@ -2415,11 +2578,19 @@ SHA1 指紋:[MD5_DIGEST] </notification> <notification name="ObjectGiveItem"> å為 <nolink>[OBJECTFROMNAME]</nolink>ã€ç”± [NAME_SLURL] æ“æœ‰çš„ç‰©ä»¶çµ¦äº†ä½ é€™å€‹ [OBJECTTYPE]: -[ITEM_SLURL] +<nolink>[ITEM_SLURL]</nolink> + <form name="form"> + <button name="Keep" text="ä¿ç•™"/> + <button name="Discard" text="丟棄"/> + <button name="Mute" text="å°éŽ–æ‰€æœ‰äºº"/> + </form> + </notification> + <notification name="OwnObjectGiveItem"> + ä½ å為 <nolink>[OBJECTFROMNAME]</nolink> çš„ç‰©ä»¶çµ¦äº†ä½ é€™å€‹ [OBJECTTYPE]: +<nolink>[ITEM_SLURL]</nolink> <form name="form"> <button name="Keep" text="ä¿ç•™"/> <button name="Discard" text="丟棄"/> - <button name="Mute" text="å°éŽ–"/> </form> </notification> <notification name="UserGiveItem"> @@ -2447,12 +2618,33 @@ SHA1 指紋:[MD5_DIGEST] <notification name="TeleportOffered"> [NAME_SLURL] 想è¦çž¬é–“傳é€ä½ 到他的地點: -[MESSAGE] - [MATURITY_STR] <icon>[MATURITY_ICON]</icon> +“[MESSAGE]†+<icon>[MATURITY_ICON]</icon> - [MATURITY_STR] <form name="form"> <button name="Teleport" text="瞬間傳é€"/> <button name="Cancel" text="å–æ¶ˆ"/> </form> </notification> + <notification name="TeleportOffered_MaturityExceeded"> + [NAME_SLURL] 想è¦çž¬é–“傳é€ä½ 到他的地點: + +“[MESSAGE]†+<icon>[MATURITY_ICON]</icon> - [MATURITY_STR] + +æ¤åœ°å€åŒ…å« [REGION_CONTENT_MATURITY] çš„åˆ†ç´šå…§å®¹ï¼Œå¯æ˜¯ä½ ç›®å‰çš„å好è¨å®šæŽ’除了 [REGION_CONTENT_MATURITY] 的分級內容。 我們å¯ä»¥è®Šæ›´ä½ çš„å好è¨å®šå¥½è®“ä½ ç¹¼çºŒçž¬é–“å‚³é€ï¼Œä½ 也å¯å–消這動作。 + <form name="form"> + <button name="Teleport" text="變更後繼續"/> + <button name="Cancel" text="å–æ¶ˆ"/> + </form> + </notification> + <notification name="TeleportOffered_MaturityBlocked"> + [NAME_SLURL] 想è¦çž¬é–“傳é€ä½ 到他的地點: + +“[MESSAGE]†+<icon>[MATURITY_ICON]</icon> - [MATURITY_STR] + +坿˜¯ï¼Œæ¤åœ°å€å«æœ‰åƒ…陿ˆäººçš„內容。 + </notification> <notification name="TeleportOfferSent"> å·²å‘ [TO_NAME] 發出瞬間傳é€é‚€è«‹ </notification> @@ -2488,10 +2680,10 @@ SHA1 指紋:[MD5_DIGEST] </form> </notification> <notification name="FriendshipAccepted"> - [NAME] 接å—äº†ä½ çš„äº¤å‹é‚€è«‹ã€‚ + <nolink>[NAME]</nolink> 接å—äº†ä½ çš„äº¤å‹é‚€è«‹ã€‚ </notification> <notification name="FriendshipDeclined"> - [NAME] è¬çµ•ä½ çš„äº¤å‹é‚€è«‹ã€‚ + <nolink>[NAME]</nolink> å©‰æ‹’äº†ä½ çš„äº¤å‹é‚€è«‹ã€‚ </notification> <notification name="FriendshipAcceptedByMe"> 交å‹é‚€è«‹è¢«æŽ¥å—。 @@ -2547,30 +2739,28 @@ SHA1 指紋:[MD5_DIGEST] </form> </notification> <notification name="ScriptQuestionCaution"> - 一個å為 '<nolink>[OBJECTNAME]</nolink>'ã€ç”± '[NAME]' æ“æœ‰çš„物件想è¦ï¼š - -[QUESTIONS] -å¦‚æžœä½ ä¸ä¿¡ä»»é€™ç‰©ä»¶æˆ–å®ƒçš„å‰µé€ äººï¼Œä½ æ‡‰è©²æ‹’çµ•é€™å€‹è¦æ±‚。 - -åŒæ„這個請求? + è¦å‘Šï¼šç‰©ä»¶ '<nolink>[OBJECTNAME]</nolink>' è¦æ±‚全權å˜å–ä½ çš„æž—ç™»å¹£å¸³æˆ¶ã€‚ ä½ å¦‚æžœå…許å˜å–帳戶,它將å¯åœ¨ä»»ä½•æ™‚å€™å¾žä½ å¸³æˆ¶å–èµ°è³‡é‡‘ï¼Œæˆ–å®Œå…¨åŠ ä»¥æ¸…ç©ºï¼Œæˆ–å®šæœŸå–èµ°éƒ¨åˆ†è³‡é‡‘ï¼Œä¸”ä¸æœƒç™¼å‡ºè¦å‘Šã€‚ + +這很å¯èƒ½æ˜¯ç¨®ä¸ç•¶çš„è¦æ±‚。 å¦‚æžœä½ ä¸å®Œå…¨çžè§£å®ƒç‚ºä½•è¦æ±‚å˜å–ä½ çš„å¸³æˆ¶ï¼Œè«‹å‹¿å…准。 <form name="form"> - <button name="Grant" text="åŒæ„"/> + <button name="Grant" text="å…許全權å˜å–"/> <button name="Deny" text="拒絕"/> - <button name="Details" text="細節..."/> </form> </notification> <notification name="ScriptDialog"> [NAME] çš„ '<nolink>[TITLE]</nolink>' [MESSAGE] <form name="form"> - <button name="Ignore" text="忽視"/> + <button name="Client_Side_Mute" text="å°éŽ–"/> + <button name="Client_Side_Ignore" text="忽視"/> </form> </notification> <notification name="ScriptDialogGroup"> [GROUPNAME] çš„ '<nolink>[TITLE]</nolink>' [MESSAGE] <form name="form"> - <button name="Ignore" text="忽視"/> + <button name="Client_Side_Mute" text="å°éŽ–"/> + <button name="Client_Side_Ignore" text="忽視"/> </form> </notification> <notification name="BuyLindenDollarSuccess"> @@ -2748,7 +2938,15 @@ SHA1 指紋:[MD5_DIGEST] é¸å–è¦åˆ†äº«çš„居民。 </notification> <notification name="MeshUploadError"> - [LABEL] 上傳失敗:[MESSAGE] [IDENTIFIER] [INVALIDITY_IDENTIFIER] + [LABEL] 上傳失敗:[MESSAGE] [IDENTIFIER] + +詳見記錄檔。 + </notification> + <notification name="MeshUploadPermError"> + 請求網é¢ä¸Šå‚³æ¬Šé™æ™‚出錯。 + </notification> + <notification name="RegionCapabilityRequestError"> + 無法å–得地å€èƒ½åŠ› '[CAPABILITY]'。 </notification> <notification name="ShareItemsConfirmation"> è«‹ç¢ºå®šä½ è¦å’Œå±…æ°‘åˆ†äº«é€™äº›ç‰©é …ï¼š @@ -2757,6 +2955,18 @@ SHA1 指紋:[MD5_DIGEST] 居民: +[RESIDENTS] + <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="ShareFolderConfirmation"> + 一次åªèƒ½åˆ†äº«ä¸€å€‹è³‡æ–™å¤¾ã€‚ + +è«‹ç¢ºå®šä½ è¦å’Œå±…æ°‘åˆ†äº«é€™äº›ç‰©é …ï¼š + +<nolink>[ITEMS]</nolink> + +居民: + [RESIDENTS] <usetemplate name="okcancelbuttons" notext="å–æ¶ˆ" yestext="確定"/> </notification> @@ -2838,6 +3048,10 @@ SHA1 指紋:[MD5_DIGEST] (å˜çºŒ [EXISTENCE] ç§’é˜ï¼‰ ä½ åœ¨ [TIME] ç§’é˜å¾Œåœ¨æœ¬åœ°ç‚º '[BODYREGION]' 更新了一個 [RESOLUTION] 的定貌æè³ªã€‚ </notification> + <notification name="LivePreviewUnavailable"> + 我們無法顯示這個æè³ªçš„é è¦½ï¼Œå› ç‚ºå®ƒè¨ç‚ºã€Œç¦æ¢è¤‡è£½ã€ä¸” / æˆ–ã€Œç¦æ¢è½‰ç§»ã€ã€‚ + <usetemplate ignoretext="ã€Œç¦æ¢è¤‡è£½ã€å’Œã€Œç¦æ¢è½‰ç§»ã€çš„æè³ªè‹¥ä¸èƒ½ä½¿ç”¨å¯¦æ™‚é 覽模å¼ï¼Œè«‹çµ¦æˆ‘è¦ç¤ºã€‚" name="okignore" yestext="確定"/> + </notification> <notification name="ConfirmLeaveCall"> ä½ ç¢ºå®šè¦é›¢é–‹é€™æ®µé€šè©±ï¼Ÿ <usetemplate ignoretext="æˆ‘çµæŸé€šè©±å‰é€²è¡Œç¢ºèª" name="okcancelignore" notext="å¦" yestext="是"/> @@ -2891,6 +3105,12 @@ SHA1 指紋:[MD5_DIGEST] <notification label="ä½ å¾—åˆ°æž—ç™»å¹£!" name="HintLindenDollar"> é€™è£¡é¡¯ç¤ºä½ ç›®å‰çš„ L$ 餘é¡ã€‚ 點按「購買 L$ã€å¯æ·»è³¼æž—登幣。 </notification> + <notification name="LowMemory"> + ä½ çš„å¯ç”¨è¨˜æ†¶é«”很å°ã€‚ 第二人生部分功能將åœç”¨ï¼Œä»¥å…當機。 請關閉其他應用程å¼ã€‚ 這狀æ³è‹¥æŒçºŒï¼Œè«‹é‡å•Ÿç¬¬äºŒäººç”Ÿã€‚ + </notification> + <notification name="ForceQuitDueToLowMemory"> + 記憶體ä¸è¶³ï¼Œç¬¬äºŒäººç”Ÿå°‡æ–¼ 30 秒後關閉離開。 + </notification> <notification name="PopupAttempt"> 一個çªé¡¯å¼è¦–窗開啟時被阻擋。 <form name="form"> @@ -2898,6 +3118,54 @@ SHA1 指紋:[MD5_DIGEST] <button name="open" text="開啟çªé¡¯å¼è¦–窗"/> </form> </notification> + <notification name="SOCKS_NOT_PERMITTED"> + SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]" 拒絕連通,è¦å‰‡é›†ä¸å…許。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_CONNECT_ERROR"> + SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]" 拒絕連通,無法打開 TCP é »é“。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_NOT_ACCEPTABLE"> + SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]" 拒絕所é¸çš„é‘’èªæ–¹æ³•。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_AUTH_FAIL"> + SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]" å›žå ±ï¼šä½ çš„é‘’èªè³‡æ–™ç„¡æ•ˆã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_UDP_FWD_NOT_GRANTED"> + SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]" 拒絕 UDP è¯çµè«‹æ±‚。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_HOST_CONNECT_FAILED"> + 無法連通 SOCKS 5 代ç†ä¼ºæœå™¨ "[HOST]:[PORT]"。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_UNKNOWN_STATUS"> + 伺æœå™¨ "[HOST]:[PORT]" ç™¼ç”Ÿä¸æ˜Žçš„代ç†ä¼ºæœå™¨éŒ¯èª¤ã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_INVALID_HOST"> + 無效的 SOCKS 代ç†ä¼ºæœå™¨ä½å€æˆ–åŸ è™Ÿ "[HOST]:[PORT]"。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="SOCKS_BAD_CREDS"> + 無效的 SOCKS 5 使用者å稱或密碼。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="PROXY_INVALID_HTTP_HOST"> + 無效的 HTTP 代ç†ä¼ºæœå™¨ä½å€æˆ–åŸ è™Ÿ "[HOST]:[PORT]"。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="PROXY_INVALID_SOCKS_HOST"> + 無效的 SOCKS 代ç†ä¼ºæœå™¨ä½å€æˆ–åŸ è™Ÿ "[HOST]:[PORT]"。 + <usetemplate name="okbutton" yestext="確定"/> + </notification> + <notification name="ChangeProxySettings"> + 釿–°å•Ÿå‹• [APP_NAME] 後將採用新的代ç†ä¼ºæœå™¨è¨å®šã€‚ + <usetemplate name="okbutton" yestext="確定"/> + </notification> <notification name="AuthRequest"> '[REALM]' é ˜åŸŸçš„ '<nolink>[HOST_NAME]</nolink>' 站點需è¦ä½¿ç”¨è€…å稱和密碼。 <form name="form"> @@ -2907,10 +3175,6 @@ SHA1 指紋:[MD5_DIGEST] <button name="cancel" text="å–æ¶ˆ"/> </form> </notification> - <notification label="" name="ModeChange"> - 改變劉覽器模å¼è¦æ±‚ä½ å¿…é ˆçµæŸé€€å‡ºä¸¦é‡æ–°å•Ÿå‹•。 - <usetemplate name="okcancelbuttons" notext="ä¸è¦çµæŸé€€å‡º" yestext="çµæŸé€€å‡º"/> - </notification> <notification label="" name="NoClassifieds"> åªæœ‰é€²éšŽæ¨¡å¼æ‰èƒ½æ–°å»ºæˆ–編輯個人廣告。 ä½ æ˜¯å¦æƒ³è¦çµæŸé›¢é–‹ï¼Œä»¥ä¾¿è®Šæ›´æ¨¡å¼ï¼Ÿ ä½ å¯åœ¨ç™»å…¥ç•«é¢é¸æ“‡æƒ³è¦çš„æ¨¡å¼ã€‚ <usetemplate name="okcancelbuttons" notext="ä¸è¦çµæŸé€€å‡º" yestext="çµæŸé€€å‡º"/> @@ -2955,6 +3219,66 @@ SHA1 指紋:[MD5_DIGEST] åªæœ‰é€²éšŽæ¨¡å¼æ‰èƒ½æœå°‹ã€‚ ä½ æ˜¯å¦è¦ç™»å‡ºä¸¦ä¸”變更模å¼ï¼Ÿ <usetemplate name="okcancelbuttons" notext="ä¸è¦çµæŸé€€å‡º" yestext="çµæŸé€€å‡º"/> </notification> + <notification label="" name="ConfirmHideUI"> + é€™å°‡æœƒéš±è—æ‰€æœ‰é¸å–®å…§å®¹å’ŒæŒ‰éˆ•。 è¦æ¢å¾©åŽŸç‹€ï¼Œå†é»žæŒ‰ [SHORTCUT] 一次。 + <usetemplate ignoretext="éš±è—使用者介é¢å‰å…ˆç¢ºèª" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom"> + 所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ã€‚ + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted"> + æ‰€é¸æŸäº›è¯çµé›†å› 權é™å•題,無法è¨å®šç‚º '[REQUESTED_TYPE]'。 這些è¯çµé›†å°‡è¢«è¨ç‚º '[RESTRICTED_TYPE]'。 + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="æ‰€é¸æŸäº›è¯çµé›†å› 權é™å•題,無法è¨å®šã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnVolume"> + æ‰€é¸æŸäº›è¯çµé›†ç„¡æ³•è¨ç‚º '[REQUESTED_TYPE]'ï¼Œå› ç‚ºå½¢ç‹€å±¬æ–¼éžå‡¸é¢ã€‚ + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="æ‰€é¸æŸäº›è¯çµé›†å› 為形狀屬於éžå‡¸é¢ï¼Œç„¡æ³•è¨å®š" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted"> + 所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ã€‚ + +æ‰€é¸æŸäº›è¯çµé›†å› 權é™å•題,無法è¨å®šç‚º '[REQUESTED_TYPE]'。 這些è¯çµé›†å°‡è¢«è¨ç‚º '[RESTRICTED_TYPE]'。 + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="所é¸çš„一些è¯çµé›†çš„å¹»å½±æ——æ¨™å¯æˆåŠŸåˆ‡æ›ï¼Œå…¶ä»–çš„å‰‡å› æ¬Šé™å•題而無法è¨å®šã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnVolume"> + 所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ã€‚ + +æ‰€é¸æŸäº›è¯çµé›†ç„¡æ³•è¨ç‚º '[REQUESTED_TYPE]'ï¼Œå› ç‚ºå½¢ç‹€å±¬æ–¼éžå‡¸é¢ã€‚ + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="所é¸çš„一些è¯çµé›†çš„å¹»å½±æ——æ¨™å¯æˆåŠŸåˆ‡æ›ï¼Œå…¶ä»–çš„å‰‡å› å½¢ç‹€å±¬æ–¼éžå‡¸é¢è€Œç„¡æ³•è¨å®š" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_MismatchOnRestricted_MismatchOnVolume"> + æ‰€é¸æŸäº›è¯çµé›†å› 權é™å•題,無法è¨å®šç‚º '[REQUESTED_TYPE]'。 這些è¯çµé›†å°‡è¢«è¨ç‚º '[RESTRICTED_TYPE]'。 + +æ‰€é¸æŸäº›è¯çµé›†ç„¡æ³•è¨ç‚º '[REQUESTED_TYPE]'ï¼Œå› ç‚ºå½¢ç‹€å±¬æ–¼éžå‡¸é¢ã€‚ 這些è¯çµé›†çš„ä½¿ç”¨é¡žåž‹å°‡ç¶æŒä¸è®Šã€‚ + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="æ‰€é¸æŸäº›è¯çµé›†å› 權é™ä¸è¶³ï¼Œä¸”形狀屬於éžå‡¸é¢ï¼Œå› æ¤ç„¡æ³•è¨å®šã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_WarnOnPhantom_MismatchOnRestricted_MismatchOnVolume"> + 所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ã€‚ + +æ‰€é¸æŸäº›è¯çµé›†å› 權é™å•題,無法è¨å®šç‚º '[REQUESTED_TYPE]'。 這些è¯çµé›†å°‡è¢«è¨ç‚º '[RESTRICTED_TYPE]'。 + +æ‰€é¸æŸäº›è¯çµé›†ç„¡æ³•è¨ç‚º '[REQUESTED_TYPE]'ï¼Œå› ç‚ºå½¢ç‹€å±¬æ–¼éžå‡¸é¢ã€‚ 這些è¯çµé›†çš„ä½¿ç”¨é¡žåž‹å°‡ç¶æŒä¸è®Šã€‚ + +ä½ ç¢ºå®šè¦ç¹¼çºŒå—Žï¼Ÿ + <usetemplate ignoretext="所é¸çš„一些è¯çµé›†çš„幻影旗標將被切æ›ï¼Œå…¶ä»–å‰‡å› æ¬Šé™ä¸è¶³ä¸”形狀屬於éžå‡¸é¢ï¼Œå› æ¤ç„¡æ³•è¨å®šã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> + <notification name="PathfindingLinksets_ChangeToFlexiblePath"> + 所é¸çš„物件會影響導航網é¢ã€‚ 將它改為彈性路徑,將使它從導航網é¢ä¸è¢«ç§»é™¤ã€‚ + <usetemplate ignoretext="所é¸çš„物件會影響導航網é¢ã€‚ 將它改為彈性路徑,將使它從導航網é¢ä¸è¢«ç§»é™¤ã€‚" name="okcancelignore" notext="å–æ¶ˆ" yestext="確定"/> + </notification> <global name="UnsupportedGLRequirements"> ä½ çš„ç¡¬é«”è¨å‚™ä¼¼ä¹Žä¸ç¬¦ [APP_NAME] çš„è¦æ±‚。 [APP_NAME] 需è¦å¯ä»¥æ”¯æ´å¤šæè³ªçš„ OpenGL 顯åƒå¡ã€‚ 在這狀æ³ä¸‹ï¼Œè«‹ç¢ºå®šä½ 的顯åƒå¡å®‰è£äº†æœ€æ–°çš„驅動程å¼ï¼Œä½œæ¥ç³»çµ±ä¹Ÿå®‰è£äº†æœ€æ–°çš„æœå‹™åŒ…å’ŒåµŒè£œç¨‹å¼ã€‚ @@ -2979,4 +3303,24 @@ SHA1 指紋:[MD5_DIGEST] <global name="You died and have been teleported to your home location"> ä½ å·²ç¶“æ»äº¡ä¸¦ä¸”被瞬間傳é€å›žä½ 的家的ä½ç½®ã€‚ </global> + <notification name="LocalBitmapsUpdateFileNotFound"> + [FNAME] 無法更新,找ä¸åˆ°è©²æª”案。 +未來將ä¸å†æ›´æ–°è©²æª”案。 + </notification> + <notification name="LocalBitmapsUpdateFailedFinal"> + [FNAME] 無法開啟或解碼,已嘗試 [NRETRIES] 次,該檔案已被èªå®šç‚ºæ¯€å£žã€‚ +未來將ä¸å†æ›´æ–°è©²æª”案。 + </notification> + <notification name="LocalBitmapsVerifyFail"> + 試圖新增一個無效或無法讀å–çš„åœ–åƒæª” [FNAME],該檔無法開啟或解碼。 +已喿¶ˆé€™ä¸€å˜—試。 + </notification> + <notification name="PathfindingReturnMultipleItems"> + ä½ æ£é€€å›ž [NUM_ITEMS] å€‹ç‰©é …ã€‚ ä½ ç¢ºå®šä½ è¦ç¹¼çºŒï¼Ÿ + <usetemplate ignoretext="ç¢ºå®šé€€å›žå¤šå€‹ç‰©é …ï¼Ÿ" name="okcancelignore" notext="å¦" yestext="是"/> + </notification> + <notification name="PathfindingDeleteMultipleItems"> + ä½ æ£åœ¨åˆªé™¤ [NUM_ITEMS] å€‹ç‰©é …ã€‚ ä½ ç¢ºå®šä½ è¦ç¹¼çºŒï¼Ÿ + <usetemplate ignoretext="確定è¦åˆªé™¤å¤šå€‹ç‰©é …?" name="okcancelignore" notext="å¦" yestext="是"/> + </notification> </notifications> diff --git a/indra/newview/skins/default/xui/zh/panel_chiclet_bar.xml b/indra/newview/skins/default/xui/zh/panel_chiclet_bar.xml new file mode 100644 index 0000000000000000000000000000000000000000..69340349bcf72837a645e2f09280fd1e96cabf80 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_chiclet_bar.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="chiclet_bar"> + <layout_stack name="toolbar_stack"> + <layout_panel name="im_well_panel"> + <chiclet_im_well name="im_well"> + <button name="Unread IM messages" tool_tip="交談"/> + </chiclet_im_well> + </layout_panel> + <layout_panel name="notification_well_panel"> + <chiclet_notification name="notification_well"> + <button name="Unread" tool_tip="通知"/> + </chiclet_notification> + </layout_panel> + </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_edit_pick.xml b/indra/newview/skins/default/xui/zh/panel_edit_pick.xml index 006c050dc136c904949a03ab17d691367b42d91d..faee42fd0e3e44a0834220375191f60f8d961cb1 100644 --- a/indra/newview/skins/default/xui/zh/panel_edit_pick.xml +++ b/indra/newview/skins/default/xui/zh/panel_edit_pick.xml @@ -29,7 +29,7 @@ <layout_panel name="layout_panel1"> <button label="儲å˜ç²¾é¸åœ°é»ž" name="save_changes_btn"/> </layout_panel> - <layout_panel name="layout_panel1"> + <layout_panel name="layout_panel2"> <button label="å–æ¶ˆ" name="cancel_btn"/> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/zh/panel_edit_skin.xml b/indra/newview/skins/default/xui/zh/panel_edit_skin.xml index 9f4c02427da5f793aa111d16c1b0047607d35459..d8552f52f0bf819441e62194361178045bb505c4 100644 --- a/indra/newview/skins/default/xui/zh/panel_edit_skin.xml +++ b/indra/newview/skins/default/xui/zh/panel_edit_skin.xml @@ -1,9 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="edit_skin_panel"> <panel name="avatar_skin_color_panel"> - <texture_picker label="é 部刺é’" name="Head Tattoos" tool_tip="點按以挑é¸åœ–片"/> - <texture_picker label="上åŠèº«åˆºé’" name="Upper Tattoos" tool_tip="點按以挑é¸åœ–片"/> - <texture_picker label="下åŠèº«åˆºé’" name="Lower Tattoos" tool_tip="點按以挑é¸åœ–片"/> + <texture_picker label="é 部" name="Head" tool_tip="點按以挑é¸åœ–片"/> + <texture_picker label="上åŠèº«" name="Upper Body" tool_tip="點按以挑é¸åœ–片"/> + <texture_picker label="下åŠèº«" name="Lower Body" tool_tip="點按以挑é¸åœ–片"/> </panel> <panel name="accordion_panel"> <accordion name="wearable_accordion"> diff --git a/indra/newview/skins/default/xui/zh/panel_group_invite.xml b/indra/newview/skins/default/xui/zh/panel_group_invite.xml index 728311cf519ad9419feb95c856436582393b827b..8921978b202b7c546868858dc50e4394d95d124d 100644 --- a/indra/newview/skins/default/xui/zh/panel_group_invite.xml +++ b/indra/newview/skins/default/xui/zh/panel_group_invite.xml @@ -9,6 +9,9 @@ <panel.string name="already_in_group"> ä½ æ‰€é¸çš„å±…æ°‘æœ‰äº›å·²ç¶“åœ¨ç¾¤çµ„è£¡ï¼Œæ‰€ä»¥ä¸æœƒå†å°ä»–們發é€é‚€è«‹ã€‚ </panel.string> + <panel.string name="invite_selection_too_large"> + 未é€å‡ºç¾¤çµ„邀請:所é¸çš„居民人數太多。 ç¾¤çµ„é‚€è«‹æ¯æ¬¡ä»¥ 100 人為上é™ã€‚ + </panel.string> <text name="help_text"> ä½ å¯ä»¥é¸æ“‡é‚€è«‹å¤šä½å±…æ°‘åˆ°ä½ çš„ç¾¤çµ„ä¾†ã€‚ é»žæŒ‰ã€Œé–‹å•Ÿå±…æ°‘é¸æ“‡å·¥å…·ã€é–‹å§‹é¸æ“‡ã€‚ </text> diff --git a/indra/newview/skins/default/xui/zh/panel_login.xml b/indra/newview/skins/default/xui/zh/panel_login.xml index 49c57a4315dcb832b8d2aafed4931cb03ab045da..672d9bb1a263c43f2b73391038b0501e02623e5a 100644 --- a/indra/newview/skins/default/xui/zh/panel_login.xml +++ b/indra/newview/skins/default/xui/zh/panel_login.xml @@ -1,13 +1,13 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="panel_login"> - <panel.string name="create_account_url"> - http://join.secondlife.com/ - </panel.string> <panel.string name="forgot_password_url"> http://secondlife.com/account/request.php </panel.string> <layout_stack name="login_widgets"> <layout_panel name="login"> + <text name="log_in_text"> + 登入 + </text> <text name="username_text"> 使用者å稱: </text> @@ -15,15 +15,8 @@ <text name="password_text"> 密碼: </text> - <check_box label="記ä½å¯†ç¢¼ï¼š" name="remember_check"/> - <button label="登入" name="connect_btn"/> - <text name="mode_selection_text"> - 模å¼ï¼š - </text> - <combo_box name="mode_combo" tool_tip="è«‹é¸æ“‡ä½ 的模å¼ã€‚ é¸ç”¨åŸºæœ¬æ¨¡å¼å¯ä»¥å¿«é€Ÿã€ç°¡å–®åœ°æŽ¢ç´¢èˆ‡èŠå¤©ï¼› é¸ç”¨é€²éšŽæ¨¡å¼å‰‡å¯ä»¥ä½¿ç”¨æ›´å¤šåŠŸèƒ½ã€‚"> - <combo_box.item label="基本" name="Basic"/> - <combo_box.item label="進階" name="Advanced"/> - </combo_box> + </layout_panel> + <layout_panel name="start_location_panel"> <text name="start_location_text"> 開始地點: </text> @@ -33,16 +26,21 @@ <combo_box.item label="<請輸入地å€å稱>" name="Typeregionname"/> </combo_box> </layout_panel> - <layout_panel name="links"> - <text name="create_new_account_text"> - 註冊 + <layout_panel name="links_login_panel"> + <text name="login_help"> + 登入時需è¦å¹«åŠ©ï¼Ÿ </text> <text name="forgot_password_text"> å¿˜è¨˜ä½ çš„ä½¿ç”¨è€…å稱或密碼? </text> - <text name="login_help"> - 登入時需è¦å¹«åŠ©ï¼Ÿ + <button label="登入" name="connect_btn"/> + <check_box label="記ä½å¯†ç¢¼ï¼š" name="remember_check"/> + </layout_panel> + <layout_panel name="links"> + <text name="create_account_text"> + å»ºç«‹ä½ çš„å¸³è™Ÿ </text> + <button label="ç¾åœ¨å°±é–‹å§‹" name="create_new_account_btn"/> </layout_panel> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_me.xml b/indra/newview/skins/default/xui/zh/panel_me.xml index 3c452b8fa83d7e96ee41d333fbf326aeb033b0aa..aad1348e4680d265507af7e5a7b44e8a21de1c6c 100644 --- a/indra/newview/skins/default/xui/zh/panel_me.xml +++ b/indra/newview/skins/default/xui/zh/panel_me.xml @@ -1,7 +1,4 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="我的個人檔案" name="panel_me"> - <tab_container name="tabs"> - <panel label="我的檔案" name="panel_profile"/> - <panel label="我的精é¸åœ°é»ž" name="panel_picks"/> - </tab_container> + <panel label="我的精é¸åœ°é»ž" name="panel_picks"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_navigation_bar.xml b/indra/newview/skins/default/xui/zh/panel_navigation_bar.xml index 2f98dd718e3ff13dfe467aa37680a4407eb17c16..7a11aa961bdf53865dbdce807bc64a28468df530 100644 --- a/indra/newview/skins/default/xui/zh/panel_navigation_bar.xml +++ b/indra/newview/skins/default/xui/zh/panel_navigation_bar.xml @@ -1,18 +1,23 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="navigation_bar"> - <panel name="navigation_panel"> - <pull_button name="back_btn" tool_tip="å‰å¾€ä¸Šä¸€å€‹ä½ç½®"/> - <pull_button name="forward_btn" tool_tip="å‰å¾€ä¸‹ä¸€å€‹ä½ç½®"/> - <button name="home_btn" tool_tip="瞬間返回我的家"/> - <location_input label="ä½ç½®" name="location_combo"/> - <search_combo_box label="æœå°‹" name="search_combo_box" tool_tip="æœå°‹"> - <combo_editor label="æœå°‹ [SECOND_LIFE]" name="search_combo_editor"/> - </search_combo_box> - </panel> - <favorites_bar name="favorite" tool_tip="拖曳傳é€åœ°æ¨™åˆ°æ¤ä»¥ä¾¿è®“ä½ åœ¨ç¬¬äºŒäººç”Ÿä¸èƒ½å¿«é€Ÿå‚³é€åˆ°ä½ 最愛的地點!!"> - <label name="favorites_bar_label" tool_tip="拖曳傳é€åœ°æ¨™åˆ°æ¤ä»¥ä¾¿è®“ä½ åœ¨ç¬¬äºŒäººç”Ÿä¸èƒ½å¿«é€Ÿå‚³é€åˆ°ä½ 最愛的地點!!"> - 最愛列 - </label> - <chevron_button name=">>" tool_tip="顯示更多我的最愛"/> - </favorites_bar> + <layout_stack name="nvp_stack"> + <layout_panel name="navigation_layout_panel"> + <panel name="navigation_panel"> + <pull_button name="back_btn" tool_tip="å‰å¾€ä¸Šä¸€å€‹ä½ç½®"/> + <pull_button name="forward_btn" tool_tip="å‰å¾€ä¸‹ä¸€å€‹ä½ç½®"/> + <button name="home_btn" tool_tip="瞬間返回我的家"/> + <location_input label="ä½ç½®" name="location_combo"/> + </panel> + </layout_panel> + <layout_panel name="favorites_layout_panel"> + <favorites_bar name="favorite" tool_tip="拖曳傳é€åœ°æ¨™åˆ°æ¤ä»¥ä¾¿è®“ä½ åœ¨ç¬¬äºŒäººç”Ÿä¸èƒ½å¿«é€Ÿå‚³é€åˆ°ä½ 最愛的地點!!"> + <label name="favorites_bar_label" tool_tip="拖曳傳é€åœ°æ¨™åˆ°æ¤ä»¥ä¾¿è®“ä½ åœ¨ç¬¬äºŒäººç”Ÿä¸èƒ½å¿«é€Ÿå‚³é€åˆ°ä½ 最愛的地點!!"> + 最愛列 + </label> + <more_button name=">>" tool_tip="顯示更多我的最愛"> + 詳情 â–¼ + </more_button> + </favorites_bar> + </layout_panel> + </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_navmesh_rebake.xml b/indra/newview/skins/default/xui/zh/panel_navmesh_rebake.xml new file mode 100644 index 0000000000000000000000000000000000000000..bb52c13a1136ab864158f9bfcb52bfbda656be1f --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_navmesh_rebake.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_navmesh_rebake"> + <button label="釿–°ç”¢å‡ºåœ°å€" name="navmesh_btn" tool_tip="點按å³å¯é‡æ–°ç”¢å‡ºè©²åœ°å€çš„導航網é¢ã€‚"/> + <button label="æ£åœ¨è«‹æ±‚釿–°ç”¢å‡º" name="navmesh_btn_sending" tool_tip="æ£å‘伺æœå™¨ç™¼é€é‡æ–°ç”¢å‡ºè«‹æ±‚。"/> + <button label="åœ°å€æ£åœ¨é‡æ–°ç”¢å‡º" name="navmesh_btn_baking" tool_tip="åœ°å€æ£åœ¨é‡æ–°ç”¢å‡ºã€‚ 完æˆå¾Œï¼Œé€™å€‹æŒ‰éˆ•將會消失。"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_nearby_chat.xml b/indra/newview/skins/default/xui/zh/panel_nearby_chat.xml new file mode 100644 index 0000000000000000000000000000000000000000..fc52168bb75843f6c3ec1b50dea8c1b499f203f2 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_nearby_chat.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="nearby_chat"> + <layout_stack name="stack"> + <layout_panel name="translate_chat_checkbox_lp"> + <check_box label="ç¿»è¯èŠå¤©å…§å®¹" name="translate_chat_checkbox"/> + </layout_panel> + </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_outbox_inventory.xml b/indra/newview/skins/default/xui/zh/panel_outbox_inventory.xml new file mode 100644 index 0000000000000000000000000000000000000000..8de0bb0e4d143bf38c03595513c77493d67fc3d1 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_outbox_inventory.xml @@ -0,0 +1,2 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<outbox_inventory_panel name="inventory_outbox" tool_tip="å°‡ç‰©é …æ‹–æ›³ä¸¦ç½®æ”¾åˆ°é€™è£¡ï¼Œæº–å‚™åœ¨ä½ çš„å•†åº—å‡ºå”®"/> diff --git a/indra/newview/skins/default/xui/zh/panel_outfits_list.xml b/indra/newview/skins/default/xui/zh/panel_outfits_list.xml index a4b041469b609b48bda5ecddf536dc7b3a1647e0..cfb0180f9cfa9abda818802bc4e733cf4d651e56 100644 --- a/indra/newview/skins/default/xui/zh/panel_outfits_list.xml +++ b/indra/newview/skins/default/xui/zh/panel_outfits_list.xml @@ -1,5 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel name="Outfits"> + <accordion name="outfits_accordion"> + <no_matched_tabs_text name="no_matched_outfits_msg" value="找ä¸åˆ°ä½ è¦æ‰¾çš„嗎? 請試試 [secondlife:///app/search/places/ æœå°‹]。"/> + <no_visible_tabs_text name="no_outfits_msg" value="ä½ é‚„æ²’æœ‰ä»»ä½•è£æ‰®ã€‚ 請試試[secondlife:///app/search/all/ æœå°‹]"/> + </accordion> <panel name="bottom_panel"> <menu_button name="options_gear_btn" tool_tip="顯示é¡å¤–é¸é …"/> <button name="trash_btn" tool_tip="åˆªé™¤æ‰€é¸æ“‡çš„è£æ‰®"/> diff --git a/indra/newview/skins/default/xui/zh/panel_people.xml b/indra/newview/skins/default/xui/zh/panel_people.xml index dbb439afec7b1ea2e667943209039ab581cad024..59ea7b70e2dbe870d74b53c6e9db81ce8e093af8 100644 --- a/indra/newview/skins/default/xui/zh/panel_people.xml +++ b/indra/newview/skins/default/xui/zh/panel_people.xml @@ -66,16 +66,16 @@ <layout_panel name="view_profile_btn_lp"> <button label="檔案" name="view_profile_btn" tool_tip="顯示圖片ã€ç¾¤çµ„與其他居民資訊"/> </layout_panel> - <layout_panel name="chat_btn_lp"> + <layout_panel name="im_btn_lp"> <button label="IM" name="im_btn" tool_tip="é–‹å•Ÿå³æ™‚è¨Šæ¯æœƒè©±"/> </layout_panel> - <layout_panel name="chat_btn_lp"> + <layout_panel name="call_btn_lp"> <button label="通話" name="call_btn" tool_tip="和這ä½å±…民通話"/> </layout_panel> - <layout_panel name="chat_btn_lp"> + <layout_panel name="share_btn_lp"> <button label="分享" name="share_btn" tool_tip="分享一個收ç´å€ç‰©å“"/> </layout_panel> - <layout_panel name="chat_btn_lp"> + <layout_panel name="teleport_btn_lp"> <button label="瞬間傳é€" name="teleport_btn" tool_tip="發出瞬間傳é€é‚€è«‹"/> </layout_panel> </layout_stack> diff --git a/indra/newview/skins/default/xui/zh/panel_place_profile.xml b/indra/newview/skins/default/xui/zh/panel_place_profile.xml index a32c1c17d328a0a356da2aa79aac98524b8d8688..a364f732d88e39300986adcdca1518686f535eb1 100644 --- a/indra/newview/skins/default/xui/zh/panel_place_profile.xml +++ b/indra/newview/skins/default/xui/zh/panel_place_profile.xml @@ -68,6 +68,8 @@ <text name="scripts_value" value="啟動"/> <text name="damage_label" value="傷害:"/> <text name="damage_value" value="關閉"/> + <text name="see_avatars_label" value="察看化身:"/> + <text name="see_avatars_value" value="關閉"/> <button label="土地資料" name="about_land_btn"/> </panel> </accordion_tab> diff --git a/indra/newview/skins/default/xui/zh/panel_places.xml b/indra/newview/skins/default/xui/zh/panel_places.xml index ac69b48a818231ac101fa959f60786dd70b40f24..08cae610f6b86e00877852ad3f23e350d6a1edaf 100644 --- a/indra/newview/skins/default/xui/zh/panel_places.xml +++ b/indra/newview/skins/default/xui/zh/panel_places.xml @@ -24,7 +24,7 @@ <menu_button name="overflow_btn" tool_tip="顯示é¡å¤–é¸é …"/> </layout_panel> </layout_stack> - <layout_stack name="bottom_bar_ls3"> + <layout_stack name="bottom_bar_profile_ls"> <layout_panel name="profile_btn_lp"> <button label="檔案" name="profile_btn" tool_tip="顯示地點檔案"/> </layout_panel> diff --git a/indra/newview/skins/default/xui/zh/panel_postcard_message.xml b/indra/newview/skins/default/xui/zh/panel_postcard_message.xml new file mode 100644 index 0000000000000000000000000000000000000000..563c4fca3c40c35e7e5ee4e4f5163f3b60d5ef3f --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_postcard_message.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_postcard_message"> + <text name="to_label"> + 收件人: + </text> + <text name="name_label"> + 發件人: + </text> + <text name="subject_label"> + 主旨: + </text> + <line_editor label="在æ¤è¼¸å…¥ä½ 的主旨。" name="subject_form"/> + <text name="msg_label"> + 訊æ¯ï¼š + </text> + <text_editor name="msg_form"> + 在æ¤è¼¸å…¥ä½ 的訊æ¯ã€‚ + </text_editor> + <button label="å–æ¶ˆ" name="cancel_btn"/> + <button label="é€å‡º" name="send_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_postcard_settings.xml b/indra/newview/skins/default/xui/zh/panel_postcard_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..900ab3a54e5fffec0f0c1e858d1c16df97206bd9 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_postcard_settings.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_postcard_settings"> + <combo_box label="è§£æžåº¦" name="postcard_size_combo"> + <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> + <combo_box.item label="640x480" name="640x480"/> + <combo_box.item label="800x600" name="800x600"/> + <combo_box.item label="1024x768" name="1024x768"/> + <combo_box.item label="自訂" name="Custom"/> + </combo_box> + <layout_stack name="postcard_image_params_ls"> + <layout_panel name="postcard_image_size_lp"> + <spinner label="寬" name="postcard_snapshot_width"/> + <spinner label="高度" name="postcard_snapshot_height"/> + <check_box label="éŽ–ä½æ¯”例" name="postcard_keep_aspect_check"/> + </layout_panel> + <layout_panel name="postcard_image_format_quality_lp"> + <slider label="圖åƒå“質" name="image_quality_slider"/> + <text name="image_quality_level"> + ([QLVL]) + </text> + </layout_panel> + </layout_stack> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/zh/panel_preferences_advanced.xml index c5dce10d638740c7611e409b91786b191cbb4f46..3a7d79e04be6df13a2dc4882e35fce77c69ffa97 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_advanced.xml @@ -3,6 +3,19 @@ <panel.string name="aspect_ratio_text"> [NUM]:[DEN] </panel.string> + <text name="Cache:"> + å¿«å–: + </text> + <spinner label="å¿«å–å¤§å° (64 - 9984MB)" name="cachesizespinner"/> + <text name="text_box5"> + MB + </text> + <button label="清除快å–" label_selected="清除快å–" name="clear_cache"/> + <text name="Cache location"> + å¿«å–ä½ç½®ï¼š + </text> + <button label="ç€è¦½" label_selected="ç€è¦½" name="set_cache"/> + <button label="é è¨ä½ç½®" label_selected="é è¨ä½ç½®" name="default_cache_location"/> <text name="UI Size:"> 使用者界é¢å°ºå¯¸ï¼š </text> diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_chat.xml b/indra/newview/skins/default/xui/zh/panel_preferences_chat.xml index 82d2c64aaa6a61cf0cb54b71eced63f5f355fc40..cf2f81d3139cdd02913a4209022c2b4bceb4e142 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_chat.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_chat.xml @@ -29,29 +29,7 @@ <check_box label="IM èŠå¤©" name="EnableIMChatPopups" tool_tip="ç•¶å³æ™‚è¨Šæ¯æŠµé”æ™‚查看çªé¡¯å¼è¦–窗"/> <spinner label="附近èŠå¤©å…§å®¹æç¤ºæ¡†åœé§æ™‚間:" name="nearby_toasts_lifetime"/> <spinner label="附近èŠå¤©å…§å®¹æç¤ºæ¡†æ¶ˆé€€æ™‚間:" name="nearby_toasts_fadingtime"/> - <text name="translate_chb_label"> - èŠå¤©æ™‚使用機器自動進行翻è¯ï¼ˆç”± Google 所æä¾›ï¼‰ - </text> - <text name="translate_language_text"> - å°‡èŠå¤©å…§å®¹ç¿»è¯æˆï¼š - </text> - <combo_box name="translate_language_combobox"> - <combo_box.item label="系統é è¨" name="System Default Language"/> - <combo_box.item label="英語" name="English"/> - <combo_box.item label="Dansk(丹麥語)" name="Danish"/> - <combo_box.item label="Deutsch(德語)" name="German"/> - <combo_box.item label="Español(西ç牙語)" name="Spanish"/> - <combo_box.item label="Français(法語)" name="French"/> - <combo_box.item label="Italiano(義大利語)" name="Italian"/> - <combo_box.item label="Magyar(匈牙利語)" name="Hungarian"/> - <combo_box.item label="Nederlands(è·è˜èªžï¼‰" name="Dutch"/> - <combo_box.item label="Polski(波è˜èªžï¼‰" name="Polish"/> - <combo_box.item label="Português(葡è„牙語)" name="Portugese"/> - <combo_box.item label="РуÑÑкий(俄羅斯語)" name="Russian"/> - <combo_box.item label="Türkçe(土耳其語)" name="Turkish"/> - <combo_box.item label="УкраїнÑька(çƒå…‹è˜èªžï¼‰" name="Ukrainian"/> - <combo_box.item label="䏿–‡ï¼ˆç®€ä½“ï¼‰ï¼ˆç°¡é«”ä¸æ–‡ï¼‰" name="Chinese"/> - <combo_box.item label="日本語(日語)" name="Japanese"/> - <combo_box.item label="한êµì–´ï¼ˆæ¼¢èªžï¼‰" name="Korean"/> - </combo_box> + <button label="ç¿»è¯â€¦" name="ok_btn"/> + <button label="自動å–代…" name="autoreplace_showgui"/> + <button label="æ‹¼å—æª¢æŸ¥â€¦" name="spellcheck_showgui"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_general.xml b/indra/newview/skins/default/xui/zh/panel_preferences_general.xml index 854b1cf828f3bb2515e74c84940b8c96c366eb4e..7e67a0d02d0b2410de17e0bec1346ec415378dea 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_general.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_general.xml @@ -11,10 +11,12 @@ <combo_box.item label="Español(西ç牙語)- 試用版" name="Spanish"/> <combo_box.item label="Français(法語)- 試用版" name="French"/> <combo_box.item label="Italiano(義大利語)- 試用版" name="Italian"/> - <combo_box.item label="Nederlands(è·è˜èªžï¼‰- 試用版" name="Dutch"/> <combo_box.item label="Polski(波è˜èªžï¼‰- 試用版" name="Polish"/> <combo_box.item label="Português(葡è„牙語)- 試用版" name="Portugese"/> + <combo_box.item label="РуÑÑкий(俄羅斯語)- 測試版" name="Russian"/> + <combo_box.item label="Türkçe(土耳其語)- 試用版" name="Turkish"/> <combo_box.item label="日本語(日語)- 試用版" name="(Japanese)"/> + <combo_box.item label="æ£é«”䏿–‡ - 測試版" name="Traditional Chinese"/> </combo_box> <text name="language_textbox2"> ï¼ˆé ˆé‡æ–°å•Ÿå‹•) diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_move.xml b/indra/newview/skins/default/xui/zh/panel_preferences_move.xml index ce176b1e3c5870d80ac4ed3e2a8c2494da699bfa..3a274778854c48c04ddef8127e7b8579aa6cc7d8 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_move.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_move.xml @@ -7,18 +7,33 @@ </text> <check_box label="å»ºé€ / 編輯" name="edit_camera_movement" tool_tip="ä½¿ç”¨é€²å…¥æˆ–é›¢é–‹ç·¨è¼¯æ¨¡å¼æ™‚自動調整æ”影機ä½ç½®åŠŸèƒ½"/> <check_box label="編輯外觀" name="appearance_camera_movement" tool_tip="ä½¿ç”¨ç·¨è¼¯æ¨¡å¼æ™‚自動調整æ”影機ä½ç½®åŠŸèƒ½"/> - <check_box initial_value="true" label="å´é‚Šæ¬„" name="appearance_sidebar_positioning" tool_tip="使用開啟å´é‚Šæ¬„時自動調整æ”影機ä½ç½®åŠŸèƒ½"/> + <text name="keyboard_lbl"> + éµç›¤ï¼š + </text> + <check_box label="總是使用方å‘éµç§»å‹•" name="arrow_keys_move_avatar_check"/> + <check_box label="連點按ä½å¾Œè·‘æ¥" name="tap_tap_hold_to_run"/> + <text name="mouse_lbl"> + æ»‘é¼ ï¼š + </text> <check_box label="將我顯示於第一人稱視角ä¸" name="first_person_avatar_visible"/> <text name=" Mouse Sensitivity"> ç¬¬ä¸€äººç¨±è¦–è§’æ»‘é¼ æ•æ„Ÿåº¦ï¼š </text> <check_box label="å轉" name="invert_mouse"/> - <check_box label="總是使用方å‘éµç§»å‹•" name="arrow_keys_move_avatar_check"/> - <check_box label="連點按ä½å¾Œè·‘æ¥" name="tap_tap_hold_to_run"/> - <check_box label="雙擊以:" name="double_click_chkbox"/> - <radio_group name="double_click_action"> - <radio_item label="瞬間傳é€" name="radio_teleport"/> - <radio_item label="自動導航駕駛" name="radio_autopilot"/> - </radio_group> + <text name="single_click_action_lbl"> + 在土地上點按一下: + </text> + <combo_box name="single_click_action_combo"> + <combo_box.item label="無動作" name="0"/> + <combo_box.item label="移動至點按的地點" name="1"/> + </combo_box> + <text name="double_click_action_lbl"> + 在土地上點按兩下: + </text> + <combo_box name="double_click_action_combo"> + <combo_box.item label="無動作" name="0"/> + <combo_box.item label="移動至點按的地點" name="1"/> + <combo_box.item label="瞬間傳é€è‡³é»žæŒ‰çš„地點" name="2"/> + </combo_box> <button label="å…¶ä»–è¨å‚™" name="joystick_setup_button"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_setup.xml b/indra/newview/skins/default/xui/zh/panel_preferences_setup.xml index 85f8154b99b51fb0660f660db04b72c57ac5693d..a607a7c33b936722bcf1e637b5d8ab1e467470f7 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_setup.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_setup.xml @@ -11,17 +11,6 @@ </text> <check_box label="è‡ªè¨‚åŸ " name="connection_port_enabled"/> <spinner label="åŸ è™Ÿï¼š" name="connection_port"/> - <text name="cache_size_label_l"> - å¿«å–尺寸 - </text> - <text name="text_box5"> - MB - </text> - <text name="Cache location"> - å¿«å–ä½ç½®ï¼š - </text> - <button label="ç€è¦½" label_selected="ç€è¦½" name="set_cache"/> - <button label="é‡è¨" label_selected="é‡è¨" name="reset_cache"/> <text name="Web:"> ç¶²é : </text> @@ -33,12 +22,6 @@ <check_box initial_value="true" label="æŽ¥å— cookies" name="cookies_enabled"/> <check_box initial_value="true" label="啟用 Javascript" name="browser_javascript_enabled"/> <check_box initial_value="false" label="啟用媒體ç€è¦½çš„çªé¡¯å¼è¦–窗" name="media_popup_enabled"/> - <check_box initial_value="false" label="啟用網é 代ç†ä¼ºæœå™¨" name="web_proxy_enabled"/> - <text name="Proxy location"> - 代ç†ä¼ºæœå™¨ä½ç½®ï¼š - </text> - <line_editor name="web_proxy_editor" tool_tip="ä½ å¸Œæœ›ä½¿ç”¨çš„ä»£ç†ä¼ºæœå™¨çš„å稱或 IP ä½å€"/> - <spinner label="åŸ è™Ÿï¼š" name="web_proxy_port"/> <text name="Software updates:"> 軟體更新: </text> @@ -46,4 +29,8 @@ <combo_box.item label="自動安è£" name="Install_automatically"/> <combo_box.item label="手動下載åŠå®‰è£" name="Install_manual"/> </combo_box> + <text name="Proxy Settings:"> + 代ç†ä¼ºæœå™¨è¨å®šï¼š + </text> + <button label="調整代ç†ä¼ºæœå™¨è¨å®š" label_selected="ç€è¦½" name="set_proxy"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_preferences_sound.xml b/indra/newview/skins/default/xui/zh/panel_preferences_sound.xml index 09dd4f6b0ba6bd2bacee4d2ad4ec51249a148005..e57f08fd74c3ff6c4b0f945197f8850442052f47 100644 --- a/indra/newview/skins/default/xui/zh/panel_preferences_sound.xml +++ b/indra/newview/skins/default/xui/zh/panel_preferences_sound.xml @@ -19,6 +19,7 @@ <check_box label="已啟用" name="enable_voice_check"/> <check_box label="å…è¨±åª’é«”è‡ªå‹•æ’æ”¾" name="media_auto_play_btn" tool_tip="è‹¥ä½ æƒ³è¦ï¼Œå¯ä»¥å‹¾é¸é€™å€‹å…è¨±åª’é«”è‡ªå‹•æ’æ”¾" value="true"/> <check_box label="æ’æ”¾é™„åŠ åˆ°å…¶ä»–åŒ–èº«èº«ä¸Šçš„åª’é«”" name="media_show_on_others_btn" tool_tip="若未勾é¸ï¼Œå°‡éš±è—附著於附近其他化身身上的媒體" value="true"/> + <check_box label="æ’æ”¾ä¾†è‡ªå§¿å‹¢çš„è²éŸ³" name="gesture_audio_play_btn" tool_tip="勾é¸å³å¯è½åˆ°ä¾†è‡ªå§¿å‹¢çš„è²éŸ³" value="true"/> <text name="voice_chat_settings"> 語音èŠå¤©è¨å®š </text> @@ -35,28 +36,5 @@ <button label="è¨å®šæŒ‰éµ" name="set_voice_hotkey_button"/> <button name="set_voice_middlemouse_button" tool_tip="é‡è¨æ»‘é¼ ä¸éµæŒ‰éˆ•"/> <button label="輸入 / 輸出è¨å‚™" name="device_settings_btn"/> - <panel label="è¨å‚™è¨å®š" name="device_settings_panel"> - <panel.string name="default_text"> - é è¨ - </panel.string> - <panel.string name="default system device"> - é è¨ç³»çµ±è¨å‚™ - </panel.string> - <panel.string name="no device"> - ç„¡è¨å‚™ - </panel.string> - <text name="Input"> - 輸入 - </text> - <text name="My volume label"> - 我的音é‡ï¼š - </text> - <slider_bar initial_value="1.0" name="mic_volume_slider" tool_tip="ç”¨é€™æŽ§åˆ¶æ¢æ”¹è®ŠéŸ³é‡"/> - <text name="wait_text"> - è«‹ç¨å€™ - </text> - <text name="Output"> - 輸出 - </text> - </panel> + <panel label="è¨å‚™è¨å®š" name="device_settings_panel"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_region_debug.xml b/indra/newview/skins/default/xui/zh/panel_region_debug.xml index 8639f3641099aa7d1dbcfc86c643c9d23853c312..e5d5e6eaf79e7cad29190b07b64273e9659d0132 100644 --- a/indra/newview/skins/default/xui/zh/panel_region_debug.xml +++ b/indra/newview/skins/default/xui/zh/panel_region_debug.xml @@ -30,5 +30,5 @@ <button label="å–得最常碰撞的物件..." name="top_colliders_btn" tool_tip="æ¢åˆ—出目å‰é‹ä½œä¸æœ€å¸¸ç¢°æ’žçš„物件清單"/> <button label="å–得最耗能腳本..." name="top_scripts_btn" tool_tip="æ¢åˆ—���目å‰é‹ä½œä¸æœ€è€—能的腳本清單"/> <button label="地å€é‡æ–°å•Ÿå‹•" name="restart_btn" tool_tip="給予兩分é˜å€’æ•¸è¨ˆæ™‚ä¸¦é‡æ–°å•Ÿå‹•"/> - <button label="å»¶é²é‡æ–°å•Ÿå‹•" name="cancel_restart_btn" tool_tip="å»¶é²åœ°å€é‡æ–°å•Ÿå‹•䏀尿™‚"/> + <button label="å–æ¶ˆé‡æ–°å•Ÿå‹•" name="cancel_restart_btn" tool_tip="å–æ¶ˆåœ°å€é‡æ–°å•Ÿå‹•"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_region_environment.xml b/indra/newview/skins/default/xui/zh/panel_region_environment.xml new file mode 100644 index 0000000000000000000000000000000000000000..8f466af39e485758fda8fbc869f928ed2ccb012d --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_region_environment.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel label="環境" name="panel_env_info"> + <text name="water_settings_title"> + 鏿“‡ä½ å¸Œæœ›åˆ°ä½ åœ°å€çš„訪客所能看到的水和天空 / 日循環è¨å®šã€‚ 詳情 + </text> + <radio_group name="region_settings_radio_group"> + <radio_item label="使用第二人生é è¨å€¼" name="use_sl_default_settings"/> + <radio_item label="使用以下è¨å®š" name="use_my_settings"/> + </radio_group> + <panel name="user_environment_settings"> + <text name="water_settings_title"> + æ°´çš„è¨å®š + </text> + <combo_box name="water_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + <text name="sky_dayc_settings_title"> + 天空 / 日循環 + </text> + <radio_group name="sky_dayc_settings_radio_group"> + <radio_item label="固定天空" name="my_sky_settings"/> + <radio_item label="日循環" name="my_dayc_settings"/> + </radio_group> + <combo_box name="sky_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + <combo_box name="dayc_settings_preset_combo"> + <combo_box.item label="-鏿“‡ä¸€å€‹è‡ªè¨‚é…ç½®-" name="item0"/> + </combo_box> + </panel> + <button label="套用" name="apply_btn"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_region_estate.xml b/indra/newview/skins/default/xui/zh/panel_region_estate.xml index 2d5dcfa9bba6312bcdc5cebd7f9ac44a3604da16..f3c1c85379eaad1939bf77a17310a132bec0d25e 100644 --- a/indra/newview/skins/default/xui/zh/panel_region_estate.xml +++ b/indra/newview/skins/default/xui/zh/panel_region_estate.xml @@ -20,10 +20,10 @@ <slider label="相ä½" name="sun_hour_slider"/> <check_box label="å…許公開出入" name="externally_visible_check"/> <text name="Only Allow"> - 僅å…許帳號通éŽå¦‚下驗è‰çš„人進出: + 僅å…許符åˆä»¥ä¸‹æ¢ä»¶çš„居民進入: </text> - <check_box label="é 留付款資料" name="limit_payment" tool_tip="ç¦æ¢èº«ä»½ä¸æ˜Žå±…æ°‘"/> - <check_box label="年齡驗è‰" name="limit_age_verified" tool_tip="ç¦æ¢å°šæœªé©—è‰å¹´é½¡çš„居民。 åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> + <check_box label="已經é 留付款資料" name="limit_payment" tool_tip="å±…æ°‘å¿…é ˆæä¾›ä»˜æ¬¾è³‡æ–™æ‰èƒ½é€²å…¥é€™é ˜åœ°ã€‚ åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> + <check_box label="已年滿 18 æ²" name="limit_age_verified" tool_tip="å±…æ°‘å¿…é ˆå¹´æ»¿ 18 æ²æ‰èƒ½é€²å…¥é€™é ˜åœ°ã€‚ åƒé–± [SUPPORT_SITE] ç²å–進一æ¥è³‡è¨Šã€‚"/> <check_box label="å…許語音èŠå¤©" name="voice_chat_check"/> <check_box label="å…許直接瞬間傳é€" name="allow_direct_teleport"/> <button label="套用" name="apply_btn"/> diff --git a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml index 30d12dfeb5476187412da4f9db3fc286d07a67a4..85e759e445f99d947c93cbc5772589712e8b55b0 100644 --- a/indra/newview/skins/default/xui/zh/panel_region_terrain.xml +++ b/indra/newview/skins/default/xui/zh/panel_region_terrain.xml @@ -9,11 +9,52 @@ <spinner label="水文高度" name="water_height_spin"/> <spinner label="地形æå‡é™åˆ¶" name="terrain_raise_spin"/> <spinner label="地形é™ä½Žé™åˆ¶" name="terrain_lower_spin"/> - <check_box label="ä½¿ç”¨é ˜åœ°çš„å¤ªé™½è¨å®š" name="use_estate_sun_check"/> - <check_box label="固定太陽" name="fixed_sun_check"/> - <slider label="相ä½" name="sun_hour_slider"/> - <button label="套用" name="apply_btn"/> + <text name="detail_texture_text"> + 地形æè³ªï¼ˆé ˆ 512x512,24 ä½å…ƒ .tga æª”æ ¼å¼ï¼‰ + </text> + <text name="height_text_lbl"> + 1(低) + </text> + <text name="height_text_lbl2"> + 2 + </text> + <text name="height_text_lbl3"> + 3 + </text> + <text name="height_text_lbl4"> + 4(高) + </text> + <text name="height_text_lbl5"> + æè³ªæµ·æ‹”ç¯„åœ + </text> + <text name="height_text_lbl10"> + 這些值代表以上æè³ªçš„æ··åˆç¯„åœã€‚ + </text> + <text name="height_text_lbl11"> + 以公尺為單ä½ï¼Œä½Žå€¼æ˜¯æè³ª #1 的最大高度,高值是æè³ª #4 的最å°é«˜åº¦ã€‚ + </text> + <text name="height_text_lbl6"> + 西北 + </text> + <text name="height_text_lbl7"> + æ±åŒ— + </text> + <spinner label="低" name="height_start_spin_1"/> + <spinner label="低" name="height_start_spin_3"/> + <spinner label="高" name="height_range_spin_1"/> + <spinner label="高" name="height_range_spin_3"/> + <text name="height_text_lbl8"> + è¥¿å— + </text> + <text name="height_text_lbl9"> + æ±å— + </text> + <spinner label="低" name="height_start_spin_0"/> + <spinner label="低" name="height_start_spin_2"/> + <spinner label="高" name="height_range_spin_0"/> + <spinner label="高" name="height_range_spin_2"/> <button label="下載 RAW 地形..." name="download_raw_btn" tool_tip="åªå…è¨±é ˜åœ°æ‰€æœ‰äººè€Œéžç®¡ç†è€…進行æ“作"/> <button label="上傳 RAW 地形檔..." name="upload_raw_btn" tool_tip="åªå…è¨±é ˜åœ°æ‰€æœ‰äººè€Œéžç®¡ç†è€…進行æ“作"/> <button label="確定地形" name="bake_terrain_btn" tool_tip="將目å‰åœ°å½¢è¨ç‚ºã€Œå‡é«˜ / é™ä½Žã€æ¥µé™çš„ä¸å¿ƒå€¼"/> + <button label="套用" name="apply_btn"/> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_script_ed.xml b/indra/newview/skins/default/xui/zh/panel_script_ed.xml index e59307fcfc8f8c42e901da40fdd20f0544cb9b56..29e9a35869f102e9248449a090b1441bf673118e 100644 --- a/indra/newview/skins/default/xui/zh/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/zh/panel_script_ed.xml @@ -22,6 +22,8 @@ <menu label="檔案" name="File"> <menu_item_call label="儲å˜" name="Save"/> <menu_item_call label="還原全部變更" name="Revert All Changes"/> + <menu_item_call label="從檔案載入…" name="LoadFromFile"/> + <menu_item_call label="å˜å…¥æª”案…" name="SaveToFile"/> </menu> <menu label="編輯" name="Edit"> <menu_item_call label="復原" name="Undo"/> diff --git a/indra/newview/skins/default/xui/zh/panel_script_question_toast.xml b/indra/newview/skins/default/xui/zh/panel_script_question_toast.xml new file mode 100644 index 0000000000000000000000000000000000000000..a2d0237da010e5c72549a283f6a34c7561e17eab --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_script_question_toast.xml @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<panel label="script_question_panel" name="panel_script_question_toast"> + <panel label="buttons_panel" name="buttons_panel"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_inventory.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_inventory.xml new file mode 100644 index 0000000000000000000000000000000000000000..20fb9b494a901fa9edfc30001d847306527a8922 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_snapshot_inventory.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_snapshot_inventory"> + <text name="title"> + 儲å˜åˆ°æˆ‘的收ç´å€ + </text> + <text name="hint_lbl"> + 將圖åƒå„²å˜åˆ°æ”¶ç´å€çš„費用為 L$[UPLOAD_COST]。 è‹¥è¦å°‡åœ–åƒå˜ç‚ºæè³ªï¼Œè«‹é¸æ“‡ä¸€å€‹æ£æ–¹æ ¼å¼ã€‚ + </text> + <combo_box label="è§£æžåº¦" name="texture_size_combo"> + <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> + <combo_box.item label="å°ï¼ˆ128x128)" name="Small(128x128)"/> + <combo_box.item label="ä¸ï¼ˆ256x256)" name="Medium(256x256)"/> + <combo_box.item label="大(512x512)" name="Large(512x512)"/> + <combo_box.item label="自訂" name="Custom"/> + </combo_box> + <spinner label="寬" name="inventory_snapshot_width"/> + <spinner label="高度" name="inventory_snapshot_height"/> + <check_box label="éŽ–ä½æ¯”例" name="inventory_keep_aspect_check"/> + <button label="å–æ¶ˆ" name="cancel_btn"/> + <button label="儲å˜" name="save_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_local.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_local.xml new file mode 100644 index 0000000000000000000000000000000000000000..a929c9a3fbc7641e5761af201668405dd94cb02f --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_snapshot_local.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_snapshot_local"> + <text name="title"> + 儲å˜åˆ°é›»è…¦ä¸Š + </text> + <combo_box label="è§£æžåº¦" name="local_size_combo"> + <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> + <combo_box.item label="320x240" name="320x240"/> + <combo_box.item label="640x480" name="640x480"/> + <combo_box.item label="800x600" name="800x600"/> + <combo_box.item label="1024x768" name="1024x768"/> + <combo_box.item label="1280x1024" name="1280x1024"/> + <combo_box.item label="1600x1200" name="1600x1200"/> + <combo_box.item label="自訂" name="Custom"/> + </combo_box> + <layout_stack name="local_image_params_ls"> + <layout_panel name="local_image_size_lp"> + <spinner label="寬" name="local_snapshot_width"/> + <spinner label="高度" name="local_snapshot_height"/> + <check_box label="éŽ–ä½æ¯”例" name="local_keep_aspect_check"/> + </layout_panel> + <layout_panel name="local_image_format_quality_lp"> + <combo_box label="æ ¼å¼" name="local_format_combo"> + <combo_box.item label="PNG(零失真)" name="PNG"/> + <combo_box.item label="JPEG" name="JPEG"/> + <combo_box.item label="BMP(零失真)" name="BMP"/> + </combo_box> + <slider label="圖åƒå“質" name="image_quality_slider"/> + <text name="image_quality_level"> + ([QLVL]) + </text> + </layout_panel> + </layout_stack> + <button label="å–æ¶ˆ" name="cancel_btn"/> + <flyout_button label="儲å˜" name="save_btn" tool_tip="儲å˜åœ–åƒåˆ°æª”案"> + <flyout_button.item label="儲å˜" name="save_item"/> + <flyout_button.item label="å¦å˜..." name="saveas_item"/> + </flyout_button> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml new file mode 100644 index 0000000000000000000000000000000000000000..82c2b10d8df878be98f223d92607ded0d6c8a381 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_snapshot_options.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_snapshot_options"> + <button label="é€è‡³æˆ‘的檔案訊æ¯ç™¼ä½ˆ" name="save_to_profile_btn"/> + <button label="電郵" name="save_to_email_btn"/> + <button label="儲å˜åˆ°æˆ‘的收ç´å€ï¼ˆL$[AMOUNT])" name="save_to_inventory_btn"/> + <button label="儲å˜åˆ°é›»è…¦ä¸Š" name="save_to_computer_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_postcard.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_postcard.xml new file mode 100644 index 0000000000000000000000000000000000000000..853a856104549363b63f808bfb040fef95c482b6 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_snapshot_postcard.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_snapshot_postcard"> + <string name="default_subject"> + 來自 [SECOND_LIFE] 的明信片。 + </string> + <string name="default_message"> + å¿«ä¾†çœ‹çœ‹é€™å€‹ï¼ + </string> + <string name="upload_message"> + 傳é€ä¸... + </string> + <text name="title"> + 電郵 + </text> + <button label="訊æ¯" name="message_btn"/> + <button label="è¨å®š" name="settings_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_snapshot_profile.xml b/indra/newview/skins/default/xui/zh/panel_snapshot_profile.xml new file mode 100644 index 0000000000000000000000000000000000000000..6f64a4e83c93f198d0d1087c7a8682a80cf60bfa --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_snapshot_profile.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="panel_snapshot_profile"> + <text name="title"> + é€è‡³æˆ‘的檔案訊æ¯ç™¼ä½ˆ + </text> + <combo_box label="è§£æžåº¦" name="profile_size_combo"> + <combo_box.item label="ç›®å‰è¦–窗" name="CurrentWindow"/> + <combo_box.item label="640x480" name="640x480"/> + <combo_box.item label="800x600" name="800x600"/> + <combo_box.item label="1024x768" name="1024x768"/> + <combo_box.item label="自訂" name="Custom"/> + </combo_box> + <layout_stack name="profile_image_params_ls"> + <layout_panel name="profile_image_size_lp"> + <spinner label="寬" name="profile_snapshot_width"/> + <spinner label="高度" name="profile_snapshot_height"/> + <check_box label="éŽ–ä½æ¯”例" name="profile_keep_aspect_check"/> + </layout_panel> + <layout_panel name="profile_image_metadata_lp"> + <text name="caption_label"> + 內容敘述: + </text> + <check_box initial_value="true" label="åŠ å…¥æ‰€åœ¨ä½ç½®" name="add_location_cb"/> + </layout_panel> + </layout_stack> + <button label="å–æ¶ˆ" name="cancel_btn"/> + <button label="發佈" name="post_btn"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/panel_sound_devices.xml b/indra/newview/skins/default/xui/zh/panel_sound_devices.xml index 96b00c323518ef106e7278c625f897dfedc8308d..fa4e24a6059d770fc8ca92ce19f2b603d6fc23e6 100644 --- a/indra/newview/skins/default/xui/zh/panel_sound_devices.xml +++ b/indra/newview/skins/default/xui/zh/panel_sound_devices.xml @@ -3,9 +3,18 @@ <panel.string name="default_text"> é è¨ </panel.string> + <string name="name_no_device"> + ç„¡è¨å‚™ + </string> + <string name="name_default_system_device"> + é è¨ç³»çµ±è¨å‚™ + </string> <text name="Input"> 輸入 </text> + <text name="Output"> + 輸出 + </text> <text name="My volume label"> 我的音é‡ï¼š </text> @@ -13,7 +22,4 @@ <text name="wait_text"> è«‹ç¨å€™ </text> - <text name="Output"> - 輸出 - </text> </panel> diff --git a/indra/newview/skins/default/xui/zh/panel_status_bar.xml b/indra/newview/skins/default/xui/zh/panel_status_bar.xml index ceebe943de5445084d4743fde7a03011a25d92d3..b4cdff9d6ba829f69c313cbf7db530aadc6d3ecb 100644 --- a/indra/newview/skins/default/xui/zh/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/zh/panel_status_bar.xml @@ -18,6 +18,7 @@ <panel name="balance_bg"> <text name="balance" tool_tip="é»žæŒ‰ä»¥é‡æ–°æ›´æ–°ä½ çš„ L$ 帳戶餘é¡" value="L$20"/> <button label="購買 L$" name="buyL" tool_tip="點按以購買更多 L$"/> + <button label="購物" name="goShop" tool_tip="打開第二人生購物市集"/> </panel> <text name="TimeText" tool_tip="ç›®å‰æ™‚å€ï¼ˆå¤ªå¹³æ´‹ï¼‰"> 24:00 AM PST diff --git a/indra/newview/skins/default/xui/zh/panel_volume_pulldown.xml b/indra/newview/skins/default/xui/zh/panel_volume_pulldown.xml new file mode 100644 index 0000000000000000000000000000000000000000..70ec028176728eec93324ed312ab9eac7bb9ad33 --- /dev/null +++ b/indra/newview/skins/default/xui/zh/panel_volume_pulldown.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes"?> +<panel name="volumepulldown_floater"> + <slider label="主控音é‡" name="System Volume"/> + <slider label="按éµéŸ³" name="UI Volume"/> + <slider label="環境" name="Wind Volume"/> + <slider label="è²éŸ³" name="SFX Volume"/> + <check_box name="gesture_audio_play_btn" tool_tip="啟用姿勢è²éŸ³"/> + <slider label="音樂" name="Music Volume"/> + <check_box name="enable_music" tool_tip="啟用串æµéŸ³æ¨‚"/> + <slider label="媒體" name="Media Volume"/> + <check_box name="enable_media" tool_tip="啟用串æµåª’é«”"/> + <slider label="語音" name="Voice Volume"/> + <check_box name="enable_voice_check" tool_tip="啟用語音èŠå¤©"/> +</panel> diff --git a/indra/newview/skins/default/xui/zh/sidepanel_inventory.xml b/indra/newview/skins/default/xui/zh/sidepanel_inventory.xml index 08d441a2f2af36e027f45ed49774874baeb4320e..c8aae1501118cc3a8334c6b42b3a882d02a0e78e 100644 --- a/indra/newview/skins/default/xui/zh/sidepanel_inventory.xml +++ b/indra/newview/skins/default/xui/zh/sidepanel_inventory.xml @@ -1,6 +1,27 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> <panel label="事物" name="objects panel"> <panel label="" name="sidepanel__inventory_panel"> + <layout_stack name="inventory_layout_stack"> + <layout_panel name="inbox_layout_panel"> + <panel label="" name="marketplace_inbox"> + <string name="InboxLabelWithArg"> + æ”¶åˆ°çš„ç‰©é … ([NUM]) + </string> + <string name="InboxLabelNoArg"> + æ”¶åˆ°çš„ç‰©é … + </string> + <button label="æ”¶åˆ°çš„ç‰©é …" name="inbox_btn"/> + <text name="inbox_fresh_new_count"> + [NUM] é …æ–°çš„ + </text> + <panel name="inbox_inventory_placeholder_panel" tool_tip="å°‡ç‰©é …æ‹–æ›³ç½®æ”¾åˆ°æ”¶ç´å€ï¼Œå³å¯é–‹å§‹ä½¿ç”¨"> + <text name="inbox_inventory_placeholder"> + å¾žç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚é›†è³¼å¾—ç‰©é …å°‡é€åˆ°é€™è£¡ã€‚ + </text> + </panel> + </panel> + </layout_panel> + </layout_stack> <panel name="button_panel"> <layout_stack name="button_panel_ls"> <layout_panel name="info_btn_lp"> diff --git a/indra/newview/skins/default/xui/zh/sidepanel_item_info.xml b/indra/newview/skins/default/xui/zh/sidepanel_item_info.xml index 949288a015ddd832f5745daae7a3f07b1aa155d1..1b093e0ecded2ae912e5b3e99f1b899cd12f4d92 100644 --- a/indra/newview/skins/default/xui/zh/sidepanel_item_info.xml +++ b/indra/newview/skins/default/xui/zh/sidepanel_item_info.xml @@ -3,6 +3,9 @@ <panel.string name="unknown"> (未知) </panel.string> + <panel.string name="unknown_multiple"> + (未知 / å¤šé …ï¼‰ + </panel.string> <panel.string name="public"> (公開) </panel.string> diff --git a/indra/newview/skins/default/xui/zh/sidepanel_task_info.xml b/indra/newview/skins/default/xui/zh/sidepanel_task_info.xml index 692dd81890d9ecea417fb89b78387508b9f78817..982dde40105d572eb35056556836693f753fad74 100644 --- a/indra/newview/skins/default/xui/zh/sidepanel_task_info.xml +++ b/indra/newview/skins/default/xui/zh/sidepanel_task_info.xml @@ -18,6 +18,12 @@ <panel.string name="text modify info 4"> ä½ ä¸èƒ½ä¿®æ”¹é€™äº›ç‰©ä»¶ </panel.string> + <panel.string name="text modify info 5"> + 無法跨地å€ä¿®æ”¹é€™å€‹ç‰©ä»¶ + </panel.string> + <panel.string name="text modify info 6"> + 無法跨地å€ä¿®æ”¹é€™äº›ç‰©ä»¶ + </panel.string> <panel.string name="text modify warning"> é€™å€‹ç‰©ä»¶å«æœ‰è¯çµçš„部分 </panel.string> @@ -95,6 +101,9 @@ </combo_box> <spinner label="åƒ¹æ ¼ï¼š L$" name="Edit Cost"/> <check_box label="顯示在æœå°‹ä¸" name="search_check" tool_tip="讓其他人å¯ä»¥åœ¨æœå°‹çµæžœä¸å¯Ÿçœ‹åˆ°æ¤ç‰©ä»¶"/> + <text name="pathfinding_attributes_label"> + 尋徑屬性: + </text> <text name="B:"> B: </text> diff --git a/indra/newview/skins/default/xui/zh/strings.xml b/indra/newview/skins/default/xui/zh/strings.xml index 9114cc079df6917877505b2eb6ec754f65bfda8d..2ca99e50d47b665ad24c0539366a29df625d1a12 100644 --- a/indra/newview/skins/default/xui/zh/strings.xml +++ b/indra/newview/skins/default/xui/zh/strings.xml @@ -29,11 +29,14 @@ å¿«å–æ¸…除ä¸... </string> <string name="StartupInitializingTextureCache"> - æè³ªå¿«å–åˆå§‹åŒ–ä¸... + æ£åœ¨åˆå§‹åŒ–æè³ªå¿«å–... </string> <string name="StartupInitializingVFS"> VFS åˆå§‹åŒ–ä¸... </string> + <string name="StartupRequireDriverUpdate"> + 顯åƒåˆå§‹åŒ–失敗。 è«‹æ›´æ–°ä½ çš„é¡¯åƒå¡é©…動程å¼ï¼ + </string> <string name="ProgressRestoring"> 回å˜ä¸... </string> @@ -74,10 +77,10 @@ é©—è‰å¿«å–檔案(約需 60-90 ç§’å·¦å³ï¼‰... </string> <string name="LoginProcessingResponse"> - 回應處ç†ä¸... + æ£åœ¨è™•ç†å›žæ‡‰... </string> <string name="LoginInitializingWorld"> - 世界åˆå§‹åŒ–ä¸... + æ£åœ¨åˆå§‹åŒ–虛擬世界… </string> <string name="LoginDecodingImages"> 圖åƒè§£ç¢¼ä¸... @@ -91,6 +94,12 @@ <string name="LoginQuicktimeOK"> QuickTime å·²æˆåŠŸåˆå§‹åŒ–。 </string> + <string name="LoginRequestSeedCapGrant"> + è©¢å•地å€è² è·åŠ›â€¦ + </string> + <string name="LoginRetrySeedCapGrant"> + è©¢å•地å€è² è·åŠ›ï¼Œç¬¬ [NUMBER] 次嘗試… + </string> <string name="LoginWaitingForRegionHandshake"> 地å€äº¤æ¡ç‰å¾…ä¸... </string> @@ -119,7 +128,7 @@ 無法檢驗通éŽç¶²æ ¼ä¼ºæœå™¨å‚³å›žçš„æ†‘è‰ç°½å。 è«‹è¯çµ¡ç¶²æ ¼ç®¡ç†å“¡ã€‚ </string> <string name="LoginFailedNoNetwork"> - ç¶²è·¯å‡ºéŒ¯ï¼šç„¡æ³•å»ºç«‹é€£ç·šï¼Œè«‹æª¢æŸ¥ç¶²è·¯é€£ç·šæ˜¯å¦æ£å¸¸ã€‚ + ç¶²è·¯éŒ¯èª¤ï¼šç„¡æ³•å»ºç«‹é€£ç·šï¼Œè«‹æª¢æŸ¥ç¶²è·¯é€£ç·šæ˜¯å¦æ£å¸¸ã€‚ </string> <string name="LoginFailed"> 登入失敗。 @@ -172,7 +181,7 @@ http://secondlife.com/viewer-access-faq </string> <string name="LoginFailedPremiumOnly"> ç¬¬äºŒäººç”Ÿæ¤æ™‚暫時é™åˆ¶ç™»å…¥ï¼Œä»¥ç¢ºä¿ä¸å½±éŸ¿æ•ˆèƒ½ï¼Œè®“ç›®å‰è™›æ“¬ä¸–ç•Œè£¡çš„ç”¨æˆ¶äº«å—æœ€ä½³çš„體驗。 - + å…è²»å¸³æˆ¶çš„ç”¨æˆ¶æ¤æ™‚æš«æ™‚ç„¡æ³•é€²å…¥ç¬¬äºŒäººç”Ÿï¼Œå› ç‚ºæˆ‘å€‘å¿…é ˆå„ªå…ˆå®¹ç´ä»˜è²»ç”¨æˆ¶ã€‚ </string> <string name="LoginFailedComputerProhibited"> @@ -325,6 +334,36 @@ http://secondlife.com/viewer-access-faq åªæœ‰ä¸€å€‹ç‰©å“å¯ä»¥è¢«æ‹–曳到æ¤è™• </string> <string name="TooltipPrice" value="L$[AMOUNT]:"/> + <string name="TooltipOutboxDragToWorld"> + å•†å®¶ç™¼ä»¶åŒ£å…§çš„ç‰©é …ç„¡æ³•ç”¢ç”Ÿåˆ°è™›æ“¬ä¸–ç•Œ + </string> + <string name="TooltipOutboxNoTransfer"> + 至少一個物件無法出售或轉移。 + </string> + <string name="TooltipOutboxNotInInventory"> + ä½ çš„å•†å®¶ç™¼ä»¶åŒ£åªèƒ½æŽ¥å—直接來自收ç´å€çš„ç‰©é …ã€‚ + </string> + <string name="TooltipOutboxWorn"> + ä½ ç©¿è‘—ä¸çš„ç‰©é …ç„¡æ³•æ”¾å…¥å•†å®¶ç™¼ä»¶åŒ£ã€‚ + </string> + <string name="TooltipOutboxCallingCard"> + å片ä¸å¾—放入商家發件匣 + </string> + <string name="TooltipOutboxFolderLevels"> + å·¢ç‹€è³‡æ–™å¤¾æ·±åº¦è¶…éŽ 3 + </string> + <string name="TooltipOutboxTooManyFolders"> + é ‚å±¤è³‡æ–™å¤¾çš„åè³‡æ–™å¤¾æ•¸ç›®è¶…éŽ 20 + </string> + <string name="TooltipOutboxTooManyObjects"> + é ‚å±¤è³‡æ–™å¤¾ç‰©é …æ•¸ç›®è¶…éŽ 200 + </string> + <string name="TooltipDragOntoOwnChild"> + 資料夾ä¸å¾—移到其å資料夾底下 + </string> + <string name="TooltipDragOntoSelf"> + 資料夾移動的目標ä¸å¾—為它本身 + </string> <string name="TooltipHttpUrl"> 點按以察看這個網é </string> @@ -788,6 +827,9 @@ http://secondlife.com/viewer-access-faq <string name="anim_yes_head"> 是 </string> + <string name="multiple_textures"> + 多個 + </string> <string name="texture_loading"> 載入ä¸... </string> @@ -836,6 +878,9 @@ http://secondlife.com/viewer-access-faq <string name="ScriptQuestionCautionChatDenied"> 物件「[OBJECTNAME]'ã€ï¼ˆæ‰€æœ‰äººã€Œ[OWNERNAME]ã€ï¼Œä½æ–¼ã€Œ[REGIONNAME]ã€ï¼Œæ–¹ä½ã€Œ[REGIONPOS]ã€ï¼‰å·²è¢«æ’¤é™¤ä¸‹åˆ—權é™ï¼š[PERMISSIONS]。 </string> + <string name="AdditionalPermissionsRequestHeader"> + ä½ å¦‚æžœæ‰“é–‹å¸³æˆ¶æ¬Šé™ï¼Œä¹Ÿå°‡ä¸€ä½µå…許該物件: + </string> <string name="ScriptTakeMoney"> ç”±ä½ èº«ä¸Šæ‹¿èµ°æž—ç™»å¹£ï¼ˆL$) </string> @@ -869,6 +914,9 @@ http://secondlife.com/viewer-access-faq <string name="ControlYourCamera"> æŽ§åˆ¶ä½ çš„æ”影機 </string> + <string name="TeleportYourAgent"> + 瞬間傳é€ä½ 本人 + </string> <string name="NotConnected"> æœªè¯æŽ¥ </string> @@ -947,6 +995,12 @@ http://secondlife.com/viewer-access-faq <string name="choose_the_directory"> 鏿“‡ç›®éŒ„ </string> + <string name="script_files"> + 腳本 + </string> + <string name="dictionary_files"> + å—å…¸ + </string> <string name="AvatarSetNotAway"> éžé›¢é–‹ </string> @@ -1184,6 +1238,65 @@ http://secondlife.com/viewer-access-faq <string name="InventoryNoTexture"> ä½ çš„æ”¶ç´å€è£¡æ²’有這個æè³ªçš„副本 </string> + <string name="InventoryInboxNoItems"> + ä½ å¾žç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚é›†è³¼è²·çš„ç‰©å“將出ç¾åœ¨é€™è£¡ã€‚ ä½ å¯ä»¥æŠŠå®ƒå€‘æ‹–æ›³åˆ°ä½ çš„æ”¶ç´å€ï¼Œé–‹å§‹ä½¿ç”¨ã€‚ + </string> + <string name="MarketplaceURL"> + https://marketplace.[MARKETPLACE_DOMAIN_NAME]/ + </string> + <string name="MarketplaceURL_CreateStore"> + http://community.secondlife.com/t5/English-Knowledge-Base/Selling-in-the-Marketplace/ta-p/700193#Section_.4 + </string> + <string name="MarketplaceURL_Dashboard"> + https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/dashboard + </string> + <string name="MarketplaceURL_Imports"> + https://marketplace.[MARKETPLACE_DOMAIN_NAME]/merchants/store/imports + </string> + <string name="MarketplaceURL_LearnMore"> + https://marketplace.[MARKETPLACE_DOMAIN_NAME]/learn_more + </string> + <string name="InventoryOutboxNotMerchantTitle"> + 任何人都å¯åœ¨ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集出售物å“。 + </string> + <string name="InventoryOutboxNotMerchantTooltip"/> + <string name="InventoryOutboxNotMerchant"> + å¦‚æžœä½ æƒ³æˆç‚ºå•†å®¶ï¼Œä½ 需è¦åœ¨ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集[[MARKETPLACE_CREATE_STORE_URL]é–‹è¨ä¸€é–“商店]。 + </string> + <string name="InventoryOutboxNoItemsTitle"> + ä½ çš„ç™¼ä»¶åŒ£ç›®å‰æ˜¯ç©ºçš„。 + </string> + <string name="InventoryOutboxNoItemsTooltip"/> + <string name="InventoryOutboxNoItems"> + 將資料夾拖曳到這個å€åŸŸï¼Œå†é»žæŒ‰ã€Œé€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集ã€ï¼Œå³å¯åœ¨[[MARKETPLACE_DASHBOARD_URL]第二人生購物市集]登列出售。 + </string> + <string name="Marketplace Error None"> + 零錯誤 + </string> + <string name="Marketplace Error Not Merchant"> + ç™¼ç”ŸéŒ¯èª¤ï¼šå°‡ç‰©é …é€å¾€ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚集之å‰ï¼Œä½ å¿…é ˆå–得商家的身份(å…費)。 + </string> + <string name="Marketplace Error Empty Folder"> + 錯誤:æ¤è³‡æ–™å¤¾æ²’有內容。 + </string> + <string name="Marketplace Error Unassociated Products"> + 錯誤:æ¤ç‰©é …ä¸Šå‚³å¤±æ•—ï¼Œå› ç‚ºä½ çš„å•†å®¶å¸³æˆ¶æœ‰å¤ªå¤šå’Œç”¢å“ç„¡é—œè¯çš„ç‰©é …ã€‚ è¦è§£æ±ºé€™å€‹å•é¡Œï¼Œè«‹ç™»å…¥ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚é›†ç¶²ç«™ï¼Œæ¸›ä½Žä½ çš„ç„¡é—œè¯ç‰©é …數目。 + </string> + <string name="Marketplace Error Object Limit"> + 錯誤:æ¤ç‰©é …包å«å¤ªå¤šç‰©ä»¶ã€‚ è¦è§£æ±ºé€™éŒ¯èª¤ï¼Œè«‹å°‡ç‰©ä»¶è£ç®±ï¼Œä½¿ç¸½ç‰©ä»¶æ•¸ç›®ä¸è¶…éŽ 200。 + </string> + <string name="Marketplace Error Folder Depth"> + 錯誤:æ¤ç‰©é …嫿œ‰å¤ªå¤šå±¤çš„巢狀資料夾。 è«‹åŠ ä»¥é‡æ–°æ•´ç†ï¼Œè‡³å¤šå…許 3 層的巢狀資料夾。 + </string> + <string name="Marketplace Error Unsellable Item"> + 錯誤:æ¤ç‰©é …無法在第二人生購物市集出售。 + </string> + <string name="Marketplace Error Internal Import"> + éŒ¯èª¤ï¼šé€™å€‹ç‰©é …æœ‰å•題。 è«‹ç¨å€™å†è©¦ä¸€æ¬¡ã€‚ + </string> + <string name="Open landmarks"> + 開啟地標 + </string> <string name="no_transfer" value="ï¼ˆç¦æ¢è½‰è®“)"/> <string name="no_modify" value="ï¼ˆç¦æ¢ä¿®æ”¹ï¼‰"/> <string name="no_copy" value="ï¼ˆç¦æ¢è¤‡è£½ï¼‰"/> @@ -1230,9 +1343,6 @@ http://secondlife.com/viewer-access-faq <string name="InvFolder My Inventory"> 我的收ç´å€ </string> - <string name="InvFolder My Favorites"> - 我的最愛 - </string> <string name="InvFolder Library"> 資æºåº« </string> @@ -1296,6 +1406,12 @@ http://secondlife.com/viewer-access-faq <string name="InvFolder favorite"> 我的最愛 </string> + <string name="InvFolder Favorites"> + 我的最愛 + </string> + <string name="InvFolder favorites"> + 我的最愛 + </string> <string name="InvFolder Current Outfit"> ç›®å‰è£æ‰® </string> @@ -1311,12 +1427,24 @@ http://secondlife.com/viewer-access-faq <string name="InvFolder Meshes"> ç¶²é¢ </string> + <string name="InvFolder Received Items"> + æ”¶åˆ°çš„ç‰©é … + </string> + <string name="InvFolder Merchant Outbox"> + 商家發件匣 + </string> <string name="InvFolder Friends"> æœ‹å‹ </string> <string name="InvFolder All"> 全部 </string> + <string name="no_attachments"> + 未穿著任何附件 + </string> + <string name="Attachments remain"> + 附件(尚å¯å®¹ç´ [COUNT] 件) + </string> <string name="Buy"> 購買 </string> @@ -1443,6 +1571,12 @@ http://secondlife.com/viewer-access-faq <string name="Right Pec"> å³èƒ¸è‚Œ </string> + <string name="Neck"> + é ¸éƒ¨ + </string> + <string name="Avatar Center"> + 化身ä¸å¿ƒ + </string> <string name="Invalid Attachment"> 無效的附接點 </string> @@ -1626,6 +1760,9 @@ http://secondlife.com/viewer-access-faq </string> <string name="SummaryForTheWeek" value="本週摘è¦ï¼Œèµ·å§‹æ—¥ï¼š"/> <string name="NextStipendDay" value="下一個發薪日為"/> + <string name="GroupPlanningDate"> + [mthnum,datetime,utc]/[day,datetime,utc]/[year,datetime,utc] + </string> <string name="GroupIndividualShare" value="群組 個人份é¡"/> <string name="GroupColumn" value="群組"/> <string name="Balance"> @@ -1652,6 +1789,9 @@ http://secondlife.com/viewer-access-faq <string name="IMTeen"> é’å°‘å¹´ </string> + <string name="Anyone"> + 任何人 + </string> <string name="RegionInfoError"> 錯誤 </string> @@ -1874,6 +2014,12 @@ http://secondlife.com/viewer-access-faq <string name="Public"> 公開 </string> + <string name="LocalSettings"> + 本地è¨å®š + </string> + <string name="RegionSettings"> + 地å€è¨å®š + </string> <string name="ClassifiedClicksTxt"> 點按:[TELEPORT] 瞬間傳é€ï¼Œ[MAP] 地圖,[PROFILE] 檔案 </string> @@ -1946,6 +2092,9 @@ http://secondlife.com/viewer-access-faq <string name="GroupMoneyDebits"> 借記 </string> + <string name="GroupMoneyDate"> + [weekday,datetime,utc] [mth,datetime,utc] [day,datetime,utc], [year,datetime,utc] + </string> <string name="ViewerObjectContents"> 內容 </string> @@ -3719,6 +3868,15 @@ http://secondlife.com/viewer-access-faq <string name="LocationCtrlGeneralIconTooltip"> ä¸€èˆ¬æ™®ç´šåœ°å€ </string> + <string name="LocationCtrlSeeAVsTooltip"> + å¯çœ‹åˆ°æœ¬åœ°æ®µå¤–的化身,並與之交談 + </string> + <string name="LocationCtrlPathfindingDirtyTooltip"> + 地å€é‡æ–°ç”¢å‡ºä¹‹å‰ï¼Œå¯ç§»å‹•物件å¯èƒ½ç„¡æ³•æ£å¸¸é‹ä½œã€‚ + </string> + <string name="LocationCtrlPathfindingDisabledTooltip"> + 這地å€ä¸¦æœªå•Ÿç”¨å‹•態尋徑。 + </string> <string name="UpdaterWindowTitle"> [APP_NAME] æ›´æ–° </string> @@ -3785,6 +3943,9 @@ http://secondlife.com/viewer-access-faq <string name="Saved_message"> (於 [LONG_TIMESTAMP] 儲å˜ï¼‰ </string> + <string name="IM_unblock_only_groups_friends"> + è¦å¯Ÿçœ‹é€™è¨Šæ¯ï¼Œä½ å¿…é ˆåˆ°ã€Œå好è¨å®š / éš±ç§ã€ï¼Œå–消勾é¸ã€Œåªæœ‰æˆ‘的朋å‹å’Œç¾¤çµ„å¯ä»¥ IM 或與我通話ã€ã€‚ + </string> <string name="answered_call"> ä½ çš„é€šè©±å·²ç¶“æŽ¥é€š </string> @@ -3917,6 +4078,18 @@ http://secondlife.com/viewer-access-faq <string name="you_paid_ldollars_no_name"> ä½ æ”¯ä»˜äº† L$[AMOUNT]([REASON])。 </string> + <string name="you_paid_failure_ldollars"> + ä½ æ”¯ä»˜ L$[AMOUNT] 給 [NAME] 時出錯:[REASON]。 + </string> + <string name="you_paid_failure_ldollars_no_info"> + ä½ æ”¯ä»˜ L$[AMOUNT] 時出錯。 + </string> + <string name="you_paid_failure_ldollars_no_reason"> + ä½ æ”¯ä»˜ L$[AMOUNT] 給 [NAME] 時出錯。 + </string> + <string name="you_paid_failure_ldollars_no_name"> + ä½ æ”¯ä»˜ L$[AMOUNT] 時出錯:[REASON]。 + </string> <string name="for item"> 購買 [ITEM] </string> @@ -3970,7 +4143,7 @@ http://secondlife.com/viewer-access-faq </string> <string name="uploading_abuse_report"> 上傳ä¸... - + é•è¦èˆ‰å ± </string> <string name="New Shape"> @@ -4144,6 +4317,87 @@ http://secondlife.com/viewer-access-faq <string name="Female - Wow"> 女性 - 哇塞 </string> + <string name="/bow"> + /彎腰點é + </string> + <string name="/clap"> + /æ‹æ‰‹ + </string> + <string name="/count"> + /計數 + </string> + <string name="/extinguish"> + /ç†„è¸ + </string> + <string name="/kmb"> + /çµ¦æˆ‘ä¸€å€‹å» + </string> + <string name="/muscle"> + /肌肉 + </string> + <string name="/no"> + /ä¸ + </string> + <string name="/no!"> + /ä¸ï¼ + </string> + <string name="/paper"> + /布 + </string> + <string name="/pointme"> + /指自己 + </string> + <string name="/pointyou"> + /指å‘ä½ + </string> + <string name="/rock"> + /石é + </string> + <string name="/scissor"> + /剪刀 + </string> + <string name="/smoke"> + /æŠ½è¸ + </string> + <string name="/stretch"> + /伸展 + </string> + <string name="/whistle"> + /å¹å£å“¨ + </string> + <string name="/yes"> + /是 + </string> + <string name="/yes!"> + /æ˜¯ï¼ + </string> + <string name="afk"> + 暫時離開 + </string> + <string name="dance1"> + 跳舞1 + </string> + <string name="dance2"> + 跳舞2 + </string> + <string name="dance3"> + 跳舞3 + </string> + <string name="dance4"> + 跳舞4 + </string> + <string name="dance5"> + 跳舞5 + </string> + <string name="dance6"> + 跳舞6 + </string> + <string name="dance7"> + 跳舞7 + </string> + <string name="dance8"> + 跳舞8 + </string> <string name="AvatarBirthDateFormat"> [mthnum,datetime,slt]/[day,datetime,slt]/[year,datetime,slt] </string> @@ -4157,7 +4411,7 @@ http://secondlife.com/viewer-access-faq <string name="server_is_down"> 儘管我們努力é¿å…ï¼Œé‚„æ˜¯ç™¼ç”Ÿæ„æ–™å¤–的錯誤。 - 請察訪 status.secondlifegrid.net 看是å¦ç™¼ç”Ÿäº†å·²çŸ¥ç‹€æ³ã€‚ + 請察訪 status.secondlifegrid.net 看是å¦ç™¼ç”Ÿäº†å·²çŸ¥ç‹€æ³ã€‚ å¦‚æžœæ–‡é«”ç¹¼çºŒç™¼ç”Ÿï¼Œè«‹æª¢æŸ¥ä½ çš„ç¶²è·¯å’Œé˜²ç«ç‰†è¨å®šã€‚ </string> <string name="dateTimeWeekdaysNames"> @@ -4246,6 +4500,12 @@ http://secondlife.com/viewer-access-faq <string name="ExternalEditorFailedToRun"> 執行外部編輯器失敗。 </string> + <string name="TranslationFailed"> + 無法翻è¯ï¼š[REASON] + </string> + <string name="TranslationResponseParseError"> + 無法剖æžå¹³ç§»å›žæ‡‰ã€‚ + </string> <string name="Esc"> Esc éµ </string> @@ -4588,4 +4848,223 @@ http://secondlife.com/viewer-access-faq <string name="ParticleHiding"> éš±è—ç²’åæ•ˆæžœ </string> + <string name="Command_AboutLand_Label"> + 土地資料 + </string> + <string name="Command_Appearance_Label"> + 編輯外觀 + </string> + <string name="Command_Avatar_Label"> + 化身 + </string> + <string name="Command_Build_Label"> + å»ºé€ + </string> + <string name="Command_Chat_Label"> + èŠå¤© + </string> + <string name="Command_Compass_Label"> + 羅盤 + </string> + <string name="Command_Destinations_Label"> + 目的地 + </string> + <string name="Command_Gestures_Label"> + å§¿å‹¢ + </string> + <string name="Command_HowTo_Label"> + ç°¡æ˜“æ•™å¸ + </string> + <string name="Command_Inventory_Label"> + æ”¶ç´å€ + </string> + <string name="Command_Map_Label"> + 地圖 + </string> + <string name="Command_Marketplace_Label"> + 第二人生購物市集 + </string> + <string name="Command_MiniMap_Label"> + è¿·ä½ åœ°åœ– + </string> + <string name="Command_Move_Label"> + 行走 / è·‘æ¥ / 飛行 + </string> + <string name="Command_Outbox_Label"> + 商家發件匣 + </string> + <string name="Command_People_Label"> + 人群 + </string> + <string name="Command_Picks_Label"> + ç²¾é¸åœ°é»ž + </string> + <string name="Command_Places_Label"> + 地點 + </string> + <string name="Command_Preferences_Label"> + å好è¨å®š + </string> + <string name="Command_Profile_Label"> + 檔案 + </string> + <string name="Command_Search_Label"> + æœå°‹ + </string> + <string name="Command_Snapshot_Label"> + å¿«ç…§ + </string> + <string name="Command_Speak_Label"> + 說話 + </string> + <string name="Command_View_Label"> + æ”影機控制 + </string> + <string name="Command_Voice_Label"> + 語音è¨å®š + </string> + <string name="Command_AboutLand_Tooltip"> + æœ‰é—œä½ æ‰€è™•åœŸåœ°çš„è³‡è¨Š + </string> + <string name="Command_Appearance_Tooltip"> + 改變化身 + </string> + <string name="Command_Avatar_Tooltip"> + 鏿“‡ä¸€å€‹å®Œæ•´çš„化身 + </string> + <string name="Command_Build_Tooltip"> + 建製物件和é‡å¡‘地形 + </string> + <string name="Command_Chat_Tooltip"> + é€éŽæ–‡å—和附近人們èŠå¤© + </string> + <string name="Command_Compass_Tooltip"> + 指å—é‡ + </string> + <string name="Command_Destinations_Tooltip"> + ä½ å¯èƒ½æ„Ÿèˆˆè¶£çš„目的地 + </string> + <string name="Command_Gestures_Tooltip"> + ä½ åŒ–èº«å¯ç”¨çš„å§¿å‹¢ + </string> + <string name="Command_HowTo_Tooltip"> + 如何完æˆå¸¸ç”¨çš„動作 + </string> + <string name="Command_Inventory_Tooltip"> + å¯Ÿçœ‹ä¸¦ä½¿ç”¨ä½ æ“æœ‰çš„物件 + </string> + <string name="Command_Map_Tooltip"> + 世界地圖 + </string> + <string name="Command_Marketplace_Tooltip"> + å‰å¾€è³¼ç‰© + </string> + <string name="Command_MiniMap_Tooltip"> + 顯示附近的人 + </string> + <string name="Command_Move_Tooltip"> + 移動化身 + </string> + <string name="Command_Outbox_Tooltip"> + å°‡ç‰©é …è½‰ç§»åˆ°ç¬¬äºŒäººç”Ÿè³¼ç‰©å¸‚é›†å¾…å”® + </string> + <string name="Command_People_Tooltip"> + 朋å‹ã€ç¾¤çµ„和附近的人 + </string> + <string name="Command_Picks_Tooltip"> + é¡¯ç¤ºåœ¨ä½ çš„å°æª”案ä¸çš„æœ€æ„›åœ°é»ž + </string> + <string name="Command_Places_Tooltip"> + ä½ å„²å˜çš„地點 + </string> + <string name="Command_Preferences_Tooltip"> + å好è¨å®š + </string> + <string name="Command_Profile_Tooltip"> + ç·¨è¼¯æˆ–å¯Ÿçœ‹ä½ çš„å°æª”案 + </string> + <string name="Command_Search_Tooltip"> + å°‹æ‰¾åœ°é»žã€æ´»å‹•ã€å…¶ä»–人 + </string> + <string name="Command_Snapshot_Tooltip"> + æ‹ä¸€å¼µç…§ç‰‡ + </string> + <string name="Command_Speak_Tooltip"> + 用麥克風和附近人們交談 + </string> + <string name="Command_View_Tooltip"> + 調整æ”影機角度 + </string> + <string name="Command_Voice_Tooltip"> + åœ¨è™›æ“¬ä¸–ç•Œè£¡é€šè©±å’Œé™„è¿‘äººç¾¤çš„éŸ³é‡æŽ§åˆ¶ + </string> + <string name="Toolbar_Bottom_Tooltip"> + ç›®å‰ä½åœ¨ä½ 的底部工具列 + </string> + <string name="Toolbar_Left_Tooltip"> + ç›®å‰ä½åœ¨ä½ 的左工具列 + </string> + <string name="Toolbar_Right_Tooltip"> + ç›®å‰ä½åœ¨ä½ çš„å³å·¥å…·åˆ— + </string> + <string name="Retain%"> + ä¿ç•™% + </string> + <string name="Detail"> + 細節 + </string> + <string name="Better Detail"> + 更多細節 + </string> + <string name="Surface"> + è¡¨é¢ + </string> + <string name="Solid"> + 固體 + </string> + <string name="Wrap"> + Wrap + </string> + <string name="Preview"> + é 覽 + </string> + <string name="Normal"> + æ£å¸¸ + </string> + <string name="Pathfinding_Wiki_URL"> + http://wiki.secondlife.com/wiki/Pathfinding_Tools_in_the_Second_Life_Viewer + </string> + <string name="Pathfinding_Object_Attr_None"> + ç„¡ + </string> + <string name="Pathfinding_Object_Attr_Permanent"> + å½±éŸ¿å°Žèˆªç¶²é¢ + </string> + <string name="Pathfinding_Object_Attr_Character"> + 角色 + </string> + <string name="Pathfinding_Object_Attr_MultiSelect"> + ï¼ˆå¤šé …ï¼‰ + </string> + <string name="snapshot_quality_very_low"> + 很低 + </string> + <string name="snapshot_quality_low"> + 低 + </string> + <string name="snapshot_quality_medium"> + ä¸ + </string> + <string name="snapshot_quality_high"> + 高 + </string> + <string name="snapshot_quality_very_high"> + 很高 + </string> + <string name="TeleportMaturityExceeded"> + æ¤å±…æ°‘ä¸å¾—進入æ¤åœ°å€ã€‚ + </string> + <string name="UserDictionary"> + [User] + </string> </strings> diff --git a/indra/newview/skins/default/xui/zh/teleport_strings.xml b/indra/newview/skins/default/xui/zh/teleport_strings.xml index b43497bbe6e29e28d8dd68f595c7d3d27d87ed86..37080a8d0c87e419de50a01d6fe067a13b995d08 100644 --- a/indra/newview/skins/default/xui/zh/teleport_strings.xml +++ b/indra/newview/skins/default/xui/zh/teleport_strings.xml @@ -21,8 +21,8 @@ è«‹ç¨å¾Œå†è©¦ã€‚ </message> <message name="NoHelpIslandTP"> - 您ä¸èƒ½çž¬é—´è½¬ç§»å›žâ€œæ´åЩ岛â€ã€‚ -去“公共æ´åЩ岛â€é‡å¤æ‚¨çš„æ•™ç¨‹ã€‚ + ä½ ç„¡æ³•çž¬é–“å‚³é€å›žã€Œæ–°æ‰‹å°Žå¼•å³¶ã€ã€‚ +請到「大眾新手導引島ã€é‡æ–°åƒåŠ å°Žå¼•æ•™å¸ã€‚ </message> <message name="noaccess_tport"> 抱æ‰ï¼Œä½ 並沒有權é™é€²å…¥è¦çž¬é–“傳é€çš„目的地。 @@ -45,6 +45,9 @@ <message name="no_inventory_host"> æ”¶ç´å€åŠŸèƒ½ç›®å‰ç„¡æ³•使用。 </message> + <message name="MustGetAgeRegion"> + ä½ å¿…é ˆå¹´æ»¿ 18 æ²æ‰å¯é€²å…¥é€™åœ°å€ã€‚ + </message> </message_set> <message_set name="progress"> <message name="sending_dest"> @@ -80,5 +83,8 @@ <message name="requesting"> 瞬間傳é€è¦æ±‚ä¸... </message> + <message name="pending"> + ç‰å¾…瞬間傳é€â€¦ + </message> </message_set> </teleport_messages> diff --git a/indra/newview/tests/llviewerassetstats_test.cpp b/indra/newview/tests/llviewerassetstats_test.cpp old mode 100755 new mode 100644 diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 99dcc90f8f2a59423fa38e9ee3735cc691ab42aa..8338a271402ad04d4781878a09b3af076982e105 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -28,6 +28,7 @@ $/LicenseInfo$ """ import sys import os.path +import errno import re import tarfile import time @@ -74,20 +75,20 @@ class ViewerManifest(LLManifest): # include the list of Lindens (if any) # see https://wiki.lindenlab.com/wiki/Generated_Linden_Credits linden_names_path = os.getenv("LINDEN_CREDITS") - if linden_names_path : + if not linden_names_path : + print "No 'LINDEN_CREDITS' specified in environment, using built-in list" + else: try: linden_file = open(linden_names_path,'r') + except IOError: + print "No Linden names found at '%s', using built-in list" % linden_names_path + else: # all names should be one line, but the join below also converts to a string linden_names = ', '.join(linden_file.readlines()) self.put_in_file(linden_names, "lindens.txt") linden_file.close() print "Linden names extracted from '%s'" % linden_names_path self.file_list.append([linden_names_path,self.dst_path_of("lindens.txt")]) - except IOError: - print "No Linden names found at '%s', using built-in list" % linden_names_path - pass - else : - print "No 'LINDEN_CREDITS' specified in environment, using built-in list" # ... and the entire windlight directory self.path("windlight") @@ -149,14 +150,9 @@ class ViewerManifest(LLManifest): self.path("gpu_table.txt") # The summary.json file gets left in the base checkout dir by - # build.sh. It's only created for a build.sh build, therefore we - # have to check whether it exists. :-P - summary_json = "summary.json" - summary_json_path = os.path.join(os.pardir, os.pardir, summary_json) - if os.path.exists(os.path.join(self.get_src_prefix(), summary_json_path)): - self.path(summary_json_path, summary_json) - else: - print "No %s" % os.path.join(self.get_src_prefix(), summary_json_path) + # build.sh. It's only created for a build.sh build. + if not self.path2basename(os.path.join(os.pardir, os.pardir), "summary.json"): + print "No summary.json file" def login_channel(self): """Channel reported for login and upgrade purposes ONLY; @@ -327,13 +323,13 @@ class WindowsManifest(ViewerManifest): self.path(src='%s/secondlife-bin.exe' % self.args['configuration'], dst=self.final_exe()) # Plugin host application - self.path(os.path.join(os.pardir, - 'llplugin', 'slplugin', self.args['configuration'], "slplugin.exe"), - "slplugin.exe") + self.path2basename(os.path.join(os.pardir, + 'llplugin', 'slplugin', self.args['configuration']), + "slplugin.exe") #self.disable_manifest_check() - self.path(src="../viewer_components/updater/scripts/windows/update_install.bat", dst="update_install.bat") + self.path2basename("../viewer_components/updater/scripts/windows", "update_install.bat") # Get shared libs from the shared libs staging directory if self.prefix(src=os.path.join(os.pardir, 'sharedlibs', self.args['configuration']), dst=""): @@ -367,9 +363,7 @@ class WindowsManifest(ViewerManifest): # Get fmod dll, continue if missing - try: - self.path("fmod.dll") - except: + if not self.path("fmod.dll"): print "Skipping fmod.dll" # For textures @@ -711,85 +705,82 @@ class DarwinManifest(ViewerManifest): self.path("uk.lproj") self.path("zh-Hans.lproj") - libdir = "../packages/lib/release" - dylibs = {} + def path_optional(src, dst): + """ + For a number of our self.path() calls, not only do we want + to deal with the absence of src, we also want to remember + which were present. Return either an empty list (absent) + or a list containing dst (present). Concatenate these + return values to get a list of all libs that are present. + """ + if self.path(src, dst): + return [dst] + print "Skipping %s" % dst + return [] - # Need to get the llcommon dll from any of the build directories as well - lib = "llcommon" - libfile = "lib%s.dylib" % lib - try: - self.path(self.find_existing_file(os.path.join(os.pardir, - lib, - self.args['configuration'], - libfile), - os.path.join(libdir, libfile)), - dst=libfile) - except RuntimeError: - print "Skipping %s" % libfile - dylibs[lib] = False - else: - dylibs[lib] = True - - if dylibs["llcommon"]: - for libfile in ("libapr-1.0.dylib", - "libaprutil-1.0.dylib", - "libexpat.1.5.2.dylib", - "libexception_handler.dylib", - "libGLOD.dylib", - "libcollada14dom.dylib" - ): - self.path(os.path.join(libdir, libfile), libfile) - - # SLVoice and vivox lols - for libfile in ('libsndfile.dylib', 'libvivoxoal.dylib', 'libortp.dylib', \ - 'libvivoxsdk.dylib', 'libvivoxplatform.dylib', 'SLVoice') : - self.path(os.path.join(libdir, libfile), libfile) + libdir = "../packages/lib/release" + # dylibs is a list of all the .dylib files we expect to need + # in our bundled sub-apps. For each of these we'll create a + # symlink from sub-app/Contents/Resources to the real .dylib. + # Need to get the llcommon dll from any of the build directories as well. + libfile = "libllcommon.dylib" + dylibs = path_optional(self.find_existing_file(os.path.join(os.pardir, + "llcommon", + self.args['configuration'], + libfile), + os.path.join(libdir, libfile)), + dst=libfile) + + for libfile in ( + "libapr-1.0.dylib", + "libaprutil-1.0.dylib", + "libcollada14dom.dylib", + "libexpat.1.5.2.dylib", + "libexception_handler.dylib", + "libGLOD.dylib", + ): + dylibs += path_optional(os.path.join(libdir, libfile), libfile) + + # SLVoice and vivox lols, no symlinks needed + for libfile in ( + 'libortp.dylib', + 'libsndfile.dylib', + 'libvivoxoal.dylib', + 'libvivoxsdk.dylib', + 'libvivoxplatform.dylib', + 'SLVoice', + ): + self.path2basename(libdir, libfile) - try: - # FMOD for sound - self.path(self.args['configuration'] + "/libfmodwrapper.dylib", "libfmodwrapper.dylib") - except: - print "Skipping FMOD - not found" + # FMOD for sound + libfile = "libfmodwrapper.dylib" + path_optional(os.path.join(self.args['configuration'], libfile), libfile) # our apps - self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app") - self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app") - - # plugin launcher - self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin.app", "SLPlugin.app") - - # our apps dependencies on shared libs - if dylibs["llcommon"]: - mac_crash_logger_res_path = self.dst_path_of("mac-crash-logger.app/Contents/Resources") - mac_updater_res_path = self.dst_path_of("mac-updater.app/Contents/Resources") - slplugin_res_path = self.dst_path_of("SLPlugin.app/Contents/Resources") - for libfile in ("libllcommon.dylib", - "libapr-1.0.dylib", - "libaprutil-1.0.dylib", - "libexpat.1.5.2.dylib", - "libexception_handler.dylib", - "libGLOD.dylib", - "libcollada14dom.dylib" - ): - target_lib = os.path.join('../../..', libfile) - self.run_command("ln -sf %(target)r %(link)r" % - {'target': target_lib, - 'link' : os.path.join(mac_crash_logger_res_path, libfile)} - ) - self.run_command("ln -sf %(target)r %(link)r" % - {'target': target_lib, - 'link' : os.path.join(mac_updater_res_path, libfile)} - ) - self.run_command("ln -sf %(target)r %(link)r" % - {'target': target_lib, - 'link' : os.path.join(slplugin_res_path, libfile)} - ) + for app_bld_dir, app in (("mac_crash_logger", "mac-crash-logger.app"), + ("mac_updater", "mac-updater.app"), + # plugin launcher + (os.path.join("llplugin", "slplugin"), "SLPlugin.app"), + ): + self.path2basename(os.path.join(os.pardir, + app_bld_dir, self.args['configuration']), + app) + + # our apps dependencies on shared libs + # for each app, for each dylib we collected in dylibs, + # create a symlink to the real copy of the dylib. + resource_path = self.dst_path_of(os.path.join(app, "Contents", "Resources")) + for libfile in dylibs: + symlinkf(os.path.join(os.pardir, os.pardir, os.pardir, libfile), + os.path.join(resource_path, libfile)) # plugins if self.prefix(src="", dst="llplugin"): - self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib") - self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib") - self.path("../packages/lib/release/libllqtwebkit.dylib", "libllqtwebkit.dylib") + self.path2basename("../media_plugins/quicktime/" + self.args['configuration'], + "media_plugin_quicktime.dylib") + self.path2basename("../media_plugins/webkit/" + self.args['configuration'], + "media_plugin_webkit.dylib") + self.path2basename("../packages/lib/release", "libllqtwebkit.dylib") self.end_prefix("llplugin") @@ -956,20 +947,25 @@ class LinuxManifest(ViewerManifest): self.path("client-readme-voice.txt","README-linux-voice.txt") self.path("client-readme-joystick.txt","README-linux-joystick.txt") self.path("wrapper.sh","secondlife") - self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh") - self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh") - self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh") - self.path("launch_url.sh","etc/launch_url.sh") + if self.prefix(src="", dst="etc"): + self.path("handle_secondlifeprotocol.sh") + self.path("register_secondlifeprotocol.sh") + self.path("refresh_desktop_app_entry.sh") + self.path("launch_url.sh") + self.end_prefix("etc") self.path("install.sh") self.end_prefix("linux_tools") # Create an appropriate gridargs.dat for this package, denoting required grid. self.put_in_file(self.flags_list(), 'etc/gridargs.dat') - self.path("secondlife-bin","bin/do-not-directly-run-secondlife-bin") - self.path("../linux_crash_logger/linux-crash-logger","bin/linux-crash-logger.bin") - self.path("../linux_updater/linux-updater", "bin/linux-updater.bin") - self.path("../llplugin/slplugin/SLPlugin", "bin/SLPlugin") + if self.prefix(src="", dst="bin"): + self.path("secondlife-bin","do-not-directly-run-secondlife-bin") + self.path("../linux_crash_logger/linux-crash-logger","linux-crash-logger.bin") + self.path("../linux_updater/linux-updater", "linux-updater.bin") + self.path2basename("../llplugin/slplugin", "SLPlugin") + self.path2basename("../viewer_components/updater/scripts/linux", "update_install") + self.end_prefix("bin") if self.prefix("res-sdl"): self.path("*") @@ -985,17 +981,13 @@ class LinuxManifest(ViewerManifest): self.end_prefix("res-sdl") self.end_prefix(icon_path) - self.path("../viewer_components/updater/scripts/linux/update_install", "bin/update_install") - # plugins if self.prefix(src="", dst="bin/llplugin"): - self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so") + self.path2basename("../media_plugins/webkit", "libmedia_plugin_webkit.so") self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_gstreamer.so") self.end_prefix("bin/llplugin") - try: - self.path("../llcommon/libllcommon.so", "lib/libllcommon.so") - except: + if not self.path("../llcommon/libllcommon.so", "lib/libllcommon.so"): print "Skipping llcommon.so (assuming llcommon was linked statically)" self.path("featuretable_linux.txt") @@ -1061,9 +1053,21 @@ class Linux_i686Manifest(LinuxManifest): super(Linux_i686Manifest, self).construct() if self.prefix("../packages/lib/release", dst="lib"): - self.path("libapr-1.so*") - self.path("libaprutil-1.so*") - self.path("libbreakpad_client.so*") + self.path("libapr-1.so") + self.path("libapr-1.so.0") + self.path("libapr-1.so.0.4.5") + self.path("libaprutil-1.so") + self.path("libaprutil-1.so.0") + self.path("libaprutil-1.so.0.4.1") + self.path("libboost_program_options-mt.so.1.48.0") + self.path("libboost_regex-mt.so.1.48.0") + self.path("libboost_thread-mt.so.1.48.0") + self.path("libboost_filesystem-mt.so.1.48.0") + self.path("libboost_signals-mt.so.1.48.0") + self.path("libboost_system-mt.so.1.48.0") + self.path("libbreakpad_client.so.0.0.0") + self.path("libbreakpad_client.so.0") + self.path("libbreakpad_client.so") self.path("libcollada14dom.so") self.path("libdb*.so") self.path("libcrypto.so.*") @@ -1079,11 +1083,8 @@ class Linux_i686Manifest(LinuxManifest): self.path("libopenjpeg.so*") self.path("libdirectfb-1.4.so.5") self.path("libfusion-1.4.so.5") - self.path("libdirect-1.4.so.5.0.4") - self.path("libdirect-1.4.so.5") - self.path("libhunspell-1.3.so") - self.path("libhunspell-1.3.so.0") - self.path("libhunspell-1.3.so.0.0.0") + self.path("libdirect-1.4.so.5*") + self.path("libhunspell-1.3.so*") self.path("libalut.so") self.path("libopenal.so", "libopenal.so.1") self.path("libopenal.so", "libvivoxoal.so.1") # vivox's sdk expects this soname @@ -1146,5 +1147,25 @@ class Linux_x86_64Manifest(LinuxManifest): ################################################################ +def symlinkf(src, dst): + """ + Like ln -sf, but uses os.symlink() instead of running ln. + """ + try: + os.symlink(src, dst) + except OSError, err: + if err.errno != errno.EEXIST: + raise + # We could just blithely attempt to remove and recreate the target + # file, but that strategy doesn't work so well if we don't have + # permissions to remove it. Check to see if it's already the + # symlink we want, which is the usual reason for EEXIST. + if not (os.path.islink(dst) and os.readlink(dst) == src): + # Here either dst isn't a symlink or it's the wrong symlink. + # Remove and recreate. Caller will just have to deal with any + # exceptions at this stage. + os.remove(dst) + os.symlink(src, dst) + if __name__ == "__main__": main() diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp index d480b63094745a1dea8d9593ce9a1f9529c9225d..bdcb068200768a17abbfc89c25d5d47fa80bb4de 100644 --- a/indra/viewer_components/login/lllogin.cpp +++ b/indra/viewer_components/login/lllogin.cpp @@ -271,6 +271,16 @@ void LLLogin::Impl::login_(LLCoros::self& self, std::string uri, LLSD login_para } return; // Done! } + + /* Sometimes we end with "Started" here. Slightly slow server? + * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below. + */ + if( status == "Started") + { + LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL; + continue; + } + // If we don't recognize status at all, trouble if (! (status == "CURLError" || status == "XMLRPCError"