Compare commits

...

249 Commits

Author SHA1 Message Date
UbitUmarov
18f652ee8d try to timeout httpclient reads 2026-02-27 16:15:10 +00:00
UbitUmarov
a99a3d2b3c split userprofiles request queues into local and HG, add console debug comand profiles status to check size of those queues
Some checks failed
.msbuildnet6 / build (push) Has been cancelled
2026-02-27 01:33:50 +00:00
UbitUmarov
492ac6a629 update CSJ2K 2026-02-26 19:56:34 +00:00
UbitUmarov
c725dae95a Ooops fix stun servers URIs. Thx MB 2026-02-26 17:20:13 +00:00
UbitUmarov
5fd022e33c more things on webrtc, reduce use of async/await 2026-02-26 14:53:45 +00:00
UbitUmarov
fc607035c8 add to opensim.ini a list of stun servers and send it to viewers in simulator features cap 2026-02-25 19:37:02 +00:00
UbitUmarov
044b5f69aa rename os webrtc ini file to *.example since only a few can test it, for now 2026-02-24 17:01:46 +00:00
UbitUmarov
b41033a8f1 do not fully trust viewers about parcels, plus cosmetics 2026-02-23 20:52:34 +00:00
UbitUmarov
b42e8dc0ad change webrtc module namespace by Robert request 2026-02-22 20:15:34 +00:00
UbitUmarov
adf5f1f6bd a few changes to webrtc module 2026-02-22 20:03:36 +00:00
UbitUmarov
674c3a0424 add Robert Adams os-webrtc-janus experimental module. License changed to same BSD as rest of OpenSimulator by Robert, for OpenSimulator use 2026-02-22 17:11:34 +00:00
UbitUmarov
eaed2f5b02 add llSetRenderMaterial (ugly code) 2026-02-22 11:51:58 +00:00
UbitUmarov
511c4c637c add some more checks nulls at profiles 2026-02-17 18:58:56 +00:00
UbitUmarov
8467ee34ff add lsl constant CLICK_ACTION_IGNORE 2026-01-31 16:14:09 +00:00
UbitUmarov
64cffc3389 cosmetics 2026-01-29 04:55:53 +00:00
UbitUmarov
4f8147696a fix setup of find parcel by fakeID at load 2026-01-29 04:55:21 +00:00
UbitUmarov
bd1e32546f cosmetics 2026-01-25 21:05:54 +00:00
UbitUmarov
25ad17d068 mantis 9230: flag need for prims inventory saves in more 9un0link cases 2026-01-24 21:40:00 +00:00
UbitUmarov
b9023bb3b6 cosmetics 2026-01-21 02:07:41 +00:00
UbitUmarov
86a879c9dd linux compiler gets confused... 2026-01-14 20:56:44 +00:00
UbitUmarov
4833652d9b let osNpcPlayAnimation play any animation from asset service, if a uuid is provided, instead of only default animations 2026-01-14 02:46:18 +00:00
UbitUmarov
aed3df84a7 try to improve wquivalent regions count for standalones case on grid stats. Thx Tampa 2026-01-13 19:07:43 +00:00
UbitUmarov
9132b1c982 avoid going out bounds on llInsertString. Thx Tampa 2026-01-13 02:39:43 +00:00
UbitUmarov
afd5b34b8a mode set tcp no delay inside try catch 2026-01-06 20:13:13 +00:00
UbitUmarov
0fbf5a44b3 cosmetics 2025-12-19 20:52:36 +00:00
UbitUmarov
d9d876fb5c avoid possible null refs in parseString2List. thx Tampa 2025-12-19 05:12:18 +00:00
UbitUmarov
d9ae06f338 add a socket null check 2025-12-10 00:28:32 +00:00
UbitUmarov
139f51e01b a few changes on ServiceURLs. Add some extra checks on AssetServiceURI 2025-11-29 22:49:07 +00:00
UbitUmarov
257d4dbe0a fix typo 2025-11-25 17:50:23 +00:00
UbitUmarov
c71e61dfd6 cosmetics 2025-11-25 17:45:06 +00:00
Vincent Sylvester
2db1fabd10 TIFF loader up to 32bit floats, cosmetics
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-11-25 16:43:22 +00:00
UbitUmarov
a0713398ba another change to pgsql 2025-11-23 00:43:51 +00:00
UbitUmarov
8714f59277 a few changes to pgsql, Thx Tampa, (untested, may be bad) 2025-11-23 00:35:17 +00:00
UbitUmarov
d29cbadbb6 caps still wrong 2025-11-17 13:10:55 +00:00
UbitUmarov
9df830a943 caps still wrong 2025-11-17 13:07:23 +00:00
UbitUmarov
5ece922c09 caps still wrong 2025-11-17 12:54:07 +00:00
UbitUmarov
3a8e172b68 add linux arm64 2025-11-17 12:48:18 +00:00
UbitUmarov
c793ad5415 fix sqlite config filename case 2025-11-17 12:30:41 +00:00
UbitUmarov
080ea9fbbf fix pointer to new native sqlite lib file on linux 2025-11-17 10:58:30 +00:00
UbitUmarov
0245886cb1 increment profile migration number on the one that did add the table usersettings, that had the typo 2025-11-13 16:16:05 +00:00
UbitUmarov
fe425a8284 cosmetics to kick github to compile the fix to the typo on sqlile 2025-11-13 04:07:18 +00:00
UbitUmarov
582f2a1fb6 fix a type on a sqlite migration, that was ignored on older version 2025-11-13 03:40:30 +00:00
UbitUmarov
1ee78e40dd cosmetics 2025-11-10 18:41:14 +00:00
UbitUmarov
67137bbacf cosmetics 2025-11-10 18:37:00 +00:00
UbitUmarov
19217833ce bad git.. missing files 2025-11-10 17:41:06 +00:00
UbitUmarov
9612ea2491 mantis 9219: update SQLite to System.Data.Sqlite 2.0.2 (native 3.50.4.5). Must run prebuild. This needs testing :( 2025-11-10 17:35:22 +00:00
UbitUmarov
dc4513ac33 remove mono.data.sqliteclient,dll obsolete since mono 1.2.4, Aparently only used in webstats. So we reduce obsolesce issuses to just Mono.Data.Sqlite, for now (needs testing, and do run prebuild) 2025-10-28 18:14:36 +00:00
UbitUmarov
c69eca6911 more on ReaderWriterLockSlim and thread.abort 2025-10-18 22:07:51 +01:00
UbitUmarov
d6c83aee6b simplify DoubleDictionaryThreadAbortSafe because dotnet no longer has thread.abort. Name is not wrong but keep it for now 2025-10-18 21:25:06 +01:00
UbitUmarov
ca11a13f97 apply by and the patch in mantis 9218 and change it. (Untested) 2025-10-06 00:06:24 +01:00
UbitUmarov
13ec0b9221 put back lost parentgroud null check, it is still needed on load code 2025-10-05 20:53:49 +01:00
UbitUmarov
7249b20796 irritating warnings 2025-10-05 03:37:32 +01:00
UbitUmarov
5590105e31 cosmetics 2025-10-05 03:31:55 +01:00
UbitUmarov
7c5ca30636 cosmetics 2025-10-05 03:28:05 +01:00
UbitUmarov
886fada445 cosmetics on YEngine 2025-10-05 03:20:44 +01:00
UbitUmarov
8b96e73938 cosmetics 2025-10-05 02:36:34 +01:00
UbitUmarov
41dda4407e avoid some unnecessary sog saves 2025-10-04 23:19:46 +01:00
UbitUmarov
f25f6d57c2 remove duplicated array bound check 2025-10-04 22:54:07 +01:00
UbitUmarov
02249bb0af reduce unnecessary saves of prim inventories 2025-09-23 22:54:30 +01:00
Vincent Sylvester
67e7dbc2e2 Cosmetics, unnecessary strings
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-09-16 18:43:22 +01:00
UbitUmarov
154637b244 patch from Licu Rau from Craft/World for PostgreSQL friends (untested :() 2025-09-16 18:37:58 +01:00
UbitUmarov
153a5e978a let llSetBuoyancy work on attachments. But unlike at sl, the effect is not removed when the script or its prim is removed/deattached. SO use with care. THis lmitation was a reason why we did not let is work on attachments 2025-09-05 19:22:12 +01:00
UbitUmarov
2950d07c86 cosmetics 2025-08-27 23:46:22 +01:00
UbitUmarov
0fc4291ffa cosmetics 2025-08-25 01:28:30 +01:00
UbitUmarov
1b81b995ef add a missing foldername urlencode on xinventory folder update 2025-08-25 00:12:55 +01:00
UbitUmarov
9dbaa4d3ac change api,yengine compile and serial version numbers (this will force scripts recompile and return to start state) 2025-08-21 22:27:47 +01:00
UbitUmarov
430c5ee9ce change defauld fov 2025-08-17 01:31:28 +01:00
UbitUmarov
15dc813fae some changes to llWorldPosToHUD. May need more (ofc it is opensim) 2025-08-17 01:23:44 +01:00
UbitUmarov
44c942788f simplify some comments about physics on ini files 2025-08-16 18:31:30 +01:00
UbitUmarov
392d324863 add llWorldPosToHUD 2025-08-16 04:31:17 +01:00
UbitUmarov
4ec6248dad add llIsLinkGLTFMaterial 2025-08-16 02:32:54 +01:00
UbitUmarov
0197ffc3ee add llGetRenderMaterial 2025-08-16 01:42:39 +01:00
UbitUmarov
988501e141 set ubODE as default physics engine 2025-08-15 17:09:17 +01:00
UbitUmarov
3b4f04107b cosmetics 2025-08-14 23:16:12 +01:00
UbitUmarov
6f5eb8c7b8 ok that was not the stub.. 2025-08-14 22:26:23 +01:00
UbitUmarov
f79e04d307 add stub for llMapBeacon 2025-08-14 22:24:31 +01:00
UbitUmarov
676d768ae7 mantis 9213: update libomv 2025-08-14 21:26:57 +01:00
UbitUmarov
fbdcf70c4a break err update libomv 2025-08-14 20:32:10 +01:00
UbitUmarov
3de5e6db14 take some future code out 2025-08-13 14:33:25 +01:00
UbitUmarov
6146233848 mantis 9212: a few changes to Lure and message transfer 2025-08-13 14:26:20 +01:00
UbitUmarov
6edc1f962b llMap* lookat does nothing 2025-08-13 12:30:07 +01:00
UbitUmarov
be8b208bf5 missing file 2025-08-12 23:48:13 +01:00
UbitUmarov
739e7a21a1 add llMapBeacon 2025-08-12 23:45:38 +01:00
UbitUmarov
7ac111627f some changes to llMapDestination 2025-08-12 22:26:46 +01:00
UbitUmarov
42994195b0 update libomv 2025-08-12 22:10:17 +01:00
UbitUmarov
5ef4abdf25 mantis 9211: change handling of event args on Yeng. Ofc needs some
testing
2025-08-05 19:02:17 +01:00
UbitUmarov
63bef17153 split exception detection on oshttp accept loop for better log 2025-08-05 17:50:02 +01:00
UbitUmarov
e3a50fb2f1 cosmetics 2025-08-02 17:33:26 +01:00
UbitUmarov
fce1666f04 change handling of event arguments (should only impact new compilations) 2025-08-02 17:31:43 +01:00
UbitUmarov
d504d1f679 remember heap used by locals 2025-08-02 17:28:35 +01:00
UbitUmarov
484ecad89e mantis 9209: another change 2025-07-31 00:43:35 +01:00
UbitUmarov
eea69a685c mantis 9209: change the decoding of face texture data on mesh upload 2025-07-30 23:21:40 +01:00
UbitUmarov
ad7cc123a4 and worng alpha value on inv prim check 2025-06-25 02:50:19 +01:00
UbitUmarov
ad17093ff4 oops wrong fix 2025-06-25 02:38:12 +01:00
UbitUmarov
27cd53112c bad merge 2025-06-25 02:32:13 +01:00
UbitUmarov
209ca3f861 use stream.copyto on meshcost mesh decoder 2025-06-25 02:24:55 +01:00
UbitUmarov
4ddd6b5706 do not try to draw invibleprims on map 2025-06-25 02:23:31 +01:00
UbitUmarov
0d71b6d871 very useless changes 2025-06-25 02:22:28 +01:00
UbitUmarov
f1d84b7866 use the changes on csj2k decoder on war3d 2025-06-24 16:59:23 +01:00
UbitUmarov
ca0b6be68e update libomv 2025-06-24 16:47:35 +01:00
UbitUmarov
86c22f9fc5 update butchered csj2k 2025-06-23 19:25:22 +01:00
UbitUmarov
427dc272c9 update libomv csj2k 2025-06-22 23:06:37 +01:00
UbitUmarov
e000f3ae55 update libomv csj2k with butchered 3.0 2025-06-22 15:33:12 +01:00
UbitUmarov
2c463ed47a update libomv again 2025-06-21 07:28:13 +01:00
UbitUmarov
9bf45c2122 update libomv 2025-06-21 06:18:19 +01:00
UbitUmarov
6f560f2133 bug fix on ubode mesh 2025-06-20 06:11:38 +01:00
UbitUmarov
2d6cb26fa2 update libomv 2025-06-20 04:57:47 +01:00
UbitUmarov
403158df06 merge fix 2025-06-20 04:48:58 +01:00
UbitUmarov
e1b5f3ad81 cosmetics 2025-06-20 04:38:51 +01:00
UbitUmarov
157de21332 cosmetics 2025-06-20 04:08:20 +01:00
UbitUmarov
9cd70dc401 cosmetics 2025-06-20 03:49:39 +01:00
UbitUmarov
4b1df46f6a make more use of stream.CopyTo on (de)compressors, plus several cosmetics 2025-06-19 20:11:42 +01:00
UbitUmarov
6180fe5340 warp3d map: avoid decode mesh LODs we do not need (using new code on our primmesher) 2025-06-19 18:01:32 +01:00
UbitUmarov
ad98f567c2 fix bad merge 2025-06-19 17:54:12 +01:00
UbitUmarov
4e822198b3 Update libomv, that now includes our own fork of PrimMesher.dll 2025-06-19 17:51:18 +01:00
UbitUmarov
7cf0f73cef on ubodeMesher no need for Coord and Quat. Those are just Vector3 and Quaternion 2025-06-19 17:48:14 +01:00
UbitUmarov
628ddc1821 more agreesing inline 2025-06-07 01:10:10 +01:00
UbitUmarov
f20e3818af a few changes on udp zero encoder and part text color 2025-06-06 23:27:51 +01:00
UbitUmarov
1dd9393fff Yengine use internal shared empty arrays 2025-06-05 11:57:22 +01:00
UbitUmarov
71a8008024 minor typo 2025-06-04 23:57:33 +01:00
UbitUmarov
e0006fb17d stop old useless bytes to float and back to bytes 2025-06-04 23:41:26 +01:00
UbitUmarov
74c64e199d mantis 9205: fix text alpha for lsl 2025-06-04 22:37:47 +01:00
UbitUmarov
85a927da28 fix YEngine HasScript() 2025-05-27 10:35:11 +01:00
UbitUmarov
41d1fdb6c6 fix a old typo on mesh upload number of sides 2025-05-27 09:46:39 +01:00
UbitUmarov
25641e95a7 remove a spurius space on viewer name 2025-05-19 01:57:46 +01:00
UbitUmarov
dacc943a86 fix bad merge 2025-05-14 03:14:59 +01:00
UbitUmarov
71289be01e cosmetics on assets connector 2025-05-14 03:10:03 +01:00
UbitUmarov
f4ab3855d8 change log message since most assets will not have a name 2025-05-14 02:39:50 +01:00
UbitUmarov
a800514050 add missing oar load help text; verify asset fullid on save to oar 2025-05-13 22:30:52 +01:00
UbitUmarov
24352a8c8d clenaup some XEngine refs from ini 2025-05-10 21:59:26 +01:00
UbitUmarov
05969fcb62 several changes; no log at that code level. (untested :( ) 2025-05-10 17:21:09 +01:00
Vincent Sylvester
39009fcfcf Improve mesh upload feedback
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-05-10 16:19:48 +01:00
UbitUmarov
0fa1d54d5b typo 2025-05-09 21:28:47 +01:00
UbitUmarov
d92940b62b add functions osListAs*(list src, integer index) identical to llList2*, but a bit faster with no typecast and index only from origin ( ie >= 0) 2025-05-09 20:00:19 +01:00
UbitUmarov
ef2fce034f a few , mostly cosmetic, changes to osSetTerrainTextures and add prim inventory osSetTerrainTextures with a list of possible asset types to match 2025-05-06 23:40:40 +01:00
UbitUmarov
23ad195e7a add osSetTerrainTextures(LSL_List textures, LSL_Integer types) to set terrain textures for legacy viewers it types == 0 or 2; textures for new viewers if types == 1 or 2 or PBR materials if types == 1 (untested :( ) 2025-05-06 01:29:24 +01:00
UbitUmarov
c9b4d3374f mantis 9188: add HG assets scan and fetch when droping a item on a prim 2025-05-05 21:28:12 +01:00
UbitUmarov
4dae4e7a4f cosmetics on useraccountservice 2025-05-04 22:12:30 +01:00
UbitUmarov
8ec2715f35 misisng bounds check on terrain changes limiter 2025-05-04 21:25:09 +01:00
UbitUmarov
7a4c6ff5b3 add another ossl tooltip 2025-05-01 19:42:53 +01:00
UbitUmarov
71fd5c5dea minor file formating fix 2025-05-01 19:32:06 +01:00
UbitUmarov
d1f8d00912 update scripts syntax file 2025-05-01 19:30:52 +01:00
UbitUmarov
c69ed37dd8 fix to compile 2025-05-01 19:20:28 +01:00
Vincent Sylvester
1847b6be24 Tooltips for ossl
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-05-01 18:54:57 +01:00
UbitUmarov
09ef908034 just a test of github.. 2025-04-30 23:38:12 +01:00
UbitUmarov
cce239d051 some code cosmetics on UserAccountService 2025-04-30 22:51:03 +01:00
UbitUmarov
4918f91d41 fix last change to support empty string group titles 2025-04-30 20:03:06 +01:00
UbitUmarov
83d3ce8a1a work around some osgrid (mb others) null group titles 2025-04-30 17:35:39 +01:00
UbitUmarov
93c81360c0 a few more entries to gitattributes 2025-04-23 03:01:47 +01:00
UbitUmarov
f669c56bb9 mantis 9197: on llHttprequest report status = 0 as 499 2025-04-22 22:10:20 +01:00
UbitUmarov
354966cda9 remove broken libsqlite3.dylib, let macos find its own for now 2025-04-22 19:34:22 +01:00
UbitUmarov
78228bd4bc mk sure we do save appearence 2025-04-20 23:54:04 +01:00
UbitUmarov
8b962b19db remove attachments from current appearence if they are not found in inventory 2025-04-20 23:52:12 +01:00
UbitUmarov
bc59e3a3e7 update script syntax 2025-04-19 20:46:27 +01:00
UbitUmarov
147d90b258 ok no static.. 2025-04-17 20:34:43 +01:00
UbitUmarov
de749dd6ed minor change 2025-04-17 20:24:47 +01:00
Vincent Sylvester
28266fc7b6 HMAC function, stubs and interfaces for compute hash
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-17 20:20:28 +01:00
UbitUmarov
70eafec6fa fix compile since HMAC_SHA224() is missing 2025-04-17 19:46:05 +01:00
Vincent Sylvester
f6227d26eb Fix llHMAC and ComputeHash
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-17 19:10:44 +01:00
UbitUmarov
dc3d44a458 actually write a hex string in caps 2025-04-17 15:53:23 +01:00
UbitUmarov
b00df07563 do not allow estate bans to grid admins 2025-04-17 15:50:08 +01:00
UbitUmarov
98703ff3a2 do scan terrain pbr materials for referenced assets on oar save 2025-04-13 15:11:52 +01:00
UbitUmarov
4b5d9c0696 ooops 2025-04-12 04:10:05 +01:00
UbitUmarov
47e9a13e2b ugly cosmetics 2025-04-11 20:49:05 +01:00
UbitUmarov
d76f0437cd ugly cosmetics 2025-04-11 20:19:49 +01:00
UbitUmarov
748d7d08db a few more chances also the the ossl version 2025-04-11 18:57:42 +01:00
UbitUmarov
a068bd3da6 oops 2025-04-11 18:36:49 +01:00
UbitUmarov
2ebbcdcfff change llListFindListNext negative instance case 2025-04-11 18:26:30 +01:00
UbitUmarov
b5d1d39829 a few fixes... 2025-04-11 16:03:14 +01:00
Vincent Sylvester
d3cf429c4c llGetVisualParams
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 15:28:40 +01:00
UbitUmarov
16665227ce fix lllistfindnext for some cases 2025-04-11 15:22:28 +01:00
Vincent Sylvester
587b19c76b llListFindListNext
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 14:56:54 +01:00
UbitUmarov
d12418ed8f cosmetics, note that sha224 is not correct 2025-04-11 09:21:29 +01:00
UbitUmarov
7d47398802 bad merge.. 2025-04-11 08:50:25 +01:00
Vincent Sylvester
d9180d3a59 llHMAC
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 08:45:15 +01:00
UbitUmarov
ac4ae13ceb use switch expression instead (the changes..) 2025-04-11 08:11:03 +01:00
Vincent Sylvester
ae4ea576d5 llGetHealth
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 08:08:06 +01:00
UbitUmarov
4d0312f122 use switch expression instead 2025-04-11 08:03:37 +01:00
Vincent Sylvester
6eab3e6efb llGetSimStats
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 07:51:12 +01:00
Vincent Sylvester
f42fd1c552 llGetInventoryDesc
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-04-11 07:25:06 +01:00
UbitUmarov
9a92d94e99 remove option ClampNegativeZ 2025-04-11 05:43:15 +01:00
UbitUmarov
f2379dc785 mantis 9133 replace some z < 0 checks by < Constants.MinSimulationHeight (-100) 2025-04-11 05:05:42 +01:00
UbitUmarov
e862eab358 allow negative instance to also index results from end (bad) on osListFindListNext. Untested, sorry 2025-04-04 11:13:11 +01:00
UbitUmarov
006566f35c fix bad merge 2025-04-04 01:44:36 +01:00
UbitUmarov
bde18322eb add LSL_Integer osListFindListNext(LSL_List src, LSL_List test, LSL_Integer lstart, LSL_Integer lend, LSL_Integer instance), like ll one but with search restricted to a substring. Untested, sorry 2025-04-04 01:30:41 +01:00
UbitUmarov
9a02b55bf2 a few more changes on terrain 2025-03-29 23:29:42 +00:00
Ubit Umarov
207a318f86 Merge pull request #27 from Tampa/SmoothArea-fix
Fix SmoothArea to be uniform again
2025-03-29 11:51:06 +00:00
Tampa
b1a2773de9 Fix SmoothArea to be uniform again 2025-03-29 08:34:45 +01:00
UbitUmarov
e5d10bf127 try to fix some terrain issues, Add some more locking 2025-03-29 01:21:43 +00:00
UbitUmarov
d346a7aea7 mantis 9187: only apply setcontentype restrictions to type html 2025-03-25 04:27:53 +00:00
UbitUmarov
26a8c5e712 fix grid stats configuraton check 2025-03-18 22:48:04 +00:00
UbitUmarov
238e714253 update sqlite .config files 2025-03-18 20:59:08 +00:00
UbitUmarov
d48a3d432f mantis 9185: Update libsqlite3 for macOS with version 3.7.5 ( that old for compatibility ) 2025-03-18 20:51:56 +00:00
UbitUmarov
11cb8b6ed0 cosmitics; allow to wear 3 animesh objects 2025-03-16 21:31:53 +00:00
UbitUmarov
c8b88141cb change picks update/add, enforcing max number of picks 2025-03-14 04:38:03 +00:00
UbitUmarov
b63acc8090 send the MaxProfilePicks in simulatorFeatures 2025-03-14 02:20:12 +00:00
UbitUmarov
75e9a49072 only send up top 20 pre sorted profile picks to viewers 2025-03-14 02:08:11 +00:00
UbitUmarov
1855be20fa place some limits on some profile pick fields 2025-03-14 00:25:21 +00:00
UbitUmarov
65eee21661 change avatar animator on forced hover, needs more testing 2025-03-12 18:06:31 +00:00
UbitUmarov
f3c348cf40 change avatar hover avoid some vodoo, but still needs more work on animation state 2025-03-12 03:27:54 +00:00
UbitUmarov
d909aea684 change avatar hover, if target is below ground, move avatar close to ground, let z control have a little action 2025-03-12 02:29:35 +00:00
UbitUmarov
3cb2732855 mantis 9184: refix avatar hover 2025-03-12 00:50:28 +00:00
UbitUmarov
8ec10f9ca8 remove the new xmlrpc grid stats. xmlrcp is obsolete; change refresh time to 15minutes; cosmetics 2025-02-18 12:37:25 +00:00
Vincent Sylvester
898cc22b22 Grid Stats as part of GridInfoService
Signed-off-by: UbitUmarov <ajlduarte@sapo.pt>
2025-02-18 11:36:06 +00:00
UbitUmarov
4516bee2aa take code for old avination service that was never donated out of main path 2025-02-15 20:40:41 +00:00
UbitUmarov
6b73f2157e mantis 9180: try to load imediate subfolders of My outfits with type outfit 2025-02-15 20:39:06 +00:00
UbitUmarov
adcba0667f save parcels media textures on oar 2025-02-11 20:40:51 +00:00
UbitUmarov
ff479b94cd say missing item inv type if known 2025-02-11 01:34:03 +00:00
UbitUmarov
b67254e57d add log of the skiped items 2025-02-11 01:21:26 +00:00
UbitUmarov
af75c37543 add option --skipbadassets to iar save. THis will skip inventory items with missing or empty main asset. Avoid using unless on try to recover from already damaged assets/inventory 2025-02-11 01:00:35 +00:00
UbitUmarov
a6bb6b08f6 missing files 2025-02-11 00:57:04 +00:00
UbitUmarov
b13c4c2055 add option --force-assets to load oar. this will force replace of assets in region cache (will also try to upload but service will most likely refuse). avoid using unless trying to do some recovery 2025-02-10 22:52:53 +00:00
UbitUmarov
46f2fb575e add some missing parcel media data to oars, Thx Tampa 2025-02-08 17:47:19 +00:00
UbitUmarov
e681e1f09a add llGetLinkSitFlags and dummy llSetLinkSitFlags 2025-02-07 22:04:01 +00:00
UbitUmarov
90c367e630 make linknumbers 0 and 1 mean RootPart by default. Since we do not consider sitting avatars on root linknumber, we will never be full compatible with sl variations 2025-02-07 20:44:57 +00:00
UbitUmarov
85b0b89992 cosmetics 2025-02-07 04:07:20 +00:00
UbitUmarov
74b29f1fef or not... grrr (thx Tampa) 2025-02-07 03:37:25 +00:00
UbitUmarov
ff95c7d798 maybe now? 2025-02-07 01:52:27 +00:00
UbitUmarov
c5d97c8a7c still bad 2025-02-07 01:25:32 +00:00
UbitUmarov
ab91a071f2 fix ProcessParcelAccessList 2025-02-07 00:52:38 +00:00
UbitUmarov
8af735d050 more cosmetics 2025-02-05 22:16:52 +00:00
UbitUmarov
38ba697b3a more cosmetics 2025-02-05 21:22:40 +00:00
UbitUmarov
c3cf7f3eb2 cosmetics on xinventory 2025-02-04 22:02:21 +00:00
UbitUmarov
6934eedfdc add folder Materials to new users inventory 2025-02-04 19:39:06 +00:00
UbitUmarov
50ce311ac2 fix Object bonus decode as Real, thx Tampa 2025-01-05 01:35:06 +00:00
UbitUmarov
d9e4389749 add flotsam comand fcache clearnegatives 2024-12-24 00:25:10 +00:00
UbitUmarov
4088b5d165 cosmetics 2024-12-22 23:29:34 +00:00
UbitUmarov
1edd874055 fix typo 2024-12-22 01:22:48 +00:00
UbitUmarov
a3a64c3a68 get PID from Environment.ProcessId 2024-11-30 21:11:03 +00:00
UbitUmarov
e1dd994f2d fix rez distance check 2024-11-30 19:52:29 +00:00
UbitUmarov
49a4285180 fix vel parameter on rez obj witrh params 2024-11-30 19:14:48 +00:00
UbitUmarov
5aa37e74eb minor changes on llgettime 2024-11-29 21:23:10 +00:00
UbitUmarov
4b993d2379 change ubode avatar outbounds park position 2024-11-29 21:18:57 +00:00
UbitUmarov
b1f5734d90 fix RezzerID 2024-11-27 18:51:10 +00:00
UbitUmarov
ecf2e0e32e clear start string parameter on other rez cases 2024-11-26 21:06:56 +00:00
UbitUmarov
606a70b268 revert some test code, not supposed to be in use 2024-11-26 19:57:47 +00:00
UbitUmarov
901c157f8c missing sql code plus cosmetics 2024-11-26 19:45:54 +00:00
UbitUmarov
cd6efb7553 persist start string parameter (from rezobject) 2024-11-26 18:34:26 +00:00
UbitUmarov
cb1c8000a1 missing file 2024-11-22 04:02:41 +00:00
UbitUmarov
a20a5f312d add llGetStartString and limited llRezObjectWithParams for testing 2024-11-22 04:01:03 +00:00
UbitUmarov
2ad93b5ad9 be more restrict when sending object contents asset ids, like when ower also should not see 2024-11-20 01:51:12 +00:00
UbitUmarov
6d8bbde5c2 change UI god cancelation on arrival decision code a bit 2024-11-19 23:13:50 +00:00
UbitUmarov
d5866f4e02 do tell viewers about UI god cancelation on arrival 2024-11-19 23:05:59 +00:00
UbitUmarov
1ff28960f1 cosmetics 2024-11-19 04:25:32 +00:00
UbitUmarov
95ef45ba7c cosmetics 2024-11-18 03:00:26 +00:00
UbitUmarov
2d3140fc9a oops let loop go on. Thanks Tampa 2024-11-17 00:01:55 +00:00
UbitUmarov
df4664f05b avoid possible null ref 2024-11-16 00:05:47 +00:00
UbitUmarov
c3ea01144e few lsl constants 2024-11-14 22:30:13 +00:00
UbitUmarov
0a0e1ea0b1 prim inv item creation date is date of adding to prim 2024-11-14 22:29:32 +00:00
277 changed files with 14969 additions and 6210 deletions

3
.gitattributes vendored
View File

@@ -18,3 +18,6 @@
*.ogg binary
*.dll binary
*.exe binary
*.so binary
*.dylib binary
*.anim binary

View File

@@ -1582,6 +1582,5 @@ namespace OpenSim.Groups
{
return client is null ? UUID.Zero : client.AgentId;
}
}
}

View File

@@ -1058,13 +1058,24 @@ namespace OpenSim.Groups
private bool HasPower(string agentID, UUID groupID, GroupPowers power)
{
RoleMembershipData[] rmembership = m_Database.RetrieveMemberRoles(groupID, agentID);
if (rmembership == null || (rmembership != null && rmembership.Length == 0))
if (rmembership is null || rmembership.Length == 0)
return false;
foreach (RoleMembershipData rdata in rmembership)
{
if(rdata is null)
{
m_log.Warn($"[GROUPSERVICE] null membership data entry in group {groupID} for agent {agentID}");
continue;
}
RoleData role = m_Database.RetrieveRole(groupID, rdata.RoleID);
if ( (UInt64.Parse(role.Data["Powers"]) & (ulong)power) != 0 )
if (role is null)
{
m_log.Warn($"[GROUPSERVICE] role with id {rdata.RoleID} is null");
continue;
}
if ((UInt64.Parse(role.Data["Powers"]) & (ulong)power) != 0)
return true;
}
return false;
@@ -1073,14 +1084,14 @@ namespace OpenSim.Groups
private bool IsOwner(string agentID, UUID groupID)
{
GroupData group = m_Database.RetrieveGroup(groupID);
if (group == null)
if (group is null)
return false;
RoleMembershipData rmembership = m_Database.RetrieveRoleMember(groupID, new UUID(group.Data["OwnerRoleID"]), agentID);
if (rmembership == null)
if(!UUID.TryParse(group.Data["OwnerRoleID"], out UUID ownerRoleID))
return false;
return true;
RoleMembershipData rmembership = m_Database.RetrieveRoleMember(groupID, ownerRoleID, agentID);
return rmembership is not null;
}
#endregion

View File

@@ -88,8 +88,8 @@ namespace OpenSim.OfflineIM
Dictionary<string, object> sendData = new Dictionary<string, object>();
sendData["PrincipalID"] = principalID;
Dictionary<string, object> ret = MakeRequest("GET", sendData);
Dictionary<string, object> ret = MakeRequest("GET", sendData);
if (ret == null)
return ims;
@@ -118,14 +118,12 @@ namespace OpenSim.OfflineIM
}
}
}
return ims;
}
public bool StoreMessage(GridInstantMessage im, out string reason)
{
Dictionary<string, object> sendData = OfflineIMDataUtils.GridInstantMessage(im);
Dictionary<string, object> ret = MakeRequest("STORE", sendData);
if (ret == null)

View File

@@ -163,6 +163,5 @@ namespace OpenSim.OfflineIM
m_Database.Delete("PrincipalID", userID.ToString());
m_Database.Delete("FromID", userID.ToString());
}
}
}

View File

@@ -0,0 +1,428 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
namespace osWebRtcVoice
{
// There are several different hashing systems ranging from int's to SHA versions.
// The model here is to create a hasher of the desired type, do Add's of things to
// hash, and complete with a Finish() to return a BHash that contains the hash value.
// Since some hash functions are incremental (doing Add's) while some are buffer
// oriented (create a hash of a byte buffer), the interface is built to cover both.
// Some optimizations are implemented internally (like not copying the buffer
// for buffer based hashers if Finish(bytes) is used).
//
// var hasher = new BHashSHA256();
// BHash bHash = hasher.Finish(buffer);
// byte[] theHash = bHash.ToByte();
//
// Note that BHash has IEquatable and IComparible so it can be used in Dictionaries
// and sorted Lists.
//
// The C# GetHashCode() method returns an int that is usually based on location.
// Signatures should really be at least 64 bits so these routines generate
// ulong's for hashes and fold them to make the int for GetHashCode().
// Create a BHasher, do a bunch of 'Add's, then Finish().
public interface IBHasher {
// Create a new object implementing BHasher, then Add values to be hashed
void Add(byte c);
void Add(ushort c);
void Add(uint c);
void Add(ulong c);
void Add(float c);
void Add(byte[] c, int offset, int len);
void Add(string c);
void Add(BHash c);
BHash Finish();
// Finish and add byte array.
// If no Add's before, can do the hashing without copying the byte array
BHash Finish(byte[] c);
BHash Finish(byte[] c, int offset, int len);
// Get the hash code after doing a Finish()
BHash Hash();
}
// ======================================================================
// BHasher computes a BHash which holds the hash value after Finish() is called.
public abstract class BHash : IEquatable<BHash>, IComparable<BHash> {
public abstract override string ToString();
public abstract byte[] ToBytes();
public abstract ulong ToULong(); // returns the hash of the hash if not int based hash
public abstract bool Equals(BHash other);
public abstract int CompareTo(BHash obj);
// public abstract int Compare(BHash x, BHash y); // TODO: do we need this function?
public abstract override int GetHashCode(); // to match the C# standard hash function
}
// A hash that is an UInt64
public class BHashULong : BHash {
protected ulong _hash;
public BHashULong() {
_hash = 5131;
}
public BHashULong(ulong initialHash) {
_hash = initialHash;
}
// the .NET GetHashCode uses an int. Make conversion easy.
public BHashULong(int initialHash) {
_hash = (ulong)initialHash;
}
public override string ToString() {
return _hash.ToString();
}
public override byte[] ToBytes() {
return BitConverter.GetBytes(_hash);
}
public override ulong ToULong() {
return _hash;
}
public override bool Equals(BHash other) {
bool ret = false;
if (other != null) {
BHash bh = other as BHashULong;
if (bh != null) {
ret = _hash.Equals(bh.ToULong());
}
}
return ret;
}
public override int CompareTo(BHash other) {
int ret = 1;
if (other != null) {
if (other is BHashULong bh) {
ret = _hash.CompareTo(bh.ToULong());
}
}
return ret;
}
public override int GetHashCode() {
ulong upper = (_hash >> 32) & 0xffffffff;
ulong lower = _hash & 0xffffffff;
return (int)(upper ^ lower);
}
}
// A hash that is an array of bytes
public class BHashBytes : BHash {
private readonly byte[] _hash;
public BHashBytes() {
_hash = new byte[0];
}
public BHashBytes(byte[] initialHash) {
_hash = initialHash;
}
public override string ToString() {
// BitConverter puts a hyphen between each byte. Remove them
return BitConverter.ToString(_hash).Replace("-", String.Empty);
}
public override byte[] ToBytes() {
return _hash;
}
public override ulong ToULong() {
return this.MakeHashCode();
}
public override bool Equals(BHash other) {
bool ret = false;
if (other != null) {
BHash bh = other as BHashBytes;
if (bh != null) {
ret = _hash.Equals(bh.ToBytes());
}
}
return ret;
}
public override int CompareTo(BHash other) {
int ret = 1;
if (other != null) {
BHash bh = other as BHashBytes;
if (bh != null) {
byte[] otherb = bh.ToBytes();
if (_hash.Length != otherb.Length) {
ret = _hash.Length.CompareTo(otherb.Length);
}
else {
ret = 0; // start off assuming they are equal
for (int ii = 0; ii < _hash.Length; ii++) {
ret = _hash[ii].CompareTo(otherb[ii]);
if (ret != 0) break;
}
}
}
}
return ret;
}
public override int GetHashCode()
{
ulong hashhash = this.MakeHashCode();
ulong upper = (hashhash >> 32 )& 0xffffffff;
ulong lower = hashhash & 0xffffffff;
return (int)(upper ^ lower);
}
public ulong MakeHashCode() {
ulong h = 5381;
for (int ii = 0; ii < _hash.Length; ii++) {
h = ((h << 5) + h) + (ulong)(_hash[ii]);
}
return h;
}
}
// ======================================================================
// ======================================================================
public abstract class BHasher : IBHasher
{
public BHasher() {
}
public abstract void Add(byte c);
public abstract void Add(ushort c);
public abstract void Add(uint c);
public abstract void Add(ulong c);
public abstract void Add(float c);
public abstract void Add(byte[] c, int offset, int len);
public abstract void Add(string c);
public abstract void Add(BHash c);
public abstract BHash Finish();
public abstract BHash Finish(byte[] c);
public abstract BHash Finish(byte[] c, int offset, int len);
public abstract BHash Hash();
}
// A hasher that builds up a buffer of bytes ('building') and then hashes over same
public abstract class BHasherBytes : BHasher {
protected byte[] building;
protected int buildingLoc;
protected int allocStep = 1024;
public BHasherBytes() : base() {
building = new byte[allocStep];
buildingLoc = 0;
}
private byte[] oneByte = new byte[1];
public override void Add(byte c) {
oneByte[0] = c;
AddBytes(oneByte, 0, 1);
}
public override void Add(ushort c) {
byte[] bytes = BitConverter.GetBytes(c);
AddBytes(bytes, 0, bytes.Length);
}
public override void Add(uint c) {
byte[] bytes = BitConverter.GetBytes(c);
AddBytes(bytes, 0, bytes.Length);
}
public override void Add(ulong c) {
byte[] bytes = BitConverter.GetBytes(c);
AddBytes(bytes, 0, bytes.Length);
}
public override void Add(float c) {
byte[] bytes = BitConverter.GetBytes(c);
AddBytes(bytes, 0, bytes.Length);
}
public override void Add(byte[] c, int offset, int len) {
AddBytes(c, offset, len);
}
public override void Add(string c)
{
byte[] bytes = Encoding.UTF8.GetBytes(c);
AddBytes(bytes, 0, bytes.Length);
}
public override void Add(BHash c) {
byte[] bytes = c.ToBytes();
AddBytes(bytes, 0, bytes.Length);
}
// Implemented by derived class
// public abstract BHash Finish();
// Helper function for simple byte array
public override BHash Finish(byte[] c) {
return this.Finish(c, 0, c.Length);
}
// Implemented by derived class
// public abstract BHash Finish(byte[] c, int offset, int len);
// Implemented by derived class
// public abstract BHash Hash();
// Add the given number of bytes to the byte array being built
protected void AddBytes(byte[] addition, int offset, int len) {
// byte[] tempBytes = new byte[len]; // DEBUG DEBUG
// Array.Copy(addition, offset, tempBytes, 0, len); // DEBUG DEBUG
// System.Console.WriteLine(String.Format("AddBytes: offset={0}, len={1}, bytes={2}", // DEBUG DEBUG
// offset, len, BitConverter.ToString(tempBytes).Replace("-", String.Empty))); // DEBUG DEBUG
if (len < 0 || offset < 0 || addition == null) {
throw new ArgumentException(String.Format("BHasherBytes.AddBytes: Bad parameters. offset={0}, len={1}",
offset, len));
}
if (offset + len > addition.Length) {
throw new ArgumentException(String.Format("BHasherBytes.AddBytes: addition parameters off end of array. addition.len={0}, offset={1}, len={2}",
addition.Length, offset, len));
}
if (len > 0) {
if (buildingLoc + len > building.Length) {
// New data requires expanding the data buffer
byte[] newBuilding = new byte[buildingLoc + len + allocStep];
Buffer.BlockCopy(building, 0, newBuilding, 0, buildingLoc);
building = newBuilding;
}
Buffer.BlockCopy(addition, offset, building, buildingLoc, len);
buildingLoc += len;
}
}
}
// ======================================================================
// ULong hash code taken from Meshmerizer
public class BHasherMdjb2 : BHasherBytes, IBHasher {
BHashULong hash = new BHashULong();
public BHasherMdjb2() : base() {
}
public override BHash Finish() {
hash = new BHashULong(ComputeMdjb2Hash(building, 0, buildingLoc));
return hash;
}
public override BHash Finish(byte[] c, int offset, int len) {
if (building.Length > 0) {
AddBytes(c, offset, len);
hash = new BHashULong(ComputeMdjb2Hash(building, 0, buildingLoc));
}
else {
// if no 'Add's were done, don't copy the input data
hash = new BHashULong(ComputeMdjb2Hash(c, offset, len));
}
return hash;
}
private ulong ComputeMdjb2Hash(byte[] c, int offset, int len) {
ulong h = 5381;
for (int ii = offset; ii < offset+len; ii++) {
h = ((h << 5) + h) + (ulong)(c[ii]);
}
return h;
}
public override BHash Hash() {
return hash;
}
}
// ======================================================================
public class BHasherMD5 : BHasherBytes, IBHasher {
BHashBytes hash = new BHashBytes();
public BHasherMD5() : base() {
}
public override BHash Finish() {
MD5 md5 = MD5.Create();
hash = new BHashBytes(md5.ComputeHash(building, 0, buildingLoc));
return hash;
}
public override BHash Finish(byte[] c) {
return this.Finish(c, 0, c.Length);
}
public override BHash Finish(byte[] c, int offset, int len) {
MD5 md5 = MD5.Create();
if (building.Length > 0) {
AddBytes(c, offset, len);
hash = new BHashBytes(md5.ComputeHash(building, 0, buildingLoc));
}
else {
// if no 'Add's were done, don't copy the input data
hash = new BHashBytes(md5.ComputeHash(c, offset, len));
}
return hash;
}
public override BHash Hash() {
return hash;
}
}
// ======================================================================
public class BHasherSHA256 : BHasherBytes, IBHasher {
BHashBytes hash = new BHashBytes();
public BHasherSHA256() : base() {
}
public override BHash Finish() {
using (SHA256 SHA256 = SHA256.Create()) {
hash = new BHashBytes(SHA256.ComputeHash(building, 0, buildingLoc));
}
return hash;
}
public override BHash Finish(byte[] c) {
return this.Finish(c, 0, c.Length);
}
public override BHash Finish(byte[] c, int offset, int len) {
using (SHA256 SHA256 = SHA256.Create()) {
if (buildingLoc > 0) {
AddBytes(c, offset, len);
hash = new BHashBytes(SHA256.ComputeHash(building, 0, buildingLoc));
}
else {
// if no 'Add's were done, don't copy the input data
hash = new BHashBytes(SHA256.ComputeHash(c, offset, len));
}
}
return hash;
}
public override BHash Hash() {
return hash;
}
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using log4net;
namespace osWebRtcVoice
{
// Encapsulization of a Session to the Janus server
public class JanusAudioBridge : JanusPlugin
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[JANUS AUDIO BRIDGE]";
// Wrapper around the session connection to Janus-gateway
public JanusAudioBridge(JanusSession pSession) : base(pSession, "janus.plugin.audiobridge")
{
// m_log.DebugFormat("{0} JanusAudioBridge constructor", LogHeader);
}
public override void Dispose()
{
if (IsConnected)
{
// Close the handle
}
base.Dispose();
}
public async Task<AudioBridgeResp> SendAudioBridgeMsg(PluginMsgReq pMsg)
{
AudioBridgeResp ret = null;
try
{
ret = new AudioBridgeResp(await SendPluginMsg(pMsg));
}
catch (Exception e)
{
m_log.ErrorFormat("{0} SendPluginMsg. Exception {1}", LogHeader, e);
}
return ret;
}
/// <summary>
/// Create a room with the given criteria. This talks to Janus to create the room.
/// If the room with this RoomId already exists, just return it.
/// Janus could create and return the RoomId but this presumes that the Janus server
/// is only being used for our voice service.
/// </summary>
/// <param name="pRoomId">integer room ID to create</param>
/// <param name="pSpatial">boolean on whether room will be spatial or non-spatial</param>
/// <param name="pRoomDesc">added as "description" to the created room</param>
/// <returns></returns>
public async Task<JanusRoom> CreateRoom(int pRoomId, bool pSpatial, string pRoomDesc)
{
JanusRoom ret = null;
try
{
JanusMessageResp resp = await SendPluginMsg(new AudioBridgeCreateRoomReq(pRoomId, pSpatial, pRoomDesc));
AudioBridgeResp abResp = new AudioBridgeResp(resp);
m_log.DebugFormat("{0} CreateRoom. ReturnCode: {1}", LogHeader, abResp.AudioBridgeReturnCode);
switch (abResp.AudioBridgeReturnCode)
{
case "created":
ret = new JanusRoom(this, pRoomId);
break;
case "event":
if (abResp.AudioBridgeErrorCode == 486)
{
m_log.WarnFormat("{0} CreateRoom. Room {1} already exists. Reusing! {2}", LogHeader, pRoomId, abResp.ToString());
// if room already exists, just use it
ret = new JanusRoom(this, pRoomId);
}
else
{
m_log.ErrorFormat("{0} CreateRoom. XX Room creation failed: {1}", LogHeader, abResp.ToString());
}
break;
default:
m_log.ErrorFormat("{0} CreateRoom. YY Room creation failed: {1}", LogHeader, abResp.ToString());
break;
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} CreateRoom. Exception {1}", LogHeader, e);
}
return ret;
}
public async Task<bool> DestroyRoom(JanusRoom janusRoom)
{
bool ret = false;
try
{
JanusMessageResp resp = await SendPluginMsg(new AudioBridgeDestroyRoomReq(janusRoom.RoomId));
ret = true;
}
catch (Exception e)
{
m_log.ErrorFormat("{0} DestroyRoom. Exception {1}", LogHeader, e);
}
return ret;
}
// Constant used to denote that this is a spatial audio room for the region (as opposed to parcels)
public const int REGION_ROOM_ID = -999;
private Dictionary<int, JanusRoom> _rooms = new Dictionary<int, JanusRoom>();
// Calculate a room number for the given parameters. The room number is a hash of the parameters.
// The attempt is to deterministicly create a room number so all regions will generate the
// same room number across sessions and across the grid.
// getHashCode() is not deterministic across sessions.
public static int CalcRoomNumber(string pRegionId, string pChannelType, int pParcelLocalID, string pChannelID)
{
var hasher = new BHasherMdjb2();
// If there is a channel specified it must be group
switch (pChannelType)
{
case "local":
// A "local" channel is unique to the region and parcel
hasher.Add(pRegionId);
hasher.Add(pChannelType);
hasher.Add(pParcelLocalID);
break;
case "multiagent":
// A "multiagent" channel is unique to the grid
// should add a GridId here
hasher.Add(pChannelID);
hasher.Add(pChannelType);
break;
default:
throw new Exception("Unknown channel type: " + pChannelType);
}
var hashed = hasher.Finish();
// The "Abs()" is because Janus room number must be a positive integer
// And note that this is the BHash.GetHashCode() and not Object.getHashCode().
int roomNumber = Math.Abs(hashed.GetHashCode());
return roomNumber;
}
public async Task<JanusRoom> SelectRoom(string pRegionId, string pChannelType, bool pSpatial, int pParcelLocalID, string pChannelID)
{
int roomNumber = CalcRoomNumber(pRegionId, pChannelType, pParcelLocalID, pChannelID);
// Should be unique for the given use and channel type
m_log.DebugFormat("{0} SelectRoom: roomNumber={1}", LogHeader, roomNumber);
// Check to see if the room has already been created
lock (_rooms)
{
if (_rooms.ContainsKey(roomNumber))
{
return _rooms[roomNumber];
}
}
// The room doesn't exist. Create it.
string roomDesc = pRegionId + "/" + pChannelType + "/" + pParcelLocalID + "/" + pChannelID;
JanusRoom ret = await CreateRoom(roomNumber, pSpatial, roomDesc);
JanusRoom existingRoom = null;
if (ret is not null)
{
lock (_rooms)
{
if (_rooms.ContainsKey(roomNumber))
{
// If the room was created while we were waiting,
existingRoom = _rooms[roomNumber];
}
else
{
// Our room is the first one created. Save it.
_rooms[roomNumber] = ret;
}
}
}
if (existingRoom is not null)
{
// The room we created was already created by someone else. Delete ours and use the existing one
await DestroyRoom(ret);
ret = existingRoom;
}
return ret;
}
// Return the room with the given room ID or 'null' if no such room
public JanusRoom GetRoom(int pRoomId)
{
JanusRoom ret = null;
lock (_rooms)
{
_rooms.TryGetValue(pRoomId, out ret);
}
return ret;
}
public override void Handle_Event(JanusMessageResp pResp)
{
base.Handle_Event(pResp);
AudioBridgeResp abResp = new AudioBridgeResp(pResp);
if (abResp is not null && abResp.AudioBridgeReturnCode == "event")
{
// An audio bridge event!
m_log.DebugFormat("{0} Handle_Event. {1}", LogHeader, abResp.ToString());
}
}
public override void Handle_Message(JanusMessageResp pResp)
{
base.Handle_Message(pResp);
AudioBridgeResp abResp = new AudioBridgeResp(pResp);
if (abResp is not null && abResp.AudioBridgeReturnCode == "event")
{
// An audio bridge event!
m_log.DebugFormat("{0} Handle_Event. {1}", LogHeader, abResp.ToString());
}
}
}
}

View File

@@ -0,0 +1,639 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using OpenMetaverse.StructuredData;
using OpenMetaverse;
using log4net;
namespace osWebRtcVoice
{
/// <summary>
/// Wrappers around the Janus requests and responses.
/// Since the messages are JSON and, because of the libraries we are using,
/// the internal structure is an OSDMap, these routines hold all the logic
/// to getting and setting the values in the JSON.
/// </summary>
public class JanusMessage
{
protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected static readonly string LogHeader = "[JANUS MESSAGE]";
protected OSDMap m_message = new OSDMap();
public JanusMessage()
{
}
// A basic Janus message is:
// {
// "janus": "operation",
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea",
// "session_id": 5645225333294848, // optional, gives the session ID
// "handle_id": 6969906757968657 // optional, gives the plugin handle ID
// "sender": 6969906757968657 // optional, gives the ID of the sending subsystem
// "jsep": { "type": "offer", "sdp": "..." } // optional, gives the SDP
// }
public JanusMessage(string pType) : this()
{
m_message["janus"] = pType;
m_message["transaction"] = UUID.Random().ToString();
}
public OSDMap RawBody => m_message;
public string TransactionId {
get { return m_message.TryGetString("transaction", out string tid) ? tid : null; }
set { m_message["transaction"] = value; }
}
public string Sender {
get { return m_message.TryGetString("sender", out string tid) ? tid : null; }
set { m_message["sender"] = value; }
}
public OSDMap Jsep {
get { return m_message.TryGetOSDMap("jsep", out OSDMap jsep) ? jsep : null; }
set { m_message["jsep"] = value; }
}
public void SetJsep(string pOffer, string pSdp)
{
m_message["jsep"] = new OSDMap()
{
{ "type", pOffer },
{ "sdp", pSdp }
};
}
public void AddAPIToken(string pToken)
{
m_message["apisecret"] = pToken;
}
// Note that the session_id is a long number in the JSON so we convert the string.
public string sessionId {
get { return m_message.TryGetValue("session_id", out OSD tmposd) ? OSDToLong(tmposd).ToString() : string.Empty; }
set { m_message["session_id"] = long.Parse(value); }
}
public bool hasSessionId { get { return m_message.ContainsKey("session_id"); } }
public void AddSessionId(string pToken)
{
AddSessionId(long.Parse(pToken));
}
public void AddSessionId(long pToken)
{
m_message["session_id"] = pToken;
}
public bool hasHandleId { get { return m_message.ContainsKey("handle_id"); } }
public void AddHandleId(string pToken)
{
m_message["handle_id"] = long.Parse(pToken);
}
public string sender
{
get { return m_message.TryGetString("sender", out string str) ? str : string.Empty; }
}
public virtual string ToJson()
{
return m_message.ToString();
}
public override string ToString()
{
return m_message.ToString();
}
// Utility function to convert an OSD object to an long. The OSD object can be an OSDInteger
// or an OSDArray of 4 or 8 integers.
// This exists because the JSON to OSD parser can return an OSDArray for a long number
// since there is not an OSDLong type.
// The design of the OSD conversion functions kinda needs one to know how the number
// is stored in order to extract it. Like, if it's stored as a long value (8 bytes)
// and one fetches it with .AsInteger(), it will return the first 4 bytes as an integer
// and not the long value. So this function looks at the type of the OSD object and
// extracts the number appropriately.
public static long OSDToLong(OSD pIn)
{
long ret = 0;
switch (pIn.Type)
{
case OSDType.Integer:
ret = (long)(pIn as OSDInteger).AsInteger();
break;
case OSDType.Binary:
byte[] value = (pIn as OSDBinary).value;
if (value.Length == 4)
{
ret = (long)(pIn as OSDBinary).AsInteger();
}
if (value.Length == 8)
{
ret = (pIn as OSDBinary).AsLong();
}
break;
case OSDType.Array:
if ((pIn as OSDArray).Count == 4)
{
ret = (long)pIn.AsInteger();
}
if ((pIn as OSDArray).Count == 8)
{
ret = pIn.AsLong();
}
break;
}
return ret;
}
}
// ==============================================================
// A Janus request message is a basic Janus message with an API token
public class JanusMessageReq : JanusMessage
{
public JanusMessageReq(string pType) : base(pType)
{
}
}
// ==============================================================
// Janus message response is a basic Janus message with the response data
// {
// "janus": "success",
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea", // ID of the requesting message
// "data": { ... } // the response data
// "error": { "code": 123, "reason": "..." } // if there was an error
// }
// The "janus" return code changes depending on the request. The above is for
// a successful response. Could be
// "event": for an event message (See JanusEventResp)
// "keepalive": for a keepalive event
public class JanusMessageResp : JanusMessage
{
public JanusMessageResp() : base()
{
}
public JanusMessageResp(string pType) : base(pType)
{
}
public JanusMessageResp(OSDMap pMap) : base()
{
m_message = pMap;
}
public static JanusMessageResp FromJson(string pJson)
{
var newBody = OSDParser.DeserializeJson(pJson) as OSDMap;
return new JanusMessageResp(newBody);
}
// Return the "data" portion of the response as an OSDMap or null if there is none
public OSDMap dataSection { get { return m_message.TryGetOSDMap("data", out OSDMap osdm) ? osdm : null; } }
// Check if a successful response code is in the response
public virtual bool isSuccess { get { return CheckReturnCode("success"); } }
public virtual bool isEvent { get { return CheckReturnCode("event"); } }
public virtual bool isError { get { return CheckReturnCode("error"); } }
public virtual bool CheckReturnCode(string pCode)
{
return ReturnCode == pCode;
}
public virtual string ReturnCode { get {
string ret = String.Empty;
if (m_message is not null && m_message.ContainsKey("janus"))
{
ret = m_message["janus"].AsString();
}
return ret;
} }
}
// ==============================================================
// An error response is a Janus response with an error code and reason.
// {
// "janus": "error",
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea", // ID of the requesting message
// "error": { "code": 123, "reason": "..." } // if there was an error
// }
public class ErrorResp : JanusMessageResp
{
public ErrorResp() : base("error")
{
}
public ErrorResp(string pType) : base(pType)
{
}
public ErrorResp(JanusMessageResp pResp) : base(pResp.RawBody)
{
}
public void SetError(int pCode, string pReason)
{
m_message["error"] = new OSDMap()
{
{ "code", pCode },
{ "reason", pReason }
};
}
// Dig through the response to get the error code or 0 if there is none
public int errorCode { get {
int ret = 0;
if (m_message.ContainsKey("error"))
{
var err = m_message["error"];
if (err is OSDMap)
ret = (int)OSDToLong((err as OSDMap)["code"]);
}
return ret;
}}
// Dig through the response to get the error reason or empty string if there is none
public string errorReason { get {
string ret = String.Empty;
if (m_message.ContainsKey("error"))
{
var err = m_message["error"];
if (err is OSDMap)
ret = (err as OSDMap)["reason"];
}
// return ((m_message["error"] as OSDMap)?["reason"]) ?? String.Empty;
return ret;
}}
}
// ==============================================================
// Create session request and response
public class CreateSessionReq : JanusMessageReq
{
public CreateSessionReq() : base("create")
{
}
}
public class CreateSessionResp : JanusMessageResp
{
public CreateSessionResp(JanusMessageResp pResp) : base(pResp.RawBody)
{ }
public string returnedId { get {
// The JSON response gives a long number (not a string)
// and the ODMap conversion interprets it as a long (OSDLong).
// If one just does a "ToString()" on the OSD object, you
// get an interpretation of the binary value.
return dataSection.ContainsKey("id") ? OSDToLong(dataSection["id"]).ToString() : String.Empty;
}}
}
// ==============================================================
public class DestroySessionReq : JanusMessageReq
{
public DestroySessionReq() : base("destroy")
{
// Doesn't include the session ID because it is the URI
}
}
// ==============================================================
public class TrickleReq : JanusMessageReq
{
// An empty trickle request is used to signal the end of the trickle
public TrickleReq(JanusViewerSession pVSession) : base("trickle")
{
m_message["candidate"] = new OSDMap()
{
{ "completed", true },
};
}
public TrickleReq(JanusViewerSession pVSession, OSD pCandidates) : base("trickle")
{
m_message["viewer_session"] = pVSession.ViewerSessionID;
if (pCandidates is OSDArray)
m_message["candidates"] = pCandidates;
else
m_message["candidate"] = pCandidates;
}
}
// ==============================================================
public class AttachPluginReq : JanusMessageReq
{
public AttachPluginReq(string pPlugin) : base("attach")
{
m_message["plugin"] = pPlugin;
}
}
public class AttachPluginResp : JanusMessageResp
{
public AttachPluginResp(JanusMessageResp pResp) : base(pResp.RawBody)
{ }
public string pluginId { get {
return dataSection.ContainsKey("id") ? OSDToLong(dataSection["id"]).ToString() : String.Empty;
}}
}
// ==============================================================
public class DetachPluginReq : JanusMessageReq
{
public DetachPluginReq() : base("detach")
{
// Doesn't include the session ID or plugin ID because it is the URI
}
}
// ==============================================================
public class HangupReq : JanusMessageReq
{
public HangupReq() : base("hangup")
{
// Doesn't include the session ID or plugin ID because it is the URI
}
}
// ==============================================================
// Plugin messages are defined here as wrappers around OSDMap.
// The ToJson() method is overridden to put the OSDMap into the
// message body.
// A plugin request is formatted like:
// {
// "janus": "message",
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea",
// "body": {
// "request": "create",
// "room": 10,
// "is_private": false,
// }
public class PluginMsgReq : JanusMessageReq
{
private OSDMap m_body = new OSDMap();
// Note that the passed OSDMap is placed in the "body" section of the message
public PluginMsgReq(OSDMap pBody) : base("message")
{
m_body = pBody;
}
public void AddStringToBody(string pKey, string pValue)
{
m_body[pKey] = pValue;
}
public void AddIntToBody(string pKey, int pValue)
{
m_body[pKey] = pValue;
}
public void AddBoolToBody(string pKey, bool pValue)
{
m_body[pKey] = pValue;
}
public void AddOSDToBody(string pKey, OSD pValue)
{
m_body[pKey] = pValue;
}
public override string ToJson()
{
m_message["body"] = m_body;
return base.ToJson();
}
}
// A plugin response is formatted like:
// {
// "janus": "success",
// "session_id": 5645225333294848,
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea",
// "sender": 6969906757968657,
// "plugindata": {
// "plugin": "janus.plugin.audiobridge",
// "data": {
// "audiobridge": "created",
// "room": 10,
// "permanent": false
// }
// }
public class PluginMsgResp : JanusMessageResp
{
public OSDMap m_pluginData;
public OSDMap m_data;
public PluginMsgResp(JanusMessageResp pResp) : base(pResp.RawBody)
{
if (m_message is not null && m_message.ContainsKey("plugindata"))
{
// Move the plugin data up into the m_data var so it is easier to get to
m_pluginData = m_message["plugindata"] as OSDMap;
if (m_pluginData is not null && m_pluginData.ContainsKey("data"))
{
m_data = m_pluginData["data"] as OSDMap;
// m_log.DebugFormat("{0} AudioBridgeResp. Found both plugindata and data: data={1}", LogHeader, m_data.ToString());
}
}
}
public OSDMap PluginRespData { get { return m_data; } }
// Get an integer value for a key in the response data or zero if not there
public int PluginRespDataInt(string pKey)
{
if (m_data is null)
return 0;
return m_data.ContainsKey(pKey) ? (int)OSDToLong(m_data[pKey]) : 0;
}
// Get a string value for a key in the response data or empty string if not there
public string PluginRespDataString(string pKey)
{
if (m_data is null)
return String.Empty;
return m_data.ContainsKey(pKey) ? m_data[pKey].AsString() : String.Empty;
}
}
// ==============================================================
// Plugin messages for the audio bridge.
// Audiobridge responses are formatted like:
// {
// "janus": "success",
// "session_id": 5645225333294848,
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea",
// "sender": 6969906757968657,
// "plugindata": {
// "plugin": "janus.plugin.audiobridge",
// "data": {
// "audiobridge": "created",
// "room": 10,
// "permanent": false
// }
// }
public class AudioBridgeResp: PluginMsgResp
{
public AudioBridgeResp(JanusMessageResp pResp) : base(pResp)
{
}
public override bool isSuccess { get { return PluginRespDataString("audiobridge") == "success"; } }
// Return the return code if it is in the response or empty string if not
public string AudioBridgeReturnCode { get { return PluginRespDataString("audiobridge"); } }
// Return the error code if it is in the response or zero if not
public int AudioBridgeErrorCode { get { return PluginRespDataInt("error_code"); } }
// Return the room ID if it is in the response or zero if not
public int RoomId { get { return PluginRespDataInt("room"); } }
}
// ==============================================================
public class AudioBridgeCreateRoomReq : PluginMsgReq
{
public AudioBridgeCreateRoomReq(int pRoomId) : this(pRoomId, false, null)
{
}
public AudioBridgeCreateRoomReq(int pRoomId, bool pSpatial, string pDesc) : base(new OSDMap() {
{ "room", pRoomId },
{ "request", "create" },
{ "is_private", false },
{ "permanent", false },
{ "sampling_rate", 48000 },
{ "spatial_audio", pSpatial },
{ "denoise", false },
{ "record", false }
})
{
if (!String.IsNullOrEmpty(pDesc))
AddStringToBody("description", pDesc);
}
}
// ==============================================================
public class AudioBridgeDestroyRoomReq : PluginMsgReq
{
public AudioBridgeDestroyRoomReq(int pRoomId) : base(new OSDMap() {
{ "request", "destroy" },
{ "room", pRoomId },
{ "permanent", true }
})
{
}
}
// ==============================================================
public class AudioBridgeJoinRoomReq : PluginMsgReq
{
public AudioBridgeJoinRoomReq(int pRoomId, string pAgentName) : base(new OSDMap() {
{ "request", "join" },
{ "room", pRoomId },
{ "display", pAgentName }
})
{
}
}
// A successful response contains the participant ID and the SDP
public class AudioBridgeJoinRoomResp : AudioBridgeResp
{
public AudioBridgeJoinRoomResp(JanusMessageResp pResp) : base(pResp)
{
}
public int ParticipantId { get { return PluginRespDataInt("id"); } }
}
// ==============================================================
public class AudioBridgeConfigRoomReq : PluginMsgReq
{
// TODO:
public AudioBridgeConfigRoomReq(int pRoomId, string pSdp) : base(new OSDMap() {
{ "request", "configure" },
})
{
}
}
public class AudioBridgeConfigRoomResp : AudioBridgeResp
{
// TODO:
public AudioBridgeConfigRoomResp(JanusMessageResp pResp) : base(pResp)
{
}
}
// ==============================================================
public class AudioBridgeLeaveRoomReq : PluginMsgReq
{
public AudioBridgeLeaveRoomReq(int pRoomId, int pAttendeeId) : base(new OSDMap() {
{ "request", "leave" },
{ "room", pRoomId },
{ "id", pAttendeeId }
})
{
}
}
// ==============================================================
public class AudioBridgeListRoomsReq : PluginMsgReq
{
public AudioBridgeListRoomsReq() : base(new OSDMap() {
{ "request", "list" }
})
{
}
}
// ==============================================================
public class AudioBridgeListParticipantsReq : PluginMsgReq
{
public AudioBridgeListParticipantsReq(int pRoom) : base(new OSDMap() {
{ "request", "listparticipants" },
{ "room", pRoom }
})
{
}
}
// ==============================================================
public class AudioBridgeEvent : AudioBridgeResp
{
public AudioBridgeEvent(JanusMessageResp pResp) : base(pResp)
{
}
}
// ==============================================================
// The LongPoll request returns events from the plugins. These are formatted
// like the other responses but are not responses to requests.
// They are formatted like:
// {
// "janus": "event",
// "sender": 6969906757968657,
// "transaction": "baefcec8-70c5-4e79-b2c1-d653b9617dea",
// "plugindata": {
// "plugin": "janus.plugin.audiobridge",
// "data": {
// "audiobridge": "event",
// "room": 10,
// "participants": 1,
// "participants": [
// {
// "id": 1234,
// "display": "John Doe",
// "audio_level": 0.0,
// "video_room": false,
// "video_muted": false,
// "audio_muted": false,
// "feed": 1234
// }
// ]
// }
// }
public class EventResp : JanusMessageResp
{
public EventResp() : base()
{
}
public EventResp(string pType) : base(pType)
{
}
public EventResp(JanusMessageResp pResp) : base(pResp.RawBody)
{
}
}
// ==============================================================
}

View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Threading.Tasks;
using OpenSim.Framework;
using OpenSim.Services.Interfaces;
using OpenSim.Services.Base;
using OpenMetaverse.StructuredData;
using OpenMetaverse;
using Nini.Config;
using log4net;
namespace osWebRtcVoice
{
// Encapsulization of a Session to the Janus server
public class JanusPlugin : IDisposable
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[JANUS PLUGIN]";
protected IConfigSource _Config;
protected JanusSession _JanusSession;
public string PluginName { get; private set; }
public string PluginId { get; private set; }
public string PluginUri { get ; private set ; }
public bool IsConnected => !String.IsNullOrEmpty(PluginId);
// Wrapper around the session connection to Janus-gateway
public JanusPlugin(JanusSession pSession, string pPluginName)
{
_JanusSession = pSession;
PluginName = pPluginName;
}
public virtual void Dispose()
{
if (IsConnected)
{
// Close the handle
}
}
public Task<JanusMessageResp> SendPluginMsg(OSDMap pParams)
{
return _JanusSession.SendToJanus(new PluginMsgReq(pParams), PluginUri);
}
public Task<JanusMessageResp> SendPluginMsg(PluginMsgReq pJMsg)
{
return _JanusSession.SendToJanus(pJMsg, PluginUri);
}
/// <summary>
/// Make the create a handle to a plugin within the session.
/// </summary>
/// <returns>TRUE if handle was created successfully</returns>
public async Task<bool> Activate(IConfigSource pConfig)
{
_Config = pConfig;
bool ret = false;
try
{
var resp = await _JanusSession.SendToSession(new AttachPluginReq(PluginName));
if (resp is not null && resp.isSuccess)
{
var handleResp = new AttachPluginResp(resp);
PluginId = handleResp.pluginId;
PluginUri = _JanusSession.SessionUri + "/" + PluginId;
m_log.DebugFormat("{0} Activate. Plugin attached. ID={1}, URL={2}", LogHeader, PluginId, PluginUri);
_JanusSession.PluginId = PluginId;
_JanusSession.OnEvent += Handle_Event;
_JanusSession.OnMessage += Handle_Message;
ret = true;
}
else
{
m_log.ErrorFormat("{0} Activate: failed to attach to plugin {1}", LogHeader, PluginName);
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Activate: exception attaching to plugin {1}: {2}", LogHeader, PluginName, e);
}
return ret;
}
public virtual async Task<bool> Detach()
{
bool ret = false;
if (!IsConnected || _JanusSession is null)
{
m_log.WarnFormat("{0} Detach. Not connected", LogHeader);
return ret;
}
try
{
_JanusSession.OnEvent -= Handle_Event;
_JanusSession.OnMessage -= Handle_Message;
// We send the 'detach' message to the plugin URI
var resp = await _JanusSession.SendToJanus(new DetachPluginReq(), PluginUri);
if (resp is not null && resp.isSuccess)
{
m_log.DebugFormat("{0} Detach. Detached", LogHeader);
ret = true;
}
else
{
m_log.ErrorFormat("{0} Detach: failed", LogHeader);
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Detach: exception {1}", LogHeader, e);
}
return ret;
}
public virtual void Handle_Event(JanusMessageResp pResp)
{
m_log.DebugFormat("{0} Handle_Event: {1}", LogHeader, pResp.ToString());
}
public virtual void Handle_Message(JanusMessageResp pResp)
{
m_log.DebugFormat("{0} Handle_Message: {1}", LogHeader, pResp.ToString());
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using OpenSim.Framework;
using OpenSim.Services.Interfaces;
using OpenSim.Services.Base;
using OpenMetaverse.StructuredData;
using OpenMetaverse;
using Nini.Config;
using log4net;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using System.Collections.Generic;
namespace osWebRtcVoice
{
// Encapsulization of a Session to the Janus server
public class JanusRoom : IDisposable
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[JANUS ROOM]";
public int RoomId { get; private set; }
private JanusPlugin _AudioBridge;
// Wrapper around the session connection to Janus-gateway
public JanusRoom(JanusPlugin pAudioBridge, int pRoomId)
{
_AudioBridge = pAudioBridge;
RoomId = pRoomId;
}
public void Dispose()
{
// Close the room
}
public async Task<bool> JoinRoom(JanusViewerSession pVSession)
{
bool ret = false;
try
{
// m_log.DebugFormat("{0} JoinRoom. New joinReq for room {1}", LogHeader, RoomId);
// Discovered that AudioBridge doesn't care if the data portion is present
// and, if removed, the viewer complains that the "m=" sections are
// out of order. Not "cleaning" (removing the data section) seems to work.
// string cleanSdp = CleanupSdp(pSdp);
var joinReq = new AudioBridgeJoinRoomReq(RoomId, pVSession.AgentId.ToString());
// joinReq.SetJsep("offer", cleanSdp);
joinReq.SetJsep("offer", pVSession.Offer);
JanusMessageResp resp = await _AudioBridge.SendPluginMsg(joinReq);
AudioBridgeJoinRoomResp joinResp = new AudioBridgeJoinRoomResp(resp);
if (joinResp is not null && joinResp.AudioBridgeReturnCode == "joined")
{
pVSession.ParticipantId = joinResp.ParticipantId;
pVSession.Answer = joinResp.Jsep;
ret = true;
m_log.DebugFormat("{0} JoinRoom. Joined room {1}. Participant={2}", LogHeader, RoomId, pVSession.ParticipantId);
}
else
{
m_log.ErrorFormat("{0} JoinRoom. Failed to join room {1}. Resp={2}", LogHeader, RoomId, joinResp.ToString());
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} JoinRoom. Exception {1}", LogHeader, e);
}
return ret;
}
// TODO: this doesn't work.
// Not sure if it is needed. Janus generates Hangup events when the viewer leaves.
/*
public async Task<bool> Hangup(JanusViewerSession pAttendeeSession)
{
bool ret = false;
try
{
}
catch (Exception e)
{
m_log.ErrorFormat("{0} LeaveRoom. Exception {1}", LogHeader, e);
}
return ret;
}
*/
public async Task<bool> LeaveRoom(JanusViewerSession pAttendeeSession)
{
bool ret = false;
try
{
JanusMessageResp resp = await _AudioBridge.SendPluginMsg(
new AudioBridgeLeaveRoomReq(RoomId, pAttendeeSession.ParticipantId));
}
catch (Exception e)
{
m_log.ErrorFormat("{0} LeaveRoom. Exception {1}", LogHeader, e);
}
return ret;
}
}
}

View File

@@ -0,0 +1,658 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Mime;
using System.Reflection;
using System.Threading.Tasks;
using OpenMetaverse.StructuredData;
using log4net;
using log4net.Core;
using System.Reflection.Metadata;
using System.Threading;
namespace osWebRtcVoice
{
// Encapsulization of a Session to the Janus server
public class JanusSession : IDisposable
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[JANUS SESSION]";
// Set to 'true' to get the messages send and received from Janus
private bool _MessageDetails = false;
private string _JanusServerURI = String.Empty;
private string _JanusAPIToken = String.Empty;
private string _JanusAdminURI = String.Empty;
private string _JanusAdminToken = String.Empty;
public string JanusServerURI => _JanusServerURI;
public string JanusAdminURI => _JanusAdminURI;
public string SessionId { get; private set; }
public string SessionUri { get ; private set ; }
public string PluginId { get; set; }
private CancellationTokenSource _CancelTokenSource = new CancellationTokenSource();
private HttpClient _HttpClient = new HttpClient();
public bool IsConnected { get; set; }
// Wrapper around the session connection to Janus-gateway
public JanusSession(string pServerURI, string pAPIToken, string pAdminURI, string pAdminToken, bool pDebugMessages = false)
{
m_log.DebugFormat("{0} JanusSession constructor", LogHeader);
_JanusServerURI = pServerURI;
_JanusAPIToken = pAPIToken;
_JanusAdminURI = pAdminURI;
_JanusAdminToken = pAdminToken;
_MessageDetails = pDebugMessages;
}
public void Dispose()
{
ClearEventSubscriptions();
if (IsConnected)
{
_ = DestroySession();
}
if (_HttpClient is not null)
{
_HttpClient.Dispose();
_HttpClient = null;
}
}
/// <summary>
/// Make the create session request to the Janus server, get the
/// sessionID and return TRUE if successful.
/// </summary>
/// <returns>TRUE if session was created successfully</returns>
public async Task<bool> CreateSession()
{
bool ret = false;
try
{
var resp = await SendToJanus(new CreateSessionReq());
if (resp is not null && resp.isSuccess)
{
var sessionResp = new CreateSessionResp(resp);
SessionId = sessionResp.returnedId;
IsConnected = true;
SessionUri = _JanusServerURI + "/" + SessionId;
m_log.DebugFormat("{0} CreateSession. Created. ID={1}, URL={2}", LogHeader, SessionId, SessionUri);
ret = true;
StartLongPoll();
}
else
{
m_log.ErrorFormat("{0} CreateSession: failed", LogHeader);
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} CreateSession: exception {1}", LogHeader, e);
}
return ret;
}
public async Task<bool> DestroySession()
{
bool ret = false;
try
{
JanusMessageResp resp = await SendToSession(new DestroySessionReq());
if (resp is not null && resp.isSuccess)
{
// Note that setting IsConnected to false will cause the long poll to exit
m_log.DebugFormat("{0} DestroySession. Destroyed", LogHeader);
}
else
{
if (resp.isError)
{
ErrorResp eResp = new ErrorResp(resp);
switch (eResp.errorCode)
{
case 458:
// This is the error code for a session that is already destroyed
m_log.DebugFormat("{0} DestroySession: session already destroyed", LogHeader);
break;
case 459:
// This is the error code for handle already destroyed
m_log.DebugFormat("{0} DestroySession: Handle not found", LogHeader);
break;
default:
m_log.ErrorFormat("{0} DestroySession: failed {1}", LogHeader, eResp.errorReason);
break;
}
}
else
{
m_log.ErrorFormat("{0} DestroySession: failed. Resp: {1}", LogHeader, resp.ToString());
}
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} DestroySession: exception {1}", LogHeader, e);
}
IsConnected = false;
_CancelTokenSource.Cancel();
return ret;
}
// ====================================================================
public async Task<JanusMessageResp> TrickleCandidates(JanusViewerSession pVSession, OSDArray pCandidates)
{
JanusMessageResp ret = null;
// if the audiobridge is active, the trickle message is sent to it
if (pVSession.AudioBridge is null)
{
ret = await SendToJanusNoWait(new TrickleReq(pVSession));
}
else
{
ret = await SendToJanusNoWait(new TrickleReq(pVSession), pVSession.AudioBridge.PluginUri);
}
return ret;
}
// ====================================================================
public async Task<JanusMessageResp> TrickleCompleted(JanusViewerSession pVSession)
{
JanusMessageResp ret = null;
// if the audiobridge is active, the trickle message is sent to it
if (pVSession.AudioBridge is null)
{
ret = await SendToJanusNoWait(new TrickleReq(pVSession));
}
else
{
ret = await SendToJanusNoWait(new TrickleReq(pVSession), pVSession.AudioBridge.PluginUri);
}
return ret;
}
// ====================================================================
public Dictionary<string, JanusPlugin> _Plugins = new Dictionary<string, JanusPlugin>();
public void AddPlugin(JanusPlugin pPlugin)
{
_Plugins.Add(pPlugin.PluginName, pPlugin);
}
// ====================================================================
// Post to the session
public async Task<JanusMessageResp> SendToSession(JanusMessageReq pReq)
{
return await SendToJanus(pReq, SessionUri);
}
private class OutstandingRequest
{
public string TransactionId;
public DateTime RequestTime;
public TaskCompletionSource<JanusMessageResp> TaskCompletionSource;
}
private Dictionary<string, OutstandingRequest> _OutstandingRequests = new Dictionary<string, OutstandingRequest>();
// Send a request directly to the Janus server.
// NOTE: this is probably NOT what you want to do. This is a direct call that is outside the session.
private async Task<JanusMessageResp> SendToJanus(JanusMessageReq pReq)
{
return await SendToJanus(pReq, _JanusServerURI);
}
/// <summary>
/// Send a request to the Janus server. This is the basic call that sends a request to the server.
/// The transaction ID is used to match the response to the request.
/// If the request returns an 'ack' response, the code waits for the matching event
/// before returning the response.
/// </summary>
/// <param name="pReq"></param>
/// <param name="pURI"></param>
/// <returns></returns>
public async Task<JanusMessageResp> SendToJanus(JanusMessageReq pReq, string pURI)
{
AddJanusHeaders(pReq);
// m_log.DebugFormat("{0} SendToJanus", LogHeader);
if (_MessageDetails) m_log.DebugFormat("{0} SendToJanus. URI={1}, req={2}", LogHeader, pURI, pReq.ToJson());
JanusMessageResp ret = null;
try
{
OutstandingRequest outReq = new OutstandingRequest
{
TransactionId = pReq.TransactionId,
RequestTime = DateTime.Now,
TaskCompletionSource = new TaskCompletionSource<JanusMessageResp>()
};
_OutstandingRequests.Add(pReq.TransactionId, outReq);
string reqStr = pReq.ToJson();
HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Post, pURI);
reqMsg.Content = new StringContent(reqStr, System.Text.Encoding.UTF8, MediaTypeNames.Application.Json);
reqMsg.Headers.Add("Accept", "application/json");
HttpResponseMessage response = await _HttpClient.SendAsync(reqMsg, _CancelTokenSource.Token);
if (response.IsSuccessStatusCode)
{
string respStr = await response.Content.ReadAsStringAsync();
ret = JanusMessageResp.FromJson(respStr);
if (ret.CheckReturnCode("ack"))
{
// Some messages are asynchronous and completed with an event
if (_MessageDetails) m_log.DebugFormat("{0} SendToJanus: ack response {1}", LogHeader, respStr);
if (_OutstandingRequests.TryGetValue(pReq.TransactionId, out OutstandingRequest outstandingRequest))
{
ret = await outstandingRequest.TaskCompletionSource.Task;
_OutstandingRequests.Remove(pReq.TransactionId);
}
// If there is no OutstandingRequest, the request was not waiting for an event or already processed
}
else
{
// If the response is not an ack, that means a synchronous request/response so return the response
_OutstandingRequests.Remove(pReq.TransactionId);
if (_MessageDetails) m_log.DebugFormat("{0} SendToJanus: response {1}", LogHeader, respStr);
}
}
else
{
m_log.ErrorFormat("{0} SendToJanus: response not successful {1}", LogHeader, response);
_OutstandingRequests.Remove(pReq.TransactionId);
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} SendToJanus: exception {1}", LogHeader, e.Message);
}
return ret;
}
/// <summary>
/// Send a request to the Janus server but we just return the response and don't wait for any
/// event or anything.
/// There are some requests that are just fire-and-forget.
/// </summary>
/// <param name="pReq"></param>
/// <returns></returns>
private async Task<JanusMessageResp> SendToJanusNoWait(JanusMessageReq pReq, string pURI)
{
JanusMessageResp ret = new JanusMessageResp();
AddJanusHeaders(pReq);
try {
HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Post, pURI);
string reqStr = pReq.ToJson();
reqMsg.Content = new StringContent(reqStr, System.Text.Encoding.UTF8, MediaTypeNames.Application.Json);
reqMsg.Headers.Add("Accept", "application/json");
HttpResponseMessage response = await _HttpClient.SendAsync(reqMsg);
string respStr = await response.Content.ReadAsStringAsync();
ret = JanusMessageResp.FromJson(respStr);
}
catch (Exception e)
{
m_log.ErrorFormat("{0} SendToJanusNoWait: exception {1}", LogHeader, e.Message);
}
return ret;
}
private async Task<JanusMessageResp> SendToJanusNoWait(JanusMessageReq pReq)
{
return await SendToJanusNoWait(pReq, SessionUri);
}
// There are various headers that are in most Janus requests. Add them here.
private void AddJanusHeaders(JanusMessageReq pReq)
{
// Authentication token
if (!String.IsNullOrEmpty(_JanusAPIToken))
{
pReq.AddAPIToken(_JanusAPIToken);
}
// Transaction ID that matches responses to requests
if (String.IsNullOrEmpty(pReq.TransactionId))
{
pReq.TransactionId = Guid.NewGuid().ToString();
}
// The following two are required for the WebSocket interface. They are optional for the
// HTTP interface since the session and plugin handle are in the URL.
// SessionId is added to the message if not already there
if (!pReq.hasSessionId && !String.IsNullOrEmpty(SessionId))
{
pReq.AddSessionId(SessionId);
}
// HandleId connects to the plugin
if (!pReq.hasHandleId && !String.IsNullOrEmpty(PluginId))
{
pReq.AddHandleId(PluginId);
}
}
bool TryGetOutstandingRequest(string pTransactionId, out OutstandingRequest pOutstandingRequest)
{
if (String.IsNullOrEmpty(pTransactionId))
{
pOutstandingRequest = null;
return false;
}
bool ret = false;
lock (_OutstandingRequests)
{
if (_OutstandingRequests.TryGetValue(pTransactionId, out pOutstandingRequest))
{
_OutstandingRequests.Remove(pTransactionId);
ret = true;
}
}
return ret;
}
public Task<JanusMessageResp> SendToJanusAdmin(JanusMessageReq pReq)
{
return SendToJanus(pReq, _JanusAdminURI);
}
public Task<JanusMessageResp> GetFromJanus()
{
return GetFromJanus(_JanusServerURI);
}
/// <summary>
/// Do a GET to the Janus server and return the response.
/// If the response is an HTTP error, we return fake JanusMessageResp with the error.
/// </summary>
/// <param name="pURI"></param>
/// <returns></returns>
public async Task<JanusMessageResp> GetFromJanus(string pURI)
{
if (!String.IsNullOrEmpty(_JanusAPIToken))
{
pURI += "?apisecret=" + _JanusAPIToken;
}
JanusMessageResp ret = null;
try
{
// m_log.DebugFormat("{0} GetFromJanus: URI = \"{1}\"", LogHeader, pURI);
HttpRequestMessage reqMsg = new HttpRequestMessage(HttpMethod.Get, pURI);
reqMsg.Headers.Add("Accept", "application/json");
HttpResponseMessage response = null;
try
{
response = await _HttpClient.SendAsync(reqMsg, _CancelTokenSource.Token);
if (response is not null && response.IsSuccessStatusCode)
{
string respStr = await response.Content.ReadAsStringAsync();
ret = JanusMessageResp.FromJson(respStr);
// m_log.DebugFormat("{0} GetFromJanus: response {1}", LogHeader, respStr);
}
else
{
m_log.ErrorFormat("{0} GetFromJanus: response not successful {1}", LogHeader, response);
var eResp = new ErrorResp("GETERROR");
// Add the sessionId so the proper session can be shut down
eResp.AddSessionId(SessionId);
if (response is not null)
{
eResp.SetError((int)response.StatusCode, response.ReasonPhrase);
}
else
{
eResp.SetError(0, "Connection refused");
}
ret = eResp;
}
}
catch (TaskCanceledException e)
{
m_log.DebugFormat("{0} GetFromJanus: task canceled: {1}", LogHeader, e.Message);
var eResp = new ErrorResp("GETERROR");
eResp.SetError(499, "Task canceled");
ret = eResp;
}
catch (Exception e)
{
m_log.ErrorFormat("{0} GetFromJanus: exception {1}", LogHeader, e.Message);
var eResp = new ErrorResp("GETERROR");
eResp.SetError(400, "Exception: " + e.Message);
ret = eResp;
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} GetFromJanus: exception {1}", LogHeader, e);
var eResp = new ErrorResp("GETERROR");
eResp.SetError(400, "Exception: " + e.Message);
ret = eResp;
}
return ret;
}
// ====================================================================
public delegate void JanusEventHandler(EventResp pResp);
// Not all the events are used. CS0067 is to suppress the warning that the event is not used.
#pragma warning disable CS0067,CS0414
public event JanusEventHandler OnKeepAlive;
public event JanusEventHandler OnServerInfo;
public event JanusEventHandler OnTrickle;
public event JanusEventHandler OnHangup;
public event JanusEventHandler OnDetached;
public event JanusEventHandler OnError;
public event JanusEventHandler OnEvent;
public event JanusEventHandler OnMessage;
public event JanusEventHandler OnJoined;
public event JanusEventHandler OnLeaving;
public event JanusEventHandler OnDisconnect;
#pragma warning restore CS0067,CS0414
public void ClearEventSubscriptions()
{
OnKeepAlive = null;
OnServerInfo = null;
OnTrickle = null;
OnHangup = null;
OnDetached = null;
OnError = null;
OnEvent = null;
OnMessage = null;
OnJoined = null;
OnLeaving = null;
OnDisconnect = null;
}
// ====================================================================
/// <summary>
/// In the REST API, events are returned by a long poll. This
/// starts the poll and calls the registed event handler when
/// an event is received.
/// </summary>
private void StartLongPoll()
{
bool running = true;
m_log.DebugFormat("{0} EventLongPoll", LogHeader);
Task.Run(async () => {
while (running && IsConnected)
{
try
{
var resp = await GetFromJanus(SessionUri);
if (resp is not null)
{
_ = Task.Run(() =>
{
EventResp eventResp = new EventResp(resp);
switch (resp.ReturnCode)
{
case "keepalive":
// These should happen every 30 seconds
// m_log.DebugFormat("{0} EventLongPoll: keepalive {1}", LogHeader, resp.ToString());
break;
case "server_info":
// Just info on the Janus instance
m_log.DebugFormat("{0} EventLongPoll: server_info {1}", LogHeader, resp.ToString());
break;
case "ack":
// 'ack' says the request was received and an event will follow
if (_MessageDetails) m_log.DebugFormat("{0} EventLongPoll: ack {1}", LogHeader, resp.ToString());
break;
case "success":
// success is a sync response that says the request was completed
if (_MessageDetails) m_log.DebugFormat("{0} EventLongPoll: success {1}", LogHeader, resp.ToString());
break;
case "trickle":
// got a trickle ICE candidate from Janus
// this is for reverse communication from Janus to the client and we don't do that
if (_MessageDetails) m_log.DebugFormat("{0} EventLongPoll: trickle {1}", LogHeader, resp.ToString());
OnTrickle?.Invoke(eventResp);
break;
case "webrtcup":
// ICE and DTLS succeeded, and so Janus correctly established a PeerConnection with the user/application;
m_log.DebugFormat("{0} EventLongPoll: webrtcup {1}", LogHeader, resp.ToString());
break;
case "hangup":
// The PeerConnection was closed, either by the user/application or by Janus itself;
// If one is in the room, when a "hangup" event happens, it means that the user left the room.
m_log.DebugFormat("{0} EventLongPoll: hangup {1}", LogHeader, resp.ToString());
OnHangup?.Invoke(eventResp);
break;
case "detached":
// a plugin asked the core to detach one of our handles
m_log.DebugFormat("{0} EventLongPoll: event {1}", LogHeader, resp.ToString());
OnDetached?.Invoke(eventResp);
break;
case "media":
// Janus is receiving (receiving: true/false) audio/video (type: "audio/video") on this PeerConnection;
m_log.DebugFormat("{0} EventLongPoll: media {1}", LogHeader, resp.ToString());
break;
case "slowlink":
// Janus detected a slowlink (uplink: true/false) on this PeerConnection;
m_log.DebugFormat("{0} EventLongPoll: slowlink {1}", LogHeader, resp.ToString());
break;
case "error":
m_log.DebugFormat("{0} EventLongPoll: error {1}", LogHeader, resp.ToString());
if (TryGetOutstandingRequest(resp.TransactionId, out OutstandingRequest outstandingRequest))
{
outstandingRequest.TaskCompletionSource.SetResult(resp);
}
else
{
OnError?.Invoke(eventResp);
m_log.ErrorFormat("{0} EventLongPoll: error with no transaction. {1}", LogHeader, resp.ToString());
}
break;
case "event":
if (_MessageDetails) m_log.DebugFormat("{0} EventLongPoll: event {1}", LogHeader, resp.ToString());
if (TryGetOutstandingRequest(resp.TransactionId, out OutstandingRequest outstandingRequest2))
{
// Someone is waiting for this event
outstandingRequest2.TaskCompletionSource.SetResult(resp);
}
else
{
m_log.ErrorFormat("{0} EventLongPoll: event no outstanding request {1}", LogHeader, resp.ToString());
OnEvent?.Invoke(eventResp);
}
break;
case "message":
m_log.DebugFormat("{0} EventLongPoll: message {1}", LogHeader, resp.ToString());
OnMessage?.Invoke(eventResp);
break;
case "timeout":
// Events for the audio bridge
m_log.DebugFormat("{0} EventLongPoll: timeout {1}", LogHeader, resp.ToString());
break;
case "joined":
// Events for the audio bridge
OnJoined?.Invoke(eventResp);
m_log.DebugFormat("{0} EventLongPoll: joined {1}", LogHeader, resp.ToString());
break;
case "leaving":
// Events for the audio bridge
OnLeaving?.Invoke(eventResp);
m_log.DebugFormat("{0} EventLongPoll: leaving {1}", LogHeader, resp.ToString());
break;
case "GETERROR":
// Special error response from the GET
var errorResp = new ErrorResp(resp);
switch (errorResp.errorCode)
{
case 404:
// "Not found" means there is a Janus server but the session is gone
m_log.ErrorFormat("{0} EventLongPoll: GETERROR Not Found. URI={1}: {2}",
LogHeader, SessionUri, resp.ToString());
break;
case 400:
// "Bad request" means the session is gone
m_log.ErrorFormat("{0} EventLongPoll: Bad Request. URI={1}: {2}",
LogHeader, SessionUri, resp.ToString());
break;
case 499:
// "Task canceled" means the long poll was canceled
m_log.DebugFormat("{0} EventLongPoll: Task canceled. URI={1}", LogHeader, SessionUri);
break;
default:
m_log.DebugFormat("{0} EventLongPoll: unknown response. URI={1}: {2}",
LogHeader, SessionUri, resp.ToString());
break;
}
// This will cause the long poll to exit
running = false;
OnDisconnect?.Invoke(eventResp);
break;
default:
m_log.DebugFormat("{0} EventLongPoll: unknown response {1}", LogHeader, resp.ToString());
break;
}
});
}
else
{
m_log.ErrorFormat("{0} EventLongPoll: failed. Response is null", LogHeader);
}
}
catch (Exception e)
{
// This will cause the long poll to exit
running = false;
m_log.ErrorFormat("{0} EventLongPoll: exception {1}", LogHeader, e);
}
}
m_log.InfoFormat("{0} EventLongPoll: Exiting long poll loop", LogHeader);
});
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Reflection;
using System.Threading.Tasks;
using OMV = OpenMetaverse;
using OpenMetaverse.StructuredData;
using log4net;
namespace osWebRtcVoice
{
public class JanusViewerSession : IVoiceViewerSession
{
protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected static readonly string LogHeader = "[JANUS VIEWER SESSION]";
// 'viewer_session' that is passed to and from the viewer
// IVoiceViewerSession.ViewerSessionID
public string ViewerSessionID { get; set; }
// IVoiceViewerSession.VoiceService
public IWebRtcVoiceService VoiceService { get; set; }
// The Janus server keeps track of the user by this ID
// IVoiceViewerSession.VoiceServiceSessionId
public string VoiceServiceSessionId { get; set; }
// IVoiceViewerSession.RegionId
public OMV.UUID RegionId { get; set; }
// IVoiceViewerSession.AgentId
public OMV.UUID AgentId { get; set; }
// Janus keeps track of the user by this ID
public int ParticipantId { get; set; }
// Connections to the Janus server
public JanusSession Session { get; set; }
public JanusAudioBridge AudioBridge { get; set; }
public JanusRoom Room { get; set; }
// This keeps copies of the offer/answer incase we need to resend
public string OfferOrig { get; set; }
public string Offer { get; set; }
// Contains "type" and "sdp" fields
public OSDMap Answer { get; set; }
public JanusViewerSession(IWebRtcVoiceService pVoiceService)
{
ViewerSessionID = OMV.UUID.Random().ToString();
VoiceService = pVoiceService;
m_log.Debug($"{LogHeader} JanusViewerSession created {ViewerSessionID}");
}
public JanusViewerSession(string pViewerSessionID, IWebRtcVoiceService pVoiceService)
{
ViewerSessionID = pViewerSessionID;
VoiceService = pVoiceService;
m_log.Debug($"{LogHeader} JanusViewerSession created {ViewerSessionID}");
}
// Send the messages to the voice service to try and get rid of the session
// IVoiceViewerSession.Shutdown
public async Task Shutdown()
{
m_log.DebugFormat($"{LogHeader} JanusViewerSession shutdown {ViewerSessionID}");
if (Room is not null)
{
var rm = Room;
Room = null;
await rm.LeaveRoom(this);
}
if (AudioBridge is not null)
{
var ab = AudioBridge;
AudioBridge = null;
await ab.Detach();
}
if (Session is not null)
{
var s = Session;
Session = null;
_ = await s.DestroySession().ConfigureAwait(false);
s.Dispose();
}
}
}
}

View File

@@ -0,0 +1,478 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using System.Threading.Tasks;
using OpenSim.Framework;
using OpenSim.Services.Base;
using OpenMetaverse.StructuredData;
using OpenMetaverse;
using Nini.Config;
using log4net;
namespace osWebRtcVoice
{
public class WebRtcJanusService : ServiceBase, IWebRtcVoiceService
{
private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[JANUS WEBRTC SERVICE]";
private readonly IConfigSource _Config;
private bool _Enabled = false;
private string _JanusServerURI = string.Empty;
private string _JanusAPIToken = string.Empty;
private string _JanusAdminURI = string.Empty;
private string _JanusAdminToken = string.Empty;
private bool _MessageDetails = false;
// An extra "viewer session" that is created initially. Used to verify the service
// is working and for a handle for the console commands.
private JanusViewerSession _ViewerSession;
public WebRtcJanusService(IConfigSource pConfig) : base(pConfig)
{
Assembly assembly = Assembly.GetExecutingAssembly();
string version = assembly.GetName().Version?.ToString() ?? "unknown";
_log.DebugFormat("{0} WebRtcJanusService version {1}", LogHeader, version);
_Config = pConfig;
IConfig webRtcVoiceConfig = _Config.Configs["WebRtcVoice"];
if (webRtcVoiceConfig is not null)
{
_Enabled = webRtcVoiceConfig.GetBoolean("Enabled", false);
IConfig janusConfig = _Config.Configs["JanusWebRtcVoice"];
if (_Enabled && janusConfig is not null)
{
_JanusServerURI = janusConfig.GetString("JanusGatewayURI", string.Empty);
_JanusAPIToken = janusConfig.GetString("APIToken", string.Empty);
_JanusAdminURI = janusConfig.GetString("JanusGatewayAdminURI", string.Empty);
_JanusAdminToken = janusConfig.GetString("AdminAPIToken", string.Empty);
// Debugging options
_MessageDetails = janusConfig.GetBoolean("MessageDetails", false);
if (string.IsNullOrEmpty(_JanusServerURI) || string.IsNullOrEmpty(_JanusAPIToken) ||
string.IsNullOrEmpty(_JanusAdminURI) || string.IsNullOrEmpty(_JanusAdminToken))
{
_log.Error($"{LogHeader} JanusWebRtcVoice configuration section missing required fields");
_Enabled = false;
}
if (_Enabled)
{
if(!StartConnectionToJanus())
{
_log.Error($"{LogHeader} failed connection to Janus Gateway. Disabled");
_Enabled=false;
return;
}
RegisterConsoleCommands();
_log.Info($"{LogHeader} Enabled");
}
}
else
{
_log.Error($"{LogHeader} No JanusWebRtcVoice configuration section");
_Enabled = false;
}
}
else
{
_log.Error($"{LogHeader} No WebRtcVoice configuration section");
_Enabled = false;
}
}
// Here an initial session is created and then a handle to the audio bridge plugin
// is created for the console commands. Since webrtc PeerConnections that are created
// my Janus are per-session, the other sessions will be created by the viewer requests.
private bool StartConnectionToJanus()
{
_log.DebugFormat("{0} StartConnectionToJanus", LogHeader);
_ViewerSession = new JanusViewerSession(this);
//bad
return ConnectToSessionAndAudioBridge(_ViewerSession).Result;
}
private async Task<bool> ConnectToSessionAndAudioBridge(JanusViewerSession pViewerSession)
{
JanusSession janusSession = new JanusSession(_JanusServerURI, _JanusAPIToken, _JanusAdminURI, _JanusAdminToken, _MessageDetails);
if (await janusSession.CreateSession().ConfigureAwait(false))
{
_log.DebugFormat("{0} JanusSession created", LogHeader);
// Once the session is created, create a handle to the plugin for rooms
JanusAudioBridge audioBridge = new JanusAudioBridge(janusSession);
if (await audioBridge.Activate(_Config).ConfigureAwait(false))
{
_log.Debug($"{LogHeader} AudioBridgePluginHandle created");
// Requests through the capabilities will create rooms
janusSession.AddPlugin(audioBridge);
pViewerSession.VoiceServiceSessionId = janusSession.SessionId;
pViewerSession.Session = janusSession;
pViewerSession.AudioBridge = audioBridge;
janusSession.OnDisconnect += Handle_Hangup;
janusSession.OnHangup += Handle_Hangup;
return true;
}
_log.Error($"{LogHeader} JanusPluginHandle not created");
}
_log.Error($"{LogHeader} JanusSession not created");
return false;
}
private void Handle_Hangup(EventResp pResp)
{
if (pResp is not null)
{
var sessionId = pResp.sessionId;
_log.Debug($"{LogHeader} Handle_Hangup: {pResp.RawBody}, sessionId={sessionId}");
if (VoiceViewerSession.TryGetViewerSessionByVSSessionId(sessionId, out IVoiceViewerSession viewerSession))
{
// There is a viewer session associated with this session
DisconnectViewerSession(viewerSession as JanusViewerSession);
}
else
{
_log.Debug($"{LogHeader} Handle_Hangup: no session found. SessionId={sessionId}");
}
}
}
// Disconnect the viewer session. This is called when the viewer logs out or hangs up.
private void DisconnectViewerSession(JanusViewerSession pViewerSession)
{
if (pViewerSession is not null)
{
Task.Run(() =>
{
VoiceViewerSession.RemoveViewerSession(pViewerSession.ViewerSessionID);
// No need to wait for the session to be shutdown
_ = pViewerSession.Shutdown();
});
}
}
// The pRequest parameter is a straight conversion of the JSON request from the client.
// This is the logic that takes the client's request and converts it into
// operations on rooms in the audio bridge.
// IWebRtcVoiceService.ProvisionVoiceAccountRequest
public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
return ProvisionVoiceAccountRequestBAD(pSession, pRequest, pUserID, pSceneID).Result;
}
public async Task<OSDMap> ProvisionVoiceAccountRequestBAD(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
OSDMap ret = null;
string errorMsg = null;
JanusViewerSession viewerSession = pSession as JanusViewerSession;
if (viewerSession is not null)
{
if (viewerSession.Session is null)
{
// This is a new session so we must create a new session and handle to the audio bridge
await ConnectToSessionAndAudioBridge(viewerSession).ConfigureAwait(false);
}
// TODO: need to keep count of users in a room to know when to close a room
bool isLogout = pRequest.TryGetBool("logout", out bool lgout) && lgout;
if (isLogout)
{
// The client is logging out. Exit the room.
if (viewerSession.Room is not null)
{
await viewerSession.Room.LeaveRoom(viewerSession);
viewerSession.Room = null;
}
return new OSDMap
{
{ "response", "closed" }
};
}
// Get the parameters that select the room
// To get here, voice_server_type has already been checked to be 'webrtc' and channel_type='local'
int parcel_local_id = pRequest.TryGetInt("parcel_local_id", out int pli) ? pli : JanusAudioBridge.REGION_ROOM_ID;
string channel_id = pRequest.TryGetString("channel_id", out string cli) ? cli : string.Empty;
string channel_credentials = pRequest.TryGetString("credentials", out string cred) ? cred : string.Empty;
string channel_type = pRequest["channel_type"].AsString();
bool isSpatial = channel_type == "local";
string voice_server_type = pRequest["voice_server_type"].AsString();
_log.DebugFormat("{0} ProvisionVoiceAccountRequest: parcel_id={1} channel_id={2} channel_type={3} voice_server_type={4}", LogHeader, parcel_local_id, channel_id, channel_type, voice_server_type);
if (pRequest.TryGetOSDMap("jsep", out OSDMap jsep))
{
// The jsep is the SDP from the client. This is the client's request to connect to the audio bridge.
string jsepType = jsep["type"].AsString();
string jsepSdp = jsep["sdp"].AsString();
if (jsepType == "offer")
{
// The client is sending an offer. Find the right room and join it.
// _log.DebugFormat("{0} ProvisionVoiceAccountRequest: jsep type={1} sdp={2}", LogHeader, jsepType, jsepSdp);
viewerSession.Room = await viewerSession.AudioBridge.SelectRoom(pSceneID.ToString(),
channel_type, isSpatial, parcel_local_id, channel_id).ConfigureAwait(false);
if (viewerSession.Room is null)
{
errorMsg = "room selection failed";
_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: room selection failed");
}
else {
viewerSession.Offer = jsepSdp;
viewerSession.OfferOrig = jsepSdp;
viewerSession.AgentId = pUserID;
if (await viewerSession.Room.JoinRoom(viewerSession).ConfigureAwait(false))
{
ret = new OSDMap
{
{ "jsep", viewerSession.Answer },
{ "viewer_session", viewerSession.ViewerSessionID }
};
}
else
{
errorMsg = "JoinRoom failed";
_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: JoinRoom failed");
}
}
}
else
{
errorMsg = "jsep type not offer";
_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: jsep type={jsepType} not offer");
}
}
else
{
errorMsg = "no jsep";
_log.Debug($"{LogHeader} ProvisionVoiceAccountRequest: no jsep. req={pRequest}");
}
}
else
{
errorMsg = "viewersession not JanusViewerSession";
_log.Error("{LogHeader} ProvisionVoiceAccountRequest: viewersession not JanusViewerSession");
}
if (!string.IsNullOrEmpty(errorMsg) && ret is null)
{
// The provision failed so build an error messgage to return
ret = new OSDMap
{
{ "response", "failed" },
{ "error", errorMsg }
};
}
return ret;
}
// IWebRtcVoiceService.VoiceAccountBalanceRequest
public OSDMap VoiceSignalingRequest(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
return VoiceSignalingRequestBAD(pSession, pRequest, pUserID, pSceneID).Result;
}
public async Task<OSDMap> VoiceSignalingRequestBAD(IVoiceViewerSession pSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
OSDMap ret = null;
JanusViewerSession viewerSession = pSession as JanusViewerSession;
JanusMessageResp resp = null;
if (viewerSession is not null)
{
// The request should be an array of candidates
if (pRequest.TryGetOSDMap("candidate", out OSDMap candidate))
{
if (candidate.TryGetBool("completed", out bool iscompleted) && iscompleted)
{
// The client has finished sending candidates
resp = await viewerSession.Session.TrickleCompleted(viewerSession).ConfigureAwait(false);
_log.DebugFormat($"{LogHeader} VoiceSignalingRequest: candidate completed");
}
else
{
}
}
else if (pRequest.TryGetOSDArray("candidates", out OSDArray candidates))
{
OSDArray candidatesArray = new OSDArray();
foreach (OSDMap cand in candidates)
{
candidatesArray.Add(new OSDMap() {
{ "candidate", cand["candidate"].AsString() },
{ "sdpMid", cand["sdpMid"].AsString() },
{ "sdpMLineIndex", cand["sdpMLineIndex"].AsLong() }
});
}
resp = await viewerSession.Session.TrickleCandidates(viewerSession, candidatesArray).ConfigureAwait(false);
_log.Debug($"{LogHeader} VoiceSignalingRequest: {candidatesArray.Count} candidates");
}
else
{
_log.Error($"{LogHeader} VoiceSignalingRequest: no 'candidate' or 'candidates'");
}
}
if (resp is null)
{
_log.ErrorFormat($"{LogHeader} VoiceSignalingRequest: no response so returning error");
ret = new OSDMap
{
{ "response", "error" }
};
}
else
{
ret = resp.RawBody;
}
return ret;
}
// This module should not be invoked with this signature
// IWebRtcVoiceService.ProvisionVoiceAccountRequest
public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
throw new NotImplementedException();
}
// This module should not be invoked with this signature
// IWebRtcVoiceService.VoiceSignalingRequest
public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
throw new NotImplementedException();
}
// The viewer session object holds all the connection information to Janus.
// IWebRtcVoiceService.CreateViewerSession
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
return new JanusViewerSession(this)
{
AgentId = pUserID,
RegionId = pSceneID
};
}
// ======================================================================================================
private void RegisterConsoleCommands()
{
if (_Enabled) {
MainConsole.Instance.Commands.AddCommand("Webrtc", false, "janus info",
"janus info",
"Show Janus server information",
HandleJanusInfo);
MainConsole.Instance.Commands.AddCommand("Webrtc", false, "janus list rooms",
"janus list rooms",
"List the rooms on the Janus server",
HandleJanusListRooms);
// List rooms
// List participants in a room
}
}
private void HandleJanusInfo(string module, string[] cmdparms)
{
if (_ViewerSession is not null && _ViewerSession.Session is not null)
{
WriteOut("{0} Janus session: {1}", LogHeader, _ViewerSession.Session.SessionId);
string infoURI = _ViewerSession.Session.JanusServerURI + "/info";
var resp = _ViewerSession.Session.GetFromJanus(infoURI).Result;
if (resp is not null)
MainConsole.Instance.Output(resp.ToJson());
}
}
private void HandleJanusListRooms(string module, string[] cmdparms)
{
if (_ViewerSession is not null && _ViewerSession.Session is not null && _ViewerSession.AudioBridge is not null)
{
var ab = _ViewerSession.AudioBridge;
var resp = ab.SendAudioBridgeMsg(new AudioBridgeListRoomsReq()).Result;
if (resp is not null && resp.isSuccess)
{
if (resp.PluginRespData.TryGetValue("list", out OSD list))
{
MainConsole.Instance.Output("");
MainConsole.Instance.Output(
" {0,10} {1,15} {2,5} {3,10} {4,7} {5,7}",
"Room", "Description", "Num", "SampleRate", "Spatial", "Recording");
foreach (OSDMap room in list as OSDArray)
{
int roomid = room["room"].AsInteger();
MainConsole.Instance.Output(
" {0,10} {1,15} {2,5} {3,10} {4,7} {5,7}",
roomid, room["description"], room["num_participants"],
room["sampling_rate"], room["spatial_audio"], room["record"]);
var participantResp = ab.SendAudioBridgeMsg(new AudioBridgeListParticipantsReq(roomid)).Result;
if (participantResp is not null && participantResp.AudioBridgeReturnCode == "participants")
{
if (participantResp.PluginRespData.TryGetValue("participants", out OSD participants))
{
foreach (OSDMap participant in participants as OSDArray)
{
MainConsole.Instance.Output(" {0}/{1},muted={2},talking={3},pos={4}",
participant["id"].AsLong(), participant["display"], participant["muted"],
participant["talking"], participant["spatial_position"]);
}
}
}
}
}
else
{
MainConsole.Instance.Output("No rooms");
}
}
else
{
MainConsole.Instance.Output("Failed to get room list");
}
}
}
private void WriteOut(string msg, params object[] args)
{
// m_log.InfoFormat(msg, args);
MainConsole.Instance.Output(msg, args);
}
}
}

View File

@@ -0,0 +1,390 @@
os-webrtc-janus original work, by Robert Adams, license changed to same BSD of rest of OpenSimulator
By Robert Adams, for OpenSimulator
Original license:
// Copyright 2024 Robert Adams (misterblue@misterblue.com)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

View File

@@ -0,0 +1,234 @@
# os-webrtc-janus
Addon-module for [OpenSimulator] to provide webrtc voice support
using Janus-gateway.
For an explanation of background and architecture,
this project was presented at the
[OpenSimulator Community Conference] 2024
in the presentation
[WebRTC Voice for OpenSimulator](https://www.youtube.com/watch?v=nL78fieIFYg).
This addon works by taking viewer requests for voice service and
using a separate, external [Janus-Gateway WebRTC server].
This can be configured to allow local region spatial voice
and grid-wide group and spatial voice. See the sections below.
For running that separate Janus server, check out
[os-webrtc-janus-docker] which has instructions for running
Janus-Gateway on Linux and Windows WSL using Docker.
Instructions for:
- [Building into OpenSimulator](#Building): Build OpenSimulator with WebRTC voice service
- [Configuring Simulator for Voice Services](#Configure_Simulator)
- [Configuring Robust Grid Service](#Configure_Robust)
- [Configure Standalone Region](#Configure_Standalone)
- [Managing Voice Service](#Managing_Voice) (console commands, etc)
**Note**: as of January 2024, this solution does not provide true spatial
voice service using Janus. There are people working on additions to Janus
to provide this but the existing solution provides only non-spatial
voice services using the `AudioBridge` Janus plugin. Additionally,
features like muting and individual avatar volume are not yet implemented.
<a id="Known_Issues"></a>
## Known Issues
- No spatial audio
- One can see your own "white dot" but you don't see other avatar's white dots
- No muting
- No individual volume control
And probably more found at [os-webrtc-janus issues](https://github.com/Misterblue/os-webrtc-janus/issues).
<a id="Building"></a>
## Building Plugin into OpenSimulator
`os-webrtc-janus` is integrated as a source build into [OpenSimulator].
It uses the [OpenSimulator] addon-module feature which makes the
build as easy as cloning the `os-webrtc-janus` sources into the
[OpenSimulator] source tree, running the build configuration script,
and then building OpenSimulator.
The steps are:
```
# Get the OpenSimulator sources
git clone git://opensimulator.org/git/opensim
cd opensim # cd into the top level OpenSim directory
# Fetch the WebRtc addon
cd addon-modules
git clone https://github.com/Misterblue/os-webrtc-janus.git
cd ..
# Build the project files
./runprebuild.sh
# Compile OpenSimulator with the webrtc addon
./compile.sh
# Copy the INI file for webrtc into a config dir that is read at boot
mkdir bin/config
cp addon-modules/os-webrtc-janus/os-webrtc-janus.ini bin/config
```
These building steps create several `.dll` files for `os-webrtc-janus`
in `bin/WebRtc*.dll`. Some adventurous people have found that, rather
than building the [OpenSimulator] sources, you can just copy the `.dll`s
into an existing `/bin` directory. Just make sure the `WebRtc*.dll` files
were built on the same version of [OpenSimulator] you are running.
<a id="Configure_Simulator"></a>
## Configure a Region for Voice
The last step in [Building](#Building) copied `os-webrtc-janus.ini` into
the `bin/config` directory. [OpenSimulator] reads all the `.ini` files
in that directory so this copy operation adds the configuration for `os-webrtc-janus`
and this is what needs to be configured for the simulator and region.
The sample `.ini` file has two sections: `[WebRtcVoice]` and `[JanusWebRtcVoice]`.
The `WebRtcVoice` section configures the what services the simulator uses
for WebRtc voice. The `[JanusWebRtcVoice]` section configures any connection
the simulator makes to the Janus server. The latter section is only updated
if this simulator is using a local Janus server for spatial voice.
The values for `SpatialVoiceService` and `NonSpatialVoiceService` point
either directly to a Janus service or to a Robust grid server that is providing
the grid voice service. Both these options are in the sample `os-webrtc-janus.ini`
file and the proper one should be uncommented.
The viewer makes requests for either spatial voice (used in the region and parcels)
or non-spatial voice (used for group chats or person-to-person voice conversations).
`os-webrtc-janus` allows these two types of voice connections to be handled by
different voice services. Thus there are two different configurations:
- all voice service is provided by the grid (both spatial and non-spatial point to a robust service), and
- the region simulator provides a local Janus server for region spatial voice while the grid service is used for group chats
#### Grid Only Voice Services
The most common configuration will be for a simulator that uses the grid supplied
voice services. For this configuration, `os-webrtc-janus.ini` would look like:
```
[WebRtcVoice]
Enabled = true
SpatialVoiceService = WebRtcVoice.dll:WebRtcVoiceServiceConnector
NonSpatialVoiceService = WebRtcVoice.dll:WebRtcVoiceServiceConnector
WebRtcVoiceServerURI = ${Const|PrivURL}:${Const|PrivatePort}
```
This directs both spatial and non-spatial voice to the grid service connector
and `WebRtcVoiceServerURI` points to the configured Robust grid service.
There is no need for a `[JanusWebRtcVoice]` section because all that is handled by the grid services.
#### Local Simulator Janus Service
In a grid setup, there might be a need for a single simulator/region to use its own
Janus server for either privacy or to off-load the grid voice service.
In this configuration, spatial voice is directed to the local Janus service
while the non-spatial voice goes to the grid services to allow grid wide group
chat and region independent person-to-person chat.
This is done with a `os-webrtc-janus.ini` that looks like:
```
[WebRtcVoice]
Enabled = true
SpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
NonSpatialVoiceService = WebRtcVoice.dll:WebRtcVoiceServiceConnector
WebRtcVoiceServerURI = ${Const|PrivURL}:${Const|PrivatePort}
[JanusWebRtcVoice]
JanusGatewayURI = http://janus.example.org:14223/voice
APIToken = APITokenToNeverCheckIn
JanusGatewayAdminURI = http://janus.example.org/admin
AdminAPIToken = AdminAPITokenToNeverCheckIn
```
Notice that, since the simulator has its own Janus service, it must configure the
connection parameters to access that Janus service. The details of running and
configuring a Janus service is provided at [os-webrtc-janus-docker] but, the configuration
here needs to specify the URI to address the Janus server and the API keys
to allow this simulator access to its interfaces. The example above
contains just sample entries.
<a id="Configure_Robust"></a>
## Configure Robust Server for WebRTC Voice
For the grid services side, `os-webrtc-janus` is configured as an additional service
in the Robust OpenSimulator server. The additions to `Robust.ini` are:
```
...
[ServiceList]
...
VoiceServiceConnector = "${Const|PrivatePort}/WebRtcVoice.dll:WebRtcVoiceServerConnector"
...
[WebRtcVoice]
Enabled = true
SpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
NonSpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
[JanusWebRtcVoice]
JanusGatewayURI = http://janus.example.org:14223/voice
APIToken = APITokenToNeverCheckIn
JanusGatewayAdminURI = http://janus.example.org/admin
AdminAPIToken = AdminAPITokenToNeverCheckIn
...
```
This adds `VoiceServiceConnector` to the list of services presented by this Robust server
and adds the WebRtcVoice configuration that says to do both spatial and non-spatial voice
using the Janus server, and the configuration for the Janus server itself.
One can configure multiple Robust services to distribute the load of services
and a Robust server with only `VoiceServiceConnector` in its ServiceList is possible.
<a id="Configure_Standalone"></a>
## Configure Standalone Region
[OpenSimulator] can be run "standalone" where all the grid services and regions are
run in one simulator instance. Adding voice to this configuration is sometimes useful
for very private meetings or testing. For this configuration, a Janus server is set up
and the standalone simulator is configured to point all voice to that Janus server:
```
[WebRtcVoice]
Enabled = true
SpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
NonSpatialVoiceService = WebRtcJanusService.dll:WebRtcJanusService
WebRtcVoiceServerURI = ${Const|PrivURL}:${Const|PrivatePort}
[JanusWebRtcVoice]
JanusGatewayURI = http://janus.example.org:14223/voice
APIToken = APITokenToNeverCheckIn
JanusGatewayAdminURI = http://janus.example.org/admin
AdminAPIToken = AdminAPITokenToNeverCheckIn
```
This directs both spatial and non-spatial voice to the Janus server
and configures the URI address of the Janus server and the API access
keys for that server.
<a id="Managing_Voice"></a>
## Managing Voice (Console commands)
There are a few console commands for checking on and controlling the voice system.
The current list of commands for the simulator can be listed with the
console command `help webrtc`.
This is a growing section and will be added to over time.
**webrtc list sessions** -- not implemented
**janus info** -- list many details of the Janus-Gateway configuration. Very ugly, non-formated JSON.
**janus list rooms** -- list the rooms that have been allocated in the `AudioBridge` Janus plugin
[SecondLife WebRTC Voice]: https://wiki.secondlife.com/wiki/WebRTC_Voice
[OpenSimulator]: http://opensimulator.org
[OpenSimulator Community Conference]: https://conference.opensimulator.org
[os-webrtc-janus]: https://github.com/Misterblue/os-webrtc-janus
[Janus-Gateway WebRTC server]: https://janus.conf.meetecho.com/
[os-webrtc-janus-docker]: https://github.com/Misterblue/os-webrtc-janus-docker

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Threading.Tasks;
using OMV = OpenMetaverse;
namespace osWebRtcVoice
{
/// <summary>
/// This is the interface for the viewer session. It is used to store the
/// state of the viewer session and to disconnect the session when needed.
/// </summary>
public interface IVoiceViewerSession
{
// This ID is passed to and from the viewer to identify the session
public string ViewerSessionID { get; set; }
public IWebRtcVoiceService VoiceService { get; set; }
// THis ID is passed between us and the voice service to idetify the session
public string VoiceServiceSessionId { get; set; }
// The UUID of the region that is being connected to
public OMV.UUID RegionId { get; set; }
// The simulator has a GUID to identify the user
public OMV.UUID AgentId { get; set; }
// Disconnect the connection to the voice service for this session
public Task Shutdown();
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using OpenMetaverse;
using OpenMetaverse.StructuredData;
namespace osWebRtcVoice
{
/// <summary>
/// This is the interface for the voice service. It is used to connect
/// the user to the voice server and to handle the capability messages
/// from the viewer.
/// </summary>
public interface IWebRtcVoiceService
{
// The user is requesting a voice connection. The message contains the offer
// from the user and we must return the answer.
// If there are problems, the returned map will contain an error message.
// Initial calls to the voice server to get the user connected
public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pScene);
public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pScene);
// Once connection state is looked up, the viewer session is passed in
public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene);
public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pScene);
// Create a viewer session with all the variables needed for the underlying implementation
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pScene);
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System.Linq;
using System.Collections.Generic;
using OpenMetaverse;
using System.Threading.Tasks;
namespace osWebRtcVoice
{
public class VoiceViewerSession : IVoiceViewerSession
{
// A simple session structure that is used when the connection is actually in the
// remote service.
public VoiceViewerSession(IWebRtcVoiceService pVoiceService, UUID pRegionId, UUID pAgentId)
{
RegionId = pRegionId;
AgentId = pAgentId;
ViewerSessionID = UUID.Random().ToString();
VoiceService = pVoiceService;
}
public string ViewerSessionID { get; set; }
public IWebRtcVoiceService VoiceService { get; set; }
public string VoiceServiceSessionId
{
get => throw new System.NotImplementedException();
set => throw new System.NotImplementedException();
}
public UUID RegionId { get; set; }
public UUID AgentId { get; set; }
// =====================================================================
// ViewerSessions hold the connection information for the client connection through to the voice service.
// This collection is static and is simulator wide so there will be sessions for all regions and all clients.
public static Dictionary<string, IVoiceViewerSession> ViewerSessions = new Dictionary<string, IVoiceViewerSession>();
// Get a viewer session by the viewer session ID
public static bool TryGetViewerSession(string pViewerSessionId, out IVoiceViewerSession pViewerSession)
{
lock (ViewerSessions)
{
return ViewerSessions.TryGetValue(pViewerSessionId, out pViewerSession);
}
}
// public static bool TryGetViewerSessionByAgentId(UUID pAgentId, out IVoiceViewerSession pViewerSession)
public static bool TryGetViewerSessionByAgentId(UUID pAgentId, out IEnumerable<KeyValuePair<string, IVoiceViewerSession>> pViewerSessions)
{
lock (ViewerSessions)
{
pViewerSessions = ViewerSessions.Where(v => v.Value.AgentId == pAgentId);
return pViewerSessions.Count() > 0;
}
}
// Get a viewer session by the VoiceService session ID
public static bool TryGetViewerSessionByVSSessionId(string pVSSessionId, out IVoiceViewerSession pViewerSession)
{
lock (ViewerSessions)
{
var sessions = ViewerSessions.Where(v => v.Value.VoiceServiceSessionId == pVSSessionId);
if (sessions.Count() > 0)
{
pViewerSession = sessions.First().Value;
return true;
}
pViewerSession = null;
return false;
}
}
public static void AddViewerSession(IVoiceViewerSession pSession)
{
lock (ViewerSessions)
{
ViewerSessions[pSession.ViewerSessionID] = pSession;
}
}
public static void RemoveViewerSession(string pSessionId)
{
lock (ViewerSessions)
{
ViewerSessions.Remove(pSessionId);
}
}
// Update a ViewSession from one ID to another.
// Remove the old session ID from the ViewerSessions collection, update the
// sessionID value in the IVoiceViewerSession, and add the session back to the
// collection.
// This is used in the kludge to synchronize a region's ViewerSessionID with the
// remote VoiceService's session ID.
public static void UpdateViewerSessionId(IVoiceViewerSession pSession, string pNewSessionId)
{
lock (ViewerSessions)
{
ViewerSessions.Remove(pSession.ViewerSessionID);
pSession.ViewerSessionID = pNewSessionId;
ViewerSessions[pSession.ViewerSessionID] = pSession;
}
}
public Task Shutdown()
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Services.Interfaces;
using OpenSim.Server.Base;
using OpenSim.Server.Handlers.Base;
using System.Threading.Tasks;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using log4net;
using Nini.Config;
namespace osWebRtcVoice
{
// Class that provides the network interface to the WebRTC voice server.
// This is used by the Robust server to receive requests from the region servers
// and do the voice stuff on the WebRTC service (see WebRtcVoiceServiceConnector).
public class WebRtcVoiceServerConnector : IServiceConnector
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[WEBRTC VOICE SERVER CONNECTOR]";
private bool m_Enabled = false;
private bool m_MessageDetails = false;
private IWebRtcVoiceService m_WebRtcVoiceService;
public WebRtcVoiceServerConnector(IConfigSource pConfig, IHttpServer pServer, string pConfigName)
{
IConfig moduleConfig = pConfig.Configs["WebRtcVoice"];
if (moduleConfig is not null)
{
m_Enabled = moduleConfig.GetBoolean("Enabled", false);
if (m_Enabled)
{
m_log.InfoFormat("{0} WebRtcVoiceServerConnector enabled", LogHeader);
m_MessageDetails = moduleConfig.GetBoolean("MessageDetails", false);
// This creates the local service that handles the requests.
// The local service provides the IWebRtcVoiceService interface and directs the requests
// to the WebRTC service.
string localServiceModule = moduleConfig.GetString("LocalServiceModule", "WebRtcVoiceServiceModule.dll:WebRtcVoiceServiceModule");
m_log.DebugFormat("{0} loading {1}", LogHeader, localServiceModule);
object[] args = new object[0];
m_WebRtcVoiceService = ServerUtils.LoadPlugin<IWebRtcVoiceService>(localServiceModule, args);
// The WebRtcVoiceServiceModule is both an IWebRtcVoiceService and a ISharedRegionModule
// so we can initialize it as if it was the region module.
ISharedRegionModule sharedModule = m_WebRtcVoiceService as ISharedRegionModule;
if (sharedModule is null)
{
m_log.ErrorFormat("{0} local service module does not implement ISharedRegionModule", LogHeader);
m_Enabled = false;
return;
}
sharedModule.Initialise(pConfig);
// Now that we have someone to handle the requests, we can set up the handlers
pServer.AddJsonRPCHandler("provision_voice_account_request", Handle_ProvisionVoiceAccountRequest);
pServer.AddJsonRPCHandler("voice_signaling_request", Handle_VoiceSignalingRequest);
}
}
}
private bool Handle_ProvisionVoiceAccountRequest(OSDMap pJson, ref JsonRpcResponse pResponse)
{
bool ret = false;
m_log.DebugFormat("{0} Handle_ProvisionVoiceAccountRequest", LogHeader);
if (m_MessageDetails) m_log.DebugFormat("{0} PVAR: req={1}", LogHeader, pJson.ToString());
if (pJson.ContainsKey("params") && pJson["params"] is OSDMap paramsMap)
{
OSDMap request = paramsMap.ContainsKey("request") ? paramsMap["request"] as OSDMap : null;
UUID userID = paramsMap.ContainsKey("userID") ? paramsMap["userID"].AsUUID() : UUID.Zero;
UUID sceneID = paramsMap.ContainsKey("scene") ? paramsMap["scene"].AsUUID() : UUID.Zero;
try
{
if (m_WebRtcVoiceService is null)
{
m_log.ErrorFormat("{0} PVAR: no local service", LogHeader);
return false;
}
OSDMap resp = m_WebRtcVoiceService.ProvisionVoiceAccountRequest(request, userID, sceneID);
pResponse = new JsonRpcResponse();
pResponse.Result = resp;
if (m_MessageDetails) m_log.DebugFormat("{0} PVAR: resp={1}", LogHeader, resp.ToString());
ret = true;
}
catch (Exception e)
{
m_log.ErrorFormat("{0} PVAR: exception {1}", LogHeader, e);
}
}
else
{
m_log.ErrorFormat("{0} PVAR: missing parameters", LogHeader);
}
return ret;
}
private bool Handle_VoiceSignalingRequest(OSDMap pJson, ref JsonRpcResponse pResponse)
{
bool ret = false;
if (pJson.ContainsKey("params") && pJson["params"] is OSDMap paramsMap)
{
m_log.DebugFormat("{0} Handle_VoiceSignalingRequest", LogHeader);
if (m_MessageDetails) m_log.DebugFormat("{0} VSR: req={1}", LogHeader, paramsMap.ToString());
OSDMap request = paramsMap.ContainsKey("request") ? paramsMap["request"] as OSDMap : null;
UUID userID = paramsMap.ContainsKey("userID") ? paramsMap["userID"].AsUUID() : UUID.Zero;
UUID sceneID = paramsMap.ContainsKey("scene") ? paramsMap["scene"].AsUUID() : UUID.Zero;
try
{
OSDMap resp = m_WebRtcVoiceService.VoiceSignalingRequest(request, userID, sceneID);
pResponse = new JsonRpcResponse();
pResponse.Result = resp;
if (m_MessageDetails) m_log.DebugFormat("{0} VSR: resp={1}", LogHeader, resp.ToString());
ret = true;
}
catch (Exception e)
{
m_log.ErrorFormat("{0} VSR: exception {1}", LogHeader, e);
}
}
else
{
m_log.ErrorFormat("{0} VSR: missing parameters", LogHeader);
}
return ret;
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Reflection;
using OpenSim.Framework;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using log4net;
using Nini.Config;
namespace osWebRtcVoice
{
// Class that provides the local IWebRtcVoiceService interface to the JsonRPC Robust
// server. This is used by the region servers to talk to the Robust server.
public class WebRtcVoiceServiceConnector : IWebRtcVoiceService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string LogHeader = "[WEBRTC VOICE SERVICE CONNECTOR]";
private bool m_Enabled = false;
private bool m_MessageDetails = false;
private IConfigSource m_Config;
string m_serverURI = "http://localhost:8080";
public WebRtcVoiceServiceConnector(IConfigSource pConfig)
{
m_Config = pConfig;
IConfig moduleConfig = m_Config.Configs["WebRtcVoice"];
if (moduleConfig is not null)
{
m_Enabled = moduleConfig.GetBoolean("Enabled", false);
if (m_Enabled)
{
m_serverURI = moduleConfig.GetString("WebRtcVoiceServerURI", string.Empty);
if (string.IsNullOrWhiteSpace(m_serverURI))
{
m_log.Error($"{LogHeader} WebRtcVoiceServiceConnector enabled but no WebRtcVoiceServerURI specified");
m_Enabled = false;
}
else
{
m_log.Info($"{LogHeader} WebRtcVoiceServiceConnector enabled");
}
m_MessageDetails = moduleConfig.GetBoolean("MessageDetails", false);
}
}
}
// Create a local viewer session. This gets a local viewer session ID that is
// later changed when the ProvisionVoiceAccountRequest response is returned
// so that the viewer session ID is the same here as from the WebRTC service.
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
m_log.Debug($"{LogHeader} CreateViewerSession");
return new VoiceViewerSession(this, pUserID, pSceneID);
}
public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
m_log.Debug($"{LogHeader} ProvisionVoiceAccountRequest without ViewerSession. uID={pUserID}, sID={pSceneID}");
return null;
}
// Received a ProvisionVoiceAccountRequest from a viewer. Forward it to the WebRTC service.
public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
m_log.Debug($"{LogHeader} VoiceSignalingRequest. uID={pUserID}, sID={pSceneID}");
OSDMap req = new()
{
{ "request", pRequest },
{ "userID", pUserID.ToString() },
{ "scene", pSceneID.ToString() }
};
var resp = JsonRpcRequest("provision_voice_account_request", m_serverURI, req);
// Kludge to sync the viewer session number in our IVoiceViewerSession with the one from the WebRTC service.
if (resp.TryGetString("viewer_session", out string otherViewerSessionId))
{
m_log.Debug(
$"{LogHeader} ProvisionVoiceAccountRequest: syncing viewSessionID. old={pVSession.ViewerSessionID}, new={otherViewerSessionId}");
VoiceViewerSession.UpdateViewerSessionId(pVSession, otherViewerSessionId);
}
return resp;
}
public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
m_log.Debug($"{LogHeader} VoiceSignalingRequest without ViewerSession. uID={pUserID}, sID={pSceneID}");
return null;
}
public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
m_log.DebugFormat("{0} VoiceSignalingRequest. uID={1}, sID={2}", LogHeader, pUserID, pSceneID);
OSDMap req = new()
{
{ "request", pRequest },
{ "userID", pUserID.ToString() },
{ "scene", pSceneID.ToString() }
};
return JsonRpcRequest("voice_signaling_request", m_serverURI, req);
}
public OSDMap JsonRpcRequest(string method, string uri, OSDMap pParams)
{
string jsonId = UUID.Random().ToString();
if(string.IsNullOrWhiteSpace(uri))
return null;
OSDMap request = new()
{
{ "jsonrpc", OSD.FromString("2.0") },
{ "id", OSD.FromString(jsonId) },
{ "method", OSD.FromString(method) },
{ "params", pParams }
};
OSDMap outerResponse = null;
try
{
if (m_MessageDetails) m_log.Debug($"{LogHeader}: request: {request}");
outerResponse = WebUtil.PostToService(uri, request, 10000, true);
if (m_MessageDetails) m_log.Debug($"{LogHeader}: response: {outerResponse}");
}
catch (Exception e)
{
m_log.Error($"{LogHeader}: JsonRpc request '{method}' to {uri} failed: {e.Message}");
m_log.Debug($"{LogHeader}: request: {request}");
return new OSDMap()
{
{ "error", OSD.FromString(e.Message) }
};
}
if (!outerResponse.TryGetOSDMap("_Result", out OSDMap response))
{
string errm = $"JsonRpc request '{method}' to {1} returned an invalid response: {OSDParser.SerializeJsonString(outerResponse)}";
m_log.Error(errm);
return new OSDMap()
{
{ "error", errm }
};
}
OSD osdtmp;
if (response.TryGetValue("error", out osdtmp))
{
string errm = $"JsonRpc request '{method}' to {uri} returned an error: {OSDParser.SerializeJsonString(osdtmp)}";
m_log.Error(errm);
return new OSDMap()
{
{ "error", errm }
};
}
if (!response.TryGetOSDMap("result", out OSDMap resultmap ))
{
string errm = $"JsonRpc request '{method}' to {uri} returned result as non-OSDMap: {OSDParser.SerializeJsonString(outerResponse)}";
m_log.Error(errm);
return new OSDMap()
{
{ "error", errm }
};
}
return resultmap;
}
}
}

View File

@@ -0,0 +1,500 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.IO;
using System.Net;
using System.Reflection;
using Mono.Addins;
using OpenSim.Framework;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using Caps = OpenSim.Framework.Capabilities.Caps;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
using log4net;
using Nini.Config;
[assembly: Addin("WebRtcVoiceRegionModule", "1.0")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
namespace osWebRtcVoice
{
/// <summary>
/// This module provides the WebRTC voice interface for viewer clients..
///
/// In particular, it provides the following capabilities:
/// ProvisionVoiceAccountRequest, VoiceSignalingRequest and limited ChatSessionRequest
/// which are the user interface to the voice service.
///
/// Initially, when the user connects to the region, the region feature "VoiceServiceType" is
/// set to "webrtc" and the capabilities that support voice are enabled.
/// The capabilities then pass the user request information to the IWebRtcVoiceService interface
/// that has been registered for the reqion.
/// </summary>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "RegionVoiceModule")]
public class WebRtcVoiceRegionModule : ISharedRegionModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly string logHeader = "[REGION WEBRTC VOICE]";
private static byte[] llsdUndefAnswerBytes = Util.UTF8.GetBytes("<llsd><undef /></llsd>");
private bool _MessageDetails = false;
// Control info
private static bool m_Enabled = false;
private IConfig m_Config;
// ISharedRegionModule.Initialize
public void Initialise(IConfigSource config)
{
m_Config = config.Configs["WebRtcVoice"];
if (m_Config is not null)
{
m_Enabled = m_Config.GetBoolean("Enabled", false);
if (m_Enabled)
{
_MessageDetails = m_Config.GetBoolean("MessageDetails", false);
m_log.Info($"{logHeader}: enabled");
}
}
}
// ISharedRegionModule.PostInitialize
public void PostInitialise()
{
}
// ISharedRegionModule.AddRegion
public void AddRegion(Scene scene)
{
// todo register module to get parcels changes etc
}
// ISharedRegionModule.RemoveRegion
public void RemoveRegion(Scene scene)
{
}
// ISharedRegionModule.RegionLoaded
public void RegionLoaded(Scene scene)
{
if (m_Enabled)
{
scene.EventManager.OnRegisterCaps += delegate (UUID agentID, Caps caps)
{
OnRegisterCaps(scene, agentID, caps);
};
ISimulatorFeaturesModule simFeatures = scene.RequestModuleInterface<ISimulatorFeaturesModule>();
simFeatures?.AddFeature("VoiceServerType", OSD.FromString("webrtc"));
}
}
// ISharedRegionModule.Close
public void Close()
{
}
// ISharedRegionModule.Name
public string Name
{
get { return "RegionVoiceModule"; }
}
// ISharedRegionModule.ReplaceableInterface
public Type ReplaceableInterface
{
get { return null; }
}
// <summary>
// OnRegisterCaps is invoked via the scene.EventManager
// everytime OpenSim hands out capabilities to a client
// (login, region crossing). We contribute three capabilities to
// the set of capabilities handed back to the client:
// ProvisionVoiceAccountRequest, VoiceSignalingRequest and limited ChatSessionRequest
//
// ProvisionVoiceAccountRequest allows the client to obtain
// voice communication information the the avater.
//
// VoiceSignalingRequest: Used for trickling ICE candidates.
//
// ChatSessionRequest
//
// Note that OnRegisterCaps is called here via a closure
// delegate containing the scene of the respective region (see
// Initialise()).
// </summary>
public void OnRegisterCaps(Scene scene, UUID agentID, Caps caps)
{
m_log.Debug(
$"{logHeader}: OnRegisterCaps called with agentID {agentID} caps {caps} in scene {scene.Name}");
caps.RegisterSimpleHandler("ProvisionVoiceAccountRequest",
new SimpleStreamHandler("/" + UUID.Random(), (IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) =>
{
ProvisionVoiceAccountRequest(httpRequest, httpResponse, agentID, scene);
}));
caps.RegisterSimpleHandler("VoiceSignalingRequest",
new SimpleStreamHandler("/" + UUID.Random(), (IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) =>
{
VoiceSignalingRequest(httpRequest, httpResponse, agentID, scene);
}));
caps.RegisterSimpleHandler("ChatSessionRequest",
new SimpleStreamHandler("/" + UUID.Random(), (IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) =>
{
ChatSessionRequest(httpRequest, httpResponse, agentID, scene);
}));
}
/// <summary>
/// Callback for a client request for Voice Account Details
/// </summary>
/// <param name="scene">current scene object of the client</param>
/// <param name="request"></param>
/// <param name="path"></param>
/// <param name="param"></param>
/// <param name="agentID"></param>
/// <param name="caps"></param>
/// <returns></returns>
public void ProvisionVoiceAccountRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene)
{
// Get the voice service. If it doesn't exist, return an error.
IWebRtcVoiceService voiceService = scene.RequestModuleInterface<IWebRtcVoiceService>();
if (voiceService is null)
{
m_log.Error($"{logHeader}[ProvisionVoice]: voice service not loaded");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if(request.HttpMethod != "POST")
{
m_log.DebugFormat($"[{logHeader}][ProvisionVoice]: Not a POST request. Agent={agentID}");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
// Deserialize the request. Convert the LLSDXml to OSD for our use
OSDMap map = BodyToMap(request, "ProvisionVoiceAccountRequest");
if (map is null)
{
m_log.Error($"{logHeader}[ProvisionVoice]: No request data found. Agent={agentID}");
response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
// Make sure the request is for WebRtc voice
if (map.TryGetValue("voice_server_type", out OSD vstosd))
{
if (vstosd is OSDString vst && !((string)vst).Equals("webrtc", StringComparison.OrdinalIgnoreCase))
{
m_log.Warn($"{logHeader}[ProvisionVoice]: voice_server_type is not 'webrtc'. Request: {map}");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.OK;
return;
}
}
if (_MessageDetails) m_log.DebugFormat($"{logHeader}[ProvisionVoice]: request: {map}");
if (map.TryGetString("channel_type", out string channelType))
{
//do fully not trust viewers voice parcel requests
if (channelType == "local")
{
if (!scene.RegionInfo.EstateSettings.AllowVoice)
{
m_log.Debug($"{logHeader}[ProvisionVoice]:region \"{scene.Name}\": voice not enabled in estate settings");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.NotImplemented;
return;
}
if (scene.LandChannel == null)
{
m_log.Error($"{logHeader}[ProvisionVoice] region \"{scene.Name}\" land data not yet available");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.NotImplemented;
return;
}
if(!scene.TryGetScenePresence(agentID, out ScenePresence sp))
{
m_log.Debug($"{logHeader}[ProvisionVoice]:avatar not found");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if(map.TryGetInt("parcel_local_id", out int parcelID))
{
ILandObject parcel = scene.LandChannel.GetLandObject(parcelID);
if (parcel == null)
{
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
LandData land = parcel.LandData;
if (land == null)
{
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (!scene.RegionInfo.EstateSettings.TaxFree && (land.Flags & (uint)ParcelFlags.AllowVoiceChat) == 0)
{
m_log.Debug($"{logHeader}[ProvisionVoice]:parcel voice not allowed");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
if ((land.Flags & (uint)ParcelFlags.UseEstateVoiceChan) != 0)
{
map.Remove("parcel_local_id"); // estate channel
}
else if(parcel.IsRestrictedFromLand(agentID) || parcel.IsBannedFromLand(agentID))
{
// check Z distance?
m_log.Debug($"{logHeader}[ProvisionVoice]:agent not allowed on parcel");
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.Forbidden;
return;
}
}
}
}
// The checks passed. Send the request to the voice service.
OSDMap resp = voiceService.ProvisionVoiceAccountRequest(map, agentID, scene.RegionInfo.RegionID);
if(resp is not null)
{
if (_MessageDetails) m_log.DebugFormat($"{logHeader}[ProvisionVoice]: response: {resp}");
// TODO: check for errors and package the response
// Convert the OSD to LLSDXml for the response
string xmlResp = OSDParser.SerializeLLSDXmlString(resp);
response.RawBuffer = Util.UTF8.GetBytes(xmlResp);
response.StatusCode = (int)HttpStatusCode.OK;
}
else
{
m_log.DebugFormat($"{logHeader}[ProvisionVoice]: got null response");
response.StatusCode = (int)HttpStatusCode.OK;
}
return;
}
public void VoiceSignalingRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene)
{
IWebRtcVoiceService voiceService = scene.RequestModuleInterface<IWebRtcVoiceService>();
if (voiceService is null)
{
m_log.ErrorFormat($"{logHeader}[VoiceSignalingRequest]: avatar \"{agentID}\": no voice service");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if(request.HttpMethod != "POST")
{
m_log.Error($"[{logHeader}][VoiceSignaling]: Not a POST request. Agent={agentID}");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
// Deserialize the request. Convert the LLSDXml to OSD for our use
OSDMap map = BodyToMap(request, "VoiceSignalingRequest");
if (map is null)
{
m_log.ErrorFormat($"{logHeader}[VoiceSignalingRequest]: No request data found. Agent={agentID}");
response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
// Make sure the request is for WebRTC voice
if (map.TryGetValue("voice_server_type", out OSD vstosd))
{
if (vstosd is OSDString vst && !((string)vst).Equals("webrtc", StringComparison.OrdinalIgnoreCase))
{
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.OK;
return;
}
}
OSDMap resp = voiceService.VoiceSignalingRequest(map, agentID, scene.RegionInfo.RegionID);
if (_MessageDetails) m_log.Debug($"{logHeader}[VoiceSignalingRequest]: Response: {resp}");
// TODO: check for errors and package the response
response.RawBuffer = llsdUndefAnswerBytes;
response.StatusCode = (int)HttpStatusCode.OK;
return;
}
/// <summary>
/// Callback for a client request for ChatSessionRequest.
/// The viewer sends this request when the user tries to start a P2P text or voice session
/// with another user. We need to generate a new session ID and return it to the client.
/// </summary>
/// <param name="request"></param>
/// <param name="response"></param>
/// <param name="agentID"></param>
/// <param name="scene"></param>
public void ChatSessionRequest(IOSHttpRequest request, IOSHttpResponse response, UUID agentID, Scene scene)
{
m_log.DebugFormat("{0}: ChatSessionRequest received for agent {1} in scene {2}", logHeader, agentID, scene.RegionInfo.RegionName);
if (request.HttpMethod != "POST")
{
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (!scene.TryGetScenePresence(agentID, out ScenePresence sp) || sp.IsDeleted)
{
m_log.Warn($"{logHeader} ChatSessionRequest: scene presence not found or deleted for agent {agentID}");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
OSDMap reqmap = BodyToMap(request, "[ChatSessionRequest]");
if (reqmap is null)
{
m_log.Warn($"{logHeader} ChatSessionRequest: message body not parsable in request for agent {agentID}");
response.StatusCode = (int)HttpStatusCode.NoContent;
return;
}
m_log.Debug($"{logHeader} ChatSessionRequest");
if (!reqmap.TryGetString("method", out string method))
{
m_log.Warn($"{logHeader} ChatSessionRequest: missing required 'method' field in request for agent {agentID}");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
if (!reqmap.TryGetUUID("session-id", out UUID sessionID))
{
m_log.Warn($"{logHeader} ChatSessionRequest: missing required 'session-id' field in request for agent {agentID}");
response.StatusCode = (int)HttpStatusCode.NotFound;
return;
}
switch (method.ToLower())
{
// Several different method requests that we don't know how to handle.
// Just return OK for now.
case "decline p2p voice":
case "decline invitation":
case "start conference":
case "fetch history":
response.StatusCode = (int)HttpStatusCode.OK;
break;
// Asking to start a P2P voice session. We need to generate a new session ID and return
// it to the client in a ChatterBoxSessionStartReply event.
case "start p2p voice":
UUID newSessionID;
if (reqmap.TryGetUUID("params", out UUID otherID))
newSessionID = new(otherID.ulonga ^ agentID.ulonga, otherID.ulongb ^ agentID.ulongb);
else
newSessionID = UUID.Random();
IEventQueue queue = scene.RequestModuleInterface<IEventQueue>();
if (queue is null)
{
m_log.ErrorFormat("{0}: no event queue for scene {1}", logHeader, scene.RegionInfo.RegionName);
response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
else
{
queue.ChatterBoxSessionStartReply(
newSessionID,
sp.Name,
2,
false,
true,
sessionID,
true,
string.Empty,
agentID);
response.StatusCode = (int)HttpStatusCode.OK;
}
break;
default:
response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
}
}
/// <summary>
/// Convert the LLSDXml body of the request to an OSDMap for easier handling.
/// Also logs the request if message details is enabled.
/// </summary>
/// <param name="request"></param>
/// <param name="pCaller"></param>
/// <returns>'null' if the request body is empty or cannot be deserialized</returns>
private OSDMap BodyToMap(IOSHttpRequest request, string pCaller)
{
try
{
using Stream inputStream = request.InputStream;
if (inputStream.Length > 0)
{
OSD tmp = OSDParser.DeserializeLLSDXml(inputStream);
if (_MessageDetails)
m_log.Debug($"{pCaller} BodyToMap: Request: {tmp}");
if(tmp is OSDMap map)
return map;
}
}
catch
{
m_log.Debug($"{pCaller} BodyToMap: Fail to decode LLSDXml request");
}
return null;
}
}
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Server.Base;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using Mono.Addins;
using log4net;
using Nini.Config;
[assembly: Addin("WebRtcVoiceServiceModule", "1.0")]
[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
namespace osWebRtcVoice
{
/// <summary>
/// Interface for the WebRtcVoiceService.
/// An instance of this is registered as the IWebRtcVoiceService for this region.
/// The function here is to direct the capability requests to the appropriate voice service.
/// For the moment, there are separate voice services for spatial and non-spatial voice
/// with the idea that a region could have a pre-region spatial voice service while
/// the grid could have a non-spatial voice service for group chat, etc.
/// Fancier configurations are possible.
/// </summary>
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebRtcVoiceServiceModule")]
public class WebRtcVoiceServiceModule : ISharedRegionModule, IWebRtcVoiceService
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static string LogHeader = "[WEBRTC VOICE SERVICE MODULE]";
private static bool m_Enabled = false;
private IConfigSource m_Config;
private IWebRtcVoiceService m_spatialVoiceService;
private IWebRtcVoiceService m_nonSpatialVoiceService;
// =====================================================================
// ISharedRegionModule.Initialize
// Get configuration and load the modules that will handle spatial and non-spatial voice.
public void Initialise(IConfigSource pConfig)
{
m_Config = pConfig;
IConfig moduleConfig = m_Config.Configs["WebRtcVoice"];
if (moduleConfig is not null)
{
m_Enabled = moduleConfig.GetBoolean("Enabled", false);
if (m_Enabled)
{
// Get the DLLs for the two voice services
string spatialDllName = moduleConfig.GetString("SpatialVoiceService", string.Empty);
string nonSpatialDllName = moduleConfig.GetString("NonSpatialVoiceService", string.Empty);
if (string.IsNullOrEmpty(spatialDllName) && string.IsNullOrEmpty(nonSpatialDllName))
{
m_log.Error($"{LogHeader} No VoiceService specified in configuration");
m_Enabled = false;
return;
}
// Default non-spatial to spatial if not specified
if (string.IsNullOrEmpty(nonSpatialDllName))
{
m_log.Debug($"{LogHeader} nonSpatialDllName not specified. Defaulting to spatialDllName");
nonSpatialDllName = spatialDllName;
}
// Load the two voice services
m_log.Debug($"{LogHeader} Loading SpatialVoiceService from {spatialDllName}");
m_spatialVoiceService = ServerUtils.LoadPlugin<IWebRtcVoiceService>(spatialDllName, [m_Config]);
if (m_spatialVoiceService is null)
{
m_log.Error($"{LogHeader} Could not load SpatialVoiceService from {spatialDllName}, module disabled");
m_Enabled = false;
return;
}
m_log.Debug($"{LogHeader} Loading NonSpatialVoiceService from {nonSpatialDllName}");
if (spatialDllName != nonSpatialDllName)
{
m_nonSpatialVoiceService = ServerUtils.LoadPlugin<IWebRtcVoiceService>(nonSpatialDllName, [ m_Config ]);
if (m_nonSpatialVoiceService is null)
{
m_log.Error("{LogHeader} Could not load NonSpatialVoiceService from {nonSpatialDllName}");
m_Enabled = false;
}
}
if (m_Enabled)
{
m_log.Info($"{LogHeader} WebRtcVoiceService enabled");
}
}
}
}
// ISharedRegionModule.PostInitialize
public void PostInitialise()
{
}
// ISharedRegionModule.Close
public void Close()
{
}
// ISharedRegionModule.ReplaceableInterface
public Type ReplaceableInterface
{
get { return null; }
}
// ISharedRegionModule.Name
public string Name
{
get { return "WebRtcVoiceServiceModule"; }
}
// ISharedRegionModule.AddRegion
public void AddRegion(Scene scene)
{
if (m_Enabled)
{
m_log.Debug($"{LogHeader} Adding WebRtcVoiceService to region {scene.Name}");
scene.RegisterModuleInterface<IWebRtcVoiceService>(this);
// TODO: figure out what events we care about
// When new client (child or root) is added to scene, before OnClientLogin
// scene.EventManager.OnNewClient += Event_OnNewClient;
// When client is added on login.
// scene.EventManager.OnClientLogin += Event_OnClientLogin;
// New presence is added to scene. Child, root, and NPC. See Scene.AddNewAgent()
// scene.EventManager.OnNewPresence += Event_OnNewPresence;
// scene.EventManager.OnRemovePresence += Event_OnRemovePresence;
// update to client position (either this or 'significant')
// scene.EventManager.OnClientMovement += Event_OnClientMovement;
// "significant" update to client position
// scene.EventManager.OnSignificantClientMovement += Event_OnSignificantClientMovement;
}
}
// ISharedRegionModule.RemoveRegion
public void RemoveRegion(Scene scene)
{
if (m_Enabled)
{
scene.UnregisterModuleInterface<IWebRtcVoiceService>(this);
}
}
// ISharedRegionModule.RegionLoaded
public void RegionLoaded(Scene scene)
{
}
// =====================================================================
// Thought about doing this but currently relying on the voice service
// event ("hangup") to remove the viewer session.
private void Event_OnRemovePresence(UUID pAgentID)
{
// When a presence is removed, remove the viewer sessions for that agent
IEnumerable<KeyValuePair<string, IVoiceViewerSession>> vSessions;
if (VoiceViewerSession.TryGetViewerSessionByAgentId(pAgentID, out vSessions))
{
foreach(KeyValuePair<string, IVoiceViewerSession> v in vSessions)
{
m_log.DebugFormat("{0} Event_OnRemovePresence: removing viewer session {1}", LogHeader, v.Key);
VoiceViewerSession.RemoveViewerSession(v.Key);
v.Value.Shutdown();
}
}
}
// =====================================================================
// IWebRtcVoiceService
// IWebRtcVoiceService.ProvisionVoiceAccountRequest
public OSDMap ProvisionVoiceAccountRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
OSDMap response = null;
IVoiceViewerSession vSession = null;
if (pRequest.TryGetString("viewer_session", out string viewerSessionId))
{
// request has a viewer session. Use that to find the voice service
if (!VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession))
{
m_log.Error($"{0} ProvisionVoiceAccountRequest: viewer session {viewerSessionId} not found");
}
}
else
{
// the request does not have a viewer session. See if it's an initial request
if (pRequest.TryGetString("channel_type", out string channelType))
{
if (channelType == "local")
{
// TODO: check if this userId is making a new session (case that user is reconnecting)
vSession = m_spatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID);
VoiceViewerSession.AddViewerSession(vSession);
}
else
{
// TODO: check if this userId is making a new session (case that user is reconnecting)
vSession = m_nonSpatialVoiceService.CreateViewerSession(pRequest, pUserID, pSceneID);
VoiceViewerSession.AddViewerSession(vSession);
}
}
else
{
m_log.Error($"{LogHeader} ProvisionVoiceAccountRequest: no channel_type in request");
}
}
if (vSession is not null)
{
response = vSession.VoiceService.ProvisionVoiceAccountRequest(vSession, pRequest, pUserID, pSceneID);
}
return response;
}
// IWebRtcVoiceService.VoiceSignalingRequest
public OSDMap VoiceSignalingRequest(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
OSDMap response = null;
IVoiceViewerSession vSession = null;
if (pRequest.TryGetString("viewer_session", out string viewerSessionId))
{
// request has a viewer session. Use that to find the voice service
if (VoiceViewerSession.TryGetViewerSession(viewerSessionId, out vSession))
{
response = vSession.VoiceService.VoiceSignalingRequest(vSession, pRequest, pUserID, pSceneID);
}
else
{
m_log.ErrorFormat("{0} VoiceSignalingRequest: viewer session {1} not found", LogHeader, viewerSessionId);
}
}
else
{
m_log.ErrorFormat("{0} VoiceSignalingRequest: no viewer_session in request", LogHeader);
}
return response;
}
// This module should never be called with this signature
public OSDMap ProvisionVoiceAccountRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
throw new NotImplementedException();
}
// This module should never be called with this signature
public OSDMap VoiceSignalingRequest(IVoiceViewerSession pVSession, OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
throw new NotImplementedException();
}
public IVoiceViewerSession CreateViewerSession(OSDMap pRequest, UUID pUserID, UUID pSceneID)
{
throw new NotImplementedException();
}
}
}

View File

@@ -25,21 +25,11 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Xml;
using System.Net;
using System.Reflection;
using System.Timers;
using System.Threading;
using log4net;
using Mono.Addins;
using Nini.Config;
using Nwc.XmlRpc;
using OpenMetaverse;
using Mono.Addins;
using OpenSim;
using OpenSim.Framework;
using OpenSim.Framework.Console;
@@ -49,9 +39,20 @@ using OpenSim.Region.CoreModules.World.Terrain;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Services.Interfaces;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Timers;
using System.Xml;
using static System.Net.Mime.MediaTypeNames;
using GridRegion = OpenSim.Services.Interfaces.GridRegion;
using PermissionMask = OpenSim.Framework.PermissionMask;
using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
using RegionInfo = OpenSim.Framework.RegionInfo;
namespace OpenSim.ApplicationPlugins.RemoteController
@@ -131,13 +132,14 @@ namespace OpenSim.ApplicationPlugins.RemoteController
m_httpServer = MainServer.GetHttpServer((uint)port,ipaddr);
Dictionary<string, XmlRpcMethod> availableMethods = new Dictionary<string, XmlRpcMethod>();
availableMethods["admin_alert_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAlertUserMethod);
availableMethods["admin_broadcast"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAlertMethod);
availableMethods["admin_create_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCreateRegionMethod);
availableMethods["admin_delete_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcDeleteRegionMethod);
availableMethods["admin_close_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCloseRegionMethod);
availableMethods["admin_modify_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcModifyRegionMethod);
availableMethods["admin_region_query"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcRegionQueryMethod);
availableMethods["admin_shutdown"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcShutdownMethod);
availableMethods["admin_broadcast"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAlertMethod);
availableMethods["admin_dialog"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcDialogMethod);
availableMethods["admin_restart"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcRestartMethod);
availableMethods["admin_load_heightmap"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcLoadHeightmapMethod);
@@ -421,6 +423,39 @@ namespace OpenSim.ApplicationPlugins.RemoteController
m_log.Info("[RADMIN]: Restart Region request complete");
}
private void XmlRpcAlertUserMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient)
{
//m_log.Info("[RADMIN]: AlertUser request started");
Hashtable responseData = (Hashtable)response.Value;
Hashtable requestData = (Hashtable)request.Params[0];
string agentIdStr = (string)requestData["agent_id"];
string message = (string)requestData["message"];
responseData["accepted"] = true;
if(!UUID.TryParse(agentIdStr, out UUID agentId))
{
responseData["success"] = false;
responseData["error"] = "Invalid agent_id";
m_log.Info($"[RADMIN]: alert to agent got invalid uuid: {agentIdStr}: {message}");
return;
}
if(m_application.SceneManager.TryGetRootScenePresence(agentId, out ScenePresence sp ))
{
sp.ControllingClient.SendAlertMessage(message);
m_log.Info($"[RADMIN]: Sent alert to agent {agentIdStr}: {message}");
responseData["success"] = true;
return;
}
responseData["success"] = false;
responseData["error"] = "User not found or not online";
m_log.Info($"[RADMIN]: Fail to send alert to not found agent {agentIdStr}: {message}");
}
private void XmlRpcAlertMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient)
{
m_log.Info("[RADMIN]: Alert request started");
@@ -455,7 +490,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
string message = (string)requestData["message"];
string fromuuid = (string)requestData["from"];
m_log.InfoFormat("[RADMIN]: Broadcasting: {0}", message);
m_log.Info($"[RADMIN]: Broadcasting: {message}");
responseData["accepted"] = true;
responseData["success"] = true;
@@ -464,8 +499,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
delegate(Scene scene)
{
IDialogModule dialogModule = scene.RequestModuleInterface<IDialogModule>();
if (dialogModule != null)
dialogModule.SendNotificationToUsersInRegion(UUID.Zero, fromuuid, message);
dialogModule?.SendNotificationToUsersInRegion(UUID.Zero, fromuuid, message);
});
m_log.Info("[RADMIN]: Dialog request complete");
@@ -620,6 +654,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
m_application.Shutdown();
}
/// <summary>
/// Create a new region.
/// <summary>

View File

@@ -70,6 +70,7 @@ namespace OpenSim.Capabilities.Handlers
List<LLSDFetchInventoryDescendents> folders;
List<UUID> bad_folders = new();
try
{
OSDArray foldersrequested = null;

View File

@@ -27,7 +27,6 @@
using System.Net;
using System.Reflection;
using System.Text;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;

View File

@@ -115,7 +115,7 @@ namespace OpenSim.Framework.Capabilities
{
writer.Formatting = Formatting.None;
writer.WriteStartElement(String.Empty, "llsd", String.Empty);
writer.WriteStartElement(string.Empty, "llsd", string.Empty);
LLSDWriteOne(writer, obj);
writer.WriteEndElement();
writer.Flush();
@@ -132,32 +132,32 @@ namespace OpenSim.Framework.Capabilities
{
if (obj == null)
{
writer.WriteStartElement(String.Empty, "undef", String.Empty);
writer.WriteStartElement(string.Empty, "undef", string.Empty);
writer.WriteEndElement();
return;
}
if (obj is string s)
{
writer.WriteStartElement(String.Empty, "string", String.Empty);
writer.WriteStartElement(string.Empty, "string", string.Empty);
writer.WriteString(s);
writer.WriteEndElement();
}
else if (obj is int)
{
writer.WriteStartElement(String.Empty, "integer", String.Empty);
writer.WriteStartElement(string.Empty, "integer", string.Empty);
writer.WriteString(obj.ToString());
writer.WriteEndElement();
}
else if (obj is double || obj is float)
{
writer.WriteStartElement(String.Empty, "real", String.Empty);
writer.WriteStartElement(string.Empty, "real", string.Empty);
writer.WriteString(obj.ToString());
writer.WriteEndElement();
}
else if (obj is bool b)
{
writer.WriteStartElement(String.Empty, "boolean", String.Empty);
writer.WriteStartElement(string.Empty, "boolean", string.Empty);
writer.WriteString(b ? "1" : "0");
writer.WriteEndElement();
}
@@ -167,7 +167,7 @@ namespace OpenSim.Framework.Capabilities
}
else if (obj is UUID u)
{
writer.WriteStartElement(String.Empty, "uuid", String.Empty);
writer.WriteStartElement(string.Empty, "uuid", string.Empty);
writer.WriteString(u.ToString());
writer.WriteEndElement();
}
@@ -185,10 +185,10 @@ namespace OpenSim.Framework.Capabilities
}
else if (obj is Dictionary<string,object> dict)
{
writer.WriteStartElement(String.Empty, "map", String.Empty);
writer.WriteStartElement(string.Empty, "map", string.Empty);
foreach (KeyValuePair<string,object> kvp in dict)
{
writer.WriteStartElement(String.Empty, "key", String.Empty);
writer.WriteStartElement(string.Empty, "key", string.Empty);
writer.WriteString(kvp.Key);
writer.WriteEndElement();
LLSDWriteOne(writer, kvp.Value);
@@ -197,7 +197,7 @@ namespace OpenSim.Framework.Capabilities
}
else if (obj is ArrayList a)
{
writer.WriteStartElement(String.Empty, "array", String.Empty);
writer.WriteStartElement(string.Empty, "array", string.Empty);
foreach (object item in a)
{
LLSDWriteOne(writer, item);

View File

@@ -75,7 +75,6 @@ namespace OpenSim.ConsoleClient
Requester.MakeRequest("http://"+m_Host+":"+m_Port.ToString()+"/StartSession/", String.Format("USER={0}&PASS={1}", m_User, m_Pass), LoginReply);
string pidFile = serverConfig.GetString("PIDFile", String.Empty);
while (m_Server.Running)
{
@@ -83,7 +82,8 @@ namespace OpenSim.ConsoleClient
MainConsole.Instance.Prompt();
}
if (pidFile != String.Empty)
string pidFile = serverConfig.GetString("PIDFile", string.Empty);
if (pidFile.Length > 0)
File.Delete(pidFile);
Environment.Exit(0);

View File

@@ -324,7 +324,7 @@ namespace OpenSim.Data.MySQL
metadata.CreatorID = dbReader["CreatorID"].ToString();
// Current SHA1s are not stored/computed.
metadata.SHA1 = new byte[] { };
metadata.SHA1 = Array.Empty<byte>();
retList.Add(metadata);
}

View File

@@ -122,8 +122,7 @@ namespace OpenSim.Data.MySQL
public bool Store(AuthenticationData data)
{
if (data.Data.ContainsKey("UUID"))
data.Data.Remove("UUID");
data.Data.Remove("UUID");
string[] fields = new List<string>(data.Data.Keys).ToArray();

View File

@@ -199,7 +199,7 @@ namespace OpenSim.Data.MySQL
public RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID)
{
RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "PrincipalID" },
new string[] { groupID.ToString(), principalID.ToString() });
new string[] { groupID.ToString(), principalID });
return data;
}

View File

@@ -171,7 +171,7 @@ namespace OpenSim.Data.MySQL
"PhysicsShapeType, Density, GravityModifier, " +
"Friction, Restitution, Vehicle, PhysInertia, DynAttrs, " +
"RotationAxisLocks, sopanims, sitactrange, pseudocrc, " +
"lnkstBinData" +
"lnkstBinData, StartStr" +
") values (" + "?UUID, " +
"?CreationDate, ?Name, ?Text, " +
"?Description, ?SitName, ?TouchName, " +
@@ -207,7 +207,7 @@ namespace OpenSim.Data.MySQL
"?PhysicsShapeType, ?Density, ?GravityModifier, " +
"?Friction, ?Restitution, ?Vehicle, ?PhysInertia, ?DynAttrs, " +
"?RotationAxisLocks, ?sopanims, ?sitactrange, ?pseudocrc, " +
"?lnkstBinData)";
"?lnkstBinData, ?StartStr)";
FillPrimCommand(cmd, prim, obj.UUID, regionUUID);
@@ -341,6 +341,7 @@ namespace OpenSim.Data.MySQL
while (reader.Read())
{
SceneObjectPart prim = BuildPrim(reader);
if (reader["Shape"] is DBNull)
prim.Shape = PrimitiveBaseShape.Default;
else
@@ -362,6 +363,10 @@ namespace OpenSim.Data.MySQL
byte[] data = (byte[])reader["lnkstBinData"];
newSog.LinksetData = LinksetData.FromBin(data);
}
if(reader["StartStr"] is not DBNull)
{
newSog.RezStringParameter = (string)reader["StartStr"];
}
}
else
m_log.Warn($"[REGION DB]: duplicated SOG with root prim \"{prim.Name}\" {prim.UUID} in region {regionID}");
@@ -1623,6 +1628,11 @@ namespace OpenSim.Data.MySQL
cmd.Parameters.AddWithValue("lnkstBinData", prim.ParentGroup.LinksetData.ToBin());
else
cmd.Parameters.AddWithValue("lnkstBinData", null);
if(prim.IsRoot)
cmd.Parameters.AddWithValue("StartStr", prim.ParentGroup.RezStringParameter);
else
cmd.Parameters.AddWithValue("StartStr", null);
}
/// <summary>

View File

@@ -295,7 +295,6 @@ namespace OpenSim.Data.MySQL
ad.SimName = reader.GetString("simname");
ad.GlobalPos = reader.GetString("posglobal");
ad.ParcelName = reader.GetString("parcelname");
}
}
}

View File

@@ -231,27 +231,20 @@ namespace OpenSim.Data.MySQL
asset.Description, asset.ID, asset.Description.Length, assetDescription.Length);
}
byte[] data;
if (m_enableCompression)
{
MemoryStream outputStream = new MemoryStream();
using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false))
{
// Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue));
// We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream.
compressionStream.Close();
byte[] compressedData = outputStream.ToArray();
asset.Data = compressedData;
}
using MemoryStream inMs = new(asset.Data);
using MemoryStream outputStream = new();
using GZipStream compressionStream = new(outputStream, CompressionMode.Compress);
inMs.CopyTo(compressionStream);
compressionStream.Flush();
data = outputStream.ToArray();
}
else
data = asset.Data;
byte[] hash;
using (HashAlgorithm hasher = SHA256.Create())
hash = hasher.ComputeHash(asset.Data);
// m_log.DebugFormat(
// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}",
// asset.ID, asset.Name, hash, compressedData.Length);
byte[] hash = SHA256.HashData(data);
try
{
@@ -297,7 +290,7 @@ namespace OpenSim.Data.MySQL
dbcon))
{
cmd.Parameters.AddWithValue("?Hash", hash);
cmd.Parameters.AddWithValue("?Data", asset.Data);
cmd.Parameters.AddWithValue("?Data", data);
cmd.ExecuteNonQuery();
}
}

View File

@@ -567,3 +567,8 @@ ALTER TABLE `regionsettings` ADD COLUMN `TerrainPBR2` varchar(36) NOT NULL DEFAU
ALTER TABLE `regionsettings` ADD COLUMN `TerrainPBR3` varchar(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000';
ALTER TABLE `regionsettings` ADD COLUMN `TerrainPBR4` varchar(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000';
COMMIT;
:VERSION 67 #----- add rez start string param column
BEGIN;
ALTER TABLE `prims` ADD COLUMN `StartStr` text default NULL;
COMMIT;

View File

@@ -46,7 +46,7 @@ namespace OpenSim.Data.PGSQL
protected string m_ConnectionString;
protected PGSQLManager m_database; //used for parameter type translation
protected Dictionary<string, FieldInfo> m_Fields =
new Dictionary<string, FieldInfo>();
new Dictionary<string, FieldInfo>(StringComparer.OrdinalIgnoreCase);
protected Dictionary<string, string> m_FieldTypes = new Dictionary<string, string>();
@@ -101,7 +101,7 @@ namespace OpenSim.Data.PGSQL
private void LoadFieldTypes()
{
m_FieldTypes = new Dictionary<string, string>();
m_FieldTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
string query = string.Format(@"select column_name,data_type
from INFORMATION_SCHEMA.COLUMNS
@@ -268,7 +268,7 @@ namespace OpenSim.Data.PGSQL
List<T> result = new List<T>();
if (cmd.Connection == null)
{
cmd.Connection = new NpgsqlConnection(m_connectionString);
cmd.Connection = new NpgsqlConnection(m_ConnectionString);
}
if (cmd.Connection.State == ConnectionState.Closed)
{

View File

@@ -174,7 +174,7 @@ namespace OpenSim.Data.PGSQL
{
return (bool)value;
}
if (valueType == typeof(Byte[]))
if (valueType == typeof(byte[]))
{
return value;
}

View File

@@ -163,6 +163,10 @@ namespace OpenSim.Data.PGSQL
byte[] data = (byte[])reader["lnkstBinData"];
grp.LinksetData = LinksetData.FromBin(data);
}
if(reader["StartStr"] is not DBNull)
{
grp.RezStringParameter = (string)reader["StartStr"];
}
}
else
{
@@ -352,7 +356,7 @@ namespace OpenSim.Data.PGSQL
""ClickAction"" = :ClickAction, ""Material"" = :Material, ""CollisionSound"" = :CollisionSound, ""CollisionSoundVolume"" = :CollisionSoundVolume, ""PassTouches"" = :PassTouches,
""LinkNumber"" = :LinkNumber, ""MediaURL"" = :MediaURL, ""DynAttrs"" = :DynAttrs, ""Vehicle"" = :Vehicle,
""PhysInertia"" = :PhysInertia, ""standtargetx"" =:standtargetx, ""standtargety"" =:standtargety, ""standtargetz"" =:standtargetz,
""sitactrange"" =:sitactrange, ""pseudocrc"" = :pseudocrc, ""sopanims"" = :sopanims, ""lnkstBinData"" = :lnkstBinData
""sitactrange"" =:sitactrange, ""pseudocrc"" = :pseudocrc, ""sopanims"" = :sopanims, ""lnkstBinData"" = :lnkstBinData, ""StartStr"" = :StartStr
WHERE ""UUID"" = :UUID ;
@@ -368,7 +372,7 @@ namespace OpenSim.Data.PGSQL
""ForceMouselook"", ""ScriptAccessPin"", ""AllowedDrop"", ""DieAtEdge"", ""SalePrice"", ""SaleType"", ""ColorR"", ""ColorG"", ""ColorB"", ""ColorA"",
""ParticleSystem"", ""ClickAction"", ""Material"", ""CollisionSound"", ""CollisionSoundVolume"", ""PassTouches"", ""LinkNumber"", ""MediaURL"", ""DynAttrs"",
""PhysicsShapeType"", ""Density"", ""GravityModifier"", ""Friction"", ""Restitution"", ""PassCollisions"", ""RotationAxisLocks"", ""RezzerID"" , ""Vehicle"", ""PhysInertia"",
""standtargetx"", ""standtargety"", ""standtargetz"", ""sitactrange"", ""pseudocrc"",""sopanims"",""lnkstBinData""
""standtargetx"", ""standtargety"", ""standtargetz"", ""sitactrange"", ""pseudocrc"",""sopanims"",""lnkstBinData"", ""StartStr""
) Select
:UUID, :CreationDate, :Name, :Text, :Description, :SitName, :TouchName, :ObjectFlags, :OwnerMask, :NextOwnerMask, :GroupMask,
:EveryoneMask, :BaseMask, :PositionX, :PositionY, :PositionZ, :GroupPositionX, :GroupPositionY, :GroupPositionZ, :VelocityX,
@@ -380,7 +384,7 @@ namespace OpenSim.Data.PGSQL
:ForceMouselook, :ScriptAccessPin, :AllowedDrop, :DieAtEdge, :SalePrice, :SaleType, :ColorR, :ColorG, :ColorB, :ColorA,
:ParticleSystem, :ClickAction, :Material, :CollisionSound, :CollisionSoundVolume, :PassTouches, :LinkNumber, :MediaURL, :DynAttrs,
:PhysicsShapeType, :Density, :GravityModifier, :Friction, :Restitution, :PassCollisions, :RotationAxisLocks, :RezzerID, :Vehicle, :PhysInertia,
:standtargetx, :standtargety, :standtargetz,:sitactrange, :pseudocrc, :sopanims, :lnkstBinData
:standtargetx, :standtargety, :standtargetz,:sitactrange, :pseudocrc, :sopanims, :lnkstBinData, :StartStr
where not EXISTS (SELECT ""UUID"" FROM prims WHERE ""UUID"" = :UUID);
";
@@ -1883,6 +1887,11 @@ namespace OpenSim.Data.PGSQL
else
parameters.Add(_Database.CreateParameterNullBytea("lnkstBinData"));
if(prim.IsRoot)
parameters.Add(_Database.CreateParameter("StartStr", prim.ParentGroup.RezStringParameter));
else
parameters.Add(_Database.CreateParameter("StartStr", null));
return parameters.ToArray();
}

View File

@@ -26,11 +26,9 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
using System.Text;
using Npgsql;
using log4net;
@@ -107,21 +105,20 @@ namespace OpenSim.Data.PGSQL
return retUA;
}
*/
/*
public UserAccountData Get(UUID principalID, UUID scopeID)
{
UserAccountData ret = new UserAccountData();
ret.Data = new Dictionary<string, string>();
string sql = string.Format(@"select * from {0} where ""PrincipalID"" = :principalID", m_Realm);
string sql = string.Format(@"select * from {0} where ""PrincipalID"" = :PrincipalID", m_Realm);
if (scopeID != UUID.Zero)
sql += @" and ""ScopeID"" = :scopeID";
sql += @" and ""ScopeID"" = :ScopeID";
using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID));
cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID));
cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID));
cmd.Parameters.Add(m_database.CreateParameter("ScopeID", scopeID));
conn.Open();
using (NpgsqlDataReader result = cmd.ExecuteReader())
@@ -159,7 +156,6 @@ namespace OpenSim.Data.PGSQL
return null;
}
public override bool Store(UserAccountData data)
{
if (data.Data.ContainsKey("PrincipalID"))
@@ -269,7 +265,6 @@ namespace OpenSim.Data.PGSQL
}
return false;
}
*/
/*
public UserAccountData[] Get(string[] keys, string[] vals)
{
@@ -298,26 +293,23 @@ namespace OpenSim.Data.PGSQL
return new UserAccountData[0];
string sql = "";
UUID scope_id;
UUID.TryParse(scopeID.ToString(), out scope_id);
using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
if (words.Length == 1)
{
sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (""FirstName"" ilike :search or ""LastName"" ilike :search)", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("scopeID", (UUID)scope_id));
cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", (UUID)UUID.Zero));
sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (LOWER(""FirstName"" COLLATE ""en_US.utf8"") like LOWER(:search) or LOWER(""LastName"" COLLATE ""en_US.utf8"") like LOWER(:search))", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("ScopeID", scopeID));
cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", UUID.Zero));
cmd.Parameters.Add(m_database.CreateParameter("search", "%" + words[0] + "%"));
}
else
{
sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (""FirstName"" ilike :searchFirst or ""LastName"" ilike :searchLast)", m_Realm);
sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (LOWER(""FirstName"" COLLATE ""en_US.utf8"") like LOWER(:searchFirst) or LOWER(""LastName"" COLLATE ""en_US.utf8"") like LOWER(:searchLast))", m_Realm);
cmd.Parameters.Add(m_database.CreateParameter("searchFirst", "%" + words[0] + "%"));
cmd.Parameters.Add(m_database.CreateParameter("searchLast", "%" + words[1] + "%"));
cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", (UUID)UUID.Zero));
cmd.Parameters.Add(m_database.CreateParameter("ScopeID", (UUID)scope_id));
cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", UUID.Zero));
cmd.Parameters.Add(m_database.CreateParameter("ScopeID", scopeID));
}
cmd.Connection = conn;
cmd.CommandText = sql;
@@ -328,7 +320,27 @@ namespace OpenSim.Data.PGSQL
public UserAccountData[] GetUsersWhere(UUID scopeID, string where)
{
return null;
using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString))
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
// Fix case sensitivity for PostgreSQL column names
where = where.Replace("PrincipalID", "\"PrincipalID\"")
.Replace("ScopeID", "\"ScopeID\"")
.Replace("FirstName", "\"FirstName\"")
.Replace("LastName", "\"LastName\"");
if (!scopeID.IsZero())
{
where = "(\"ScopeID\"=:ScopeID or \"ScopeID\"='00000000-0000-0000-0000-000000000000') and (" + where + ")";
cmd.Parameters.Add(m_database.CreateParameter("ScopeID", scopeID));
}
cmd.CommandText = String.Format("select * from {0} where " + where, m_Realm);
cmd.Connection = conn;
conn.Open();
return DoQuery(cmd);
}
}
}
}

View File

@@ -32,11 +32,9 @@ using System.IO;
using System.IO.Compression;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Data;
using Npgsql;
namespace OpenSim.Data.PGSQL
@@ -58,7 +56,7 @@ namespace OpenSim.Data.PGSQL
private bool m_enableCompression = false;
private PGSQLManager m_database;
private string m_connectionString;
private object m_dbLock = new object();
private object m_dbLock = new();
/// <summary>
/// We can reuse this for all hashing since all methods are single-threaded through m_dbBLock
@@ -212,6 +210,9 @@ namespace OpenSim.Data.PGSQL
{
// m_log.DebugFormat("[XASSETS DB]: Storing asset {0} {1}", asset.Name, asset.ID);
if (!UUID.TryParse(asset.ID, out UUID asset_id))
return;
lock (m_dbLock)
{
using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString))
@@ -244,7 +245,6 @@ namespace OpenSim.Data.PGSQL
using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false))
{
// Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue));
// We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream.
compressionStream.Close();
byte[] compressedData = outputStream.ToArray();
@@ -254,12 +254,9 @@ namespace OpenSim.Data.PGSQL
byte[] hash = hasher.ComputeHash(asset.Data);
UUID asset_id;
UUID.TryParse(asset.ID, out asset_id);
// m_log.DebugFormat(
// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}",
// asset.ID, asset.Name, hash, compressedData.Length);
//m_log.DebugFormat(
// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}",
// asset.ID, asset.Name, hash, compressedData.Length);
try
{
@@ -352,6 +349,9 @@ namespace OpenSim.Data.PGSQL
if ((now - Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates)
return;
if(!UUID.TryParse(assetMetadata.ID, out UUID asset_id))
return;
lock (m_dbLock)
{
using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString))
@@ -362,14 +362,11 @@ namespace OpenSim.Data.PGSQL
try
{
UUID asset_id;
UUID.TryParse(assetMetadata.ID, out asset_id);
using (cmd)
{
// create unix epoch time
cmd.Parameters.Add(m_database.CreateParameter("id", asset_id));
cmd.Parameters.Add(m_database.CreateParameter("access_time", (int)Utils.DateTimeToUnixTime(now)));
cmd.Parameters.Add(m_database.CreateParameter("ID", asset_id));
cmd.Parameters.Add(m_database.CreateParameter("AccessTime", (int)Utils.DateTimeToUnixTime(now)));
cmd.ExecuteNonQuery();
}
}
@@ -407,7 +404,7 @@ namespace OpenSim.Data.PGSQL
{
if (dbReader.Read())
{
// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid);
// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid);
exists = true;
}
}
@@ -431,7 +428,7 @@ namespace OpenSim.Data.PGSQL
public bool[] AssetsExist(UUID[] uuids)
{
if (uuids.Length == 0)
return new bool[0];
return [];
HashSet<UUID> exist = new HashSet<UUID>();
@@ -467,7 +464,7 @@ namespace OpenSim.Data.PGSQL
/// <returns>true if it exists, false otherwise.</returns>
public bool ExistsAsset(UUID uuid)
{
// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid);
// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid);
bool assetExists = false;
@@ -478,7 +475,7 @@ namespace OpenSim.Data.PGSQL
dbcon.Open();
using (NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT id FROM XAssetsMeta WHERE id=:ID", dbcon))
{
cmd.Parameters.Add(m_database.CreateParameter("id", uuid));
cmd.Parameters.Add(m_database.CreateParameter("ID", uuid));
try
{
@@ -493,7 +490,7 @@ namespace OpenSim.Data.PGSQL
}
catch (Exception e)
{
m_log.Error(string.Format("[XASSETS DB]: PGSql failure fetching asset {0}", uuid), e);
m_log.Error($"[XASSETS DB]: PGSql failure fetching asset {uuid}", e);
}
}
}
@@ -522,9 +519,9 @@ namespace OpenSim.Data.PGSQL
dbcon.Open();
using(NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT name, description, access_time, ""AssetType"", temporary, id, asset_flags, creatorid
FROM XAssetsMeta
LIMIT :start, :count",dbcon))
LIMIT :start, :count", dbcon))
{
cmd.Parameters.Add(m_database.CreateParameter("start",start));
cmd.Parameters.Add(m_database.CreateParameter("start", start));
cmd.Parameters.Add(m_database.CreateParameter("count", count));
try
@@ -574,7 +571,7 @@ namespace OpenSim.Data.PGSQL
using (NpgsqlCommand cmd = new NpgsqlCommand(@"delete from XAssetsMeta where id=:ID", dbcon))
{
cmd.Parameters.Add(m_database.CreateParameter(id, id));
cmd.Parameters.Add(m_database.CreateParameter("ID", id));
cmd.ExecuteNonQuery();
}

View File

@@ -26,42 +26,23 @@
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
using System.Reflection;
using System.Text;
using log4net;
using Npgsql;
using NpgsqlTypes;
namespace OpenSim.Data.PGSQL
{
public class PGSQLXInventoryData : IXInventoryData
{
// private static readonly ILog m_log = LogManager.GetLogger(
// MethodBase.GetCurrentMethod().DeclaringType);
// private static readonly ILog m_log = LogManager.GetLogger(
// MethodBase.GetCurrentMethod().DeclaringType);
private PGSQLFolderHandler m_Folders;
private PGSQLItemHandler m_Items;
public PGSQLXInventoryData(string conn, string realm)
{
m_Folders = new PGSQLFolderHandler(
conn, "inventoryfolders", "InventoryStore");
m_Items = new PGSQLItemHandler(
conn, "inventoryitems", String.Empty);
}
public static UUID str2UUID(string strUUID)
{
UUID newUUID = UUID.Zero;
UUID.TryParse(strUUID, out newUUID);
return newUUID;
m_Folders = new PGSQLFolderHandler(conn, "inventoryfolders", "InventoryStore");
m_Items = new PGSQLItemHandler(conn, "inventoryitems", string.Empty);
}
public XInventoryFolder[] GetFolder(string field, string val)
@@ -128,7 +109,7 @@ namespace OpenSim.Data.PGSQL
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
return m_Items.GetActiveGestures(principalID.ToString());
return m_Items.GetActiveGestures(principalID);
}
public int GetAssetPermissions(UUID principalID, UUID assetID)
@@ -144,9 +125,14 @@ namespace OpenSim.Data.PGSQL
{
}
public bool MoveItem(string id, string newParent)
public bool MoveItem(string idstr, string newParentstr)
{
XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id });
if(!UUID.TryParse(idstr, out UUID id))
return false;
if(!UUID.TryParse(newParentstr, out UUID newParent))
return false;
XInventoryItem[] retrievedItems = Get(["inventoryID"], [idstr]);
if (retrievedItems.Length == 0)
return false;
@@ -156,7 +142,7 @@ namespace OpenSim.Data.PGSQL
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""inventoryID"" = :InventoryID", m_Realm);
cmd.CommandText = $"update {m_Realm} set parentFolderID = :ParentFolderID where inventoryID = :InventoryID";
cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newParent));
cmd.Parameters.Add(m_database.CreateParameter("InventoryID", id ));
cmd.Connection = conn;
@@ -172,19 +158,20 @@ namespace OpenSim.Data.PGSQL
return true;
}
public XInventoryItem[] GetActiveGestures(string principalID)
{
return UUID.TryParse(principalID, out UUID princID) ? GetActiveGestures(princID) : [];
}
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString))
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
// cmd.CommandText = String.Format(@"select * from inventoryitems where ""avatarID"" = :uuid and ""assetType"" = :type and ""flags"" = 1", m_Realm);
// cmd.CommandText = String.Format(@"select * from inventoryitems where ""avatarID"" = :uuid and ""assetType"" = :type and ""flags"" = 1", m_Realm);
cmd.CommandText = String.Format(@"select * from inventoryitems where ""avatarID"" = :uuid and ""assetType"" = :type and ""flags"" = 1");
UUID princID = UUID.Zero;
UUID.TryParse(principalID, out princID);
cmd.CommandText = "select * from inventoryitems where avatarID = :uuid and assetType = :type and flags = 1";
cmd.Parameters.Add(m_database.CreateParameter("uuid", principalID));
cmd.Parameters.Add(m_database.CreateParameter("type", (int)AssetType.Gesture));
@@ -208,11 +195,11 @@ namespace OpenSim.Data.PGSQL
and ""assetID"" = :AssetID
group by ""assetID"" ", m_Realm);
*/
cmd.CommandText = String.Format(@"select bit_or(""inventoryCurrentPermissions"") as ""inventoryCurrentPermissions""
cmd.CommandText = @"select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions
from inventoryitems
where ""avatarID""::uuid = :PrincipalID
and ""assetID""::uuid = :AssetID
group by ""assetID"" ");
where avatarID::uuid = :PrincipalID
and assetID::uuid = :AssetID
group by assetID";
cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID));
cmd.Parameters.Add(m_database.CreateParameter("AssetID", assetID));
@@ -255,7 +242,13 @@ namespace OpenSim.Data.PGSQL
public bool MoveFolder(string id, string newParentFolderID)
{
XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id });
if(!UUID.TryParse(id, out UUID foldID))
return false;
if(!UUID.TryParse(newParentFolderID, out UUID newPar))
return false;
XInventoryFolder[] folders = Get(["folderID"], [id]);
if (folders.Length == 0)
return false;
@@ -266,13 +259,8 @@ namespace OpenSim.Data.PGSQL
{
using (NpgsqlCommand cmd = new NpgsqlCommand())
{
UUID foldID = UUID.Zero;
UUID.TryParse(id, out foldID);
UUID newPar = UUID.Zero;
UUID.TryParse(newParentFolderID, out newPar);
cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""folderID"" = :folderID", m_Realm);
cmd.CommandText = $"update {m_Realm} set parentFolderID = :ParentFolderID where folderID = :folderID";
cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newPar));
cmd.Parameters.Add(m_database.CreateParameter("folderID", foldID));
cmd.Connection = conn;
@@ -311,20 +299,19 @@ namespace OpenSim.Data.PGSQL
protected bool IncrementFolderVersion(string folderID)
{
// m_log.DebugFormat("[PGSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID);
// Util.PrintCallStack();
//m_log.DebugFormat("[PGSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID);
//Util.PrintCallStack();
string sql = @"update inventoryfolders set version=version+1 where ""folderID"" = :folderID";
if(!UUID.TryParse(folderID, out UUID foldID))
return false;
string sql = @"update inventoryfolders set version=version+1 where folderID = :folderID";
using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString))
{
using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn))
{
UUID foldID = UUID.Zero;
UUID.TryParse(folderID, out foldID);
conn.Open();
cmd.Parameters.Add( m_database.CreateParameter("folderID", foldID) );
try

View File

@@ -139,7 +139,7 @@ CREATE TABLE Tmp_inventoryitems
INSERT INTO Tmp_inventoryitems ("inventoryID", "assetID", "assetType", "parentFolderID", "avatarID", "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", "creatorID", "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", "groupID", "groupOwned", "flags", "inventoryGroupPermissions")
SELECT cast("inventoryID" as uuid), cast("assetID" as uuid), "assetType", cast("parentFolderID" as uuid), cast("avatarID" as uuid), "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", cast("creatorID" as uuid), "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", cast("groupID" as uuid), "groupOwned", "flags", "inventoryGroupPermissions"
SELECT cast("inventoryID" as uuid), cast("assetID" as uuid), "assetType", cast("parentFolderID" as uuid), cast("avatarID" as uuid), "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", cast("creatorID" as uuid), "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "saleType", "creationDate", cast("groupID" as uuid), "groupOwned", "flags", "inventoryGroupPermissions"
FROM inventoryitems ;
DROP TABLE inventoryitems;

View File

@@ -1304,3 +1304,8 @@ CREATE TABLE "regionextra"
PRIMARY KEY ("RegionID", "Name")
);
COMMIT;
:VERSION 58 #----- add rez start string param column
BEGIN;
ALTER TABLE `prims` ADD COLUMN `StartStr` TEXT;
COMMIT;

View File

@@ -419,3 +419,8 @@ ALTER TABLE regionsettings ADD COLUMN TerrainPBR2 char(36) NOT NULL DEFAULT '000
ALTER TABLE regionsettings ADD COLUMN TerrainPBR3 char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000';
ALTER TABLE regionsettings ADD COLUMN TerrainPBR4 char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000';
COMMIT;
:VERSION 43 #----- add rez start string param column
BEGIN;
ALTER TABLE `prims` ADD COLUMN `StartStr` TEXT;
COMMIT;

View File

@@ -89,14 +89,16 @@ CREATE TABLE IF NOT EXISTS userdata (
commit;
:VERSION 3 # -------------------------------
:VERSION 4 # -------------------------------
begin;
CREATE TABLE IF NOT EXISTS usersettings (
useruuid char(36) NOT NULL,
imviaemail binary(1) NOT NULL,
visible binary(1) NOT NULL,
email varchar(254) NOT NULL,
PRIMARY KEY (useruuid)
)
);
commit;

View File

@@ -31,11 +31,7 @@ using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{

View File

@@ -30,11 +30,7 @@ using System.Data;
using System.Reflection;
using System.Collections.Generic;
using log4net;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using OpenMetaverse;
using OpenSim.Framework;
@@ -55,7 +51,7 @@ namespace OpenSim.Data.SQLite
private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, CreatorID=:CreatorID, Data=:Data where UUID=:UUID";
private const string assetSelect = "select * from assets";
private SqliteConnection m_conn;
private SQLiteConnection m_conn;
protected virtual Assembly Assembly
{
@@ -81,13 +77,13 @@ namespace OpenSim.Data.SQLite
/// <param name="dbconnect">connect string</param>
override public void Initialise(string dbconnect)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
if (dbconnect.Length == 0)
{
dbconnect = "URI=file:Asset.db,version=3";
dbconnect = "URI=file:Asset.db";
}
m_conn = new SqliteConnection(dbconnect);
m_conn = new SQLiteConnection(dbconnect);
m_conn.Open();
Migration m = new Migration(m_conn, Assembly, "AssetStore");
@@ -105,9 +101,9 @@ namespace OpenSim.Data.SQLite
{
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(SelectAssetSQL, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":UUID", uuid.ToString()));
using (IDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
@@ -157,17 +153,17 @@ namespace OpenSim.Data.SQLite
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(UpdateAssetSQL, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(UpdateAssetSQL, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Name", assetName));
cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription));
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags));
cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID));
cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data));
cmd.Parameters.Add(new SQLiteParameter(":UUID", asset.FullID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":Name", assetName));
cmd.Parameters.Add(new SQLiteParameter(":Description", assetDescription));
cmd.Parameters.Add(new SQLiteParameter(":Type", asset.Type));
cmd.Parameters.Add(new SQLiteParameter(":Local", asset.Local));
cmd.Parameters.Add(new SQLiteParameter(":Temporary", asset.Temporary));
cmd.Parameters.Add(new SQLiteParameter(":Flags", asset.Flags));
cmd.Parameters.Add(new SQLiteParameter(":CreatorID", asset.Metadata.CreatorID));
cmd.Parameters.Add(new SQLiteParameter(":Data", asset.Data));
cmd.ExecuteNonQuery();
return true;
@@ -178,17 +174,17 @@ namespace OpenSim.Data.SQLite
{
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(InsertAssetSQL, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(InsertAssetSQL, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Name", assetName));
cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription));
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags));
cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID));
cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data));
cmd.Parameters.Add(new SQLiteParameter(":UUID", asset.FullID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":Name", assetName));
cmd.Parameters.Add(new SQLiteParameter(":Description", assetDescription));
cmd.Parameters.Add(new SQLiteParameter(":Type", asset.Type));
cmd.Parameters.Add(new SQLiteParameter(":Local", asset.Local));
cmd.Parameters.Add(new SQLiteParameter(":Temporary", asset.Temporary));
cmd.Parameters.Add(new SQLiteParameter(":Flags", asset.Flags));
cmd.Parameters.Add(new SQLiteParameter(":CreatorID", asset.Metadata.CreatorID));
cmd.Parameters.Add(new SQLiteParameter(":Data", asset.Data));
cmd.ExecuteNonQuery();
return true;
@@ -231,7 +227,7 @@ namespace OpenSim.Data.SQLite
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(sql, m_conn))
{
using (IDataReader reader = cmd.ExecuteReader())
{
@@ -307,10 +303,10 @@ namespace OpenSim.Data.SQLite
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(SelectAssetMetadataSQL, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(SelectAssetMetadataSQL, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":start", start));
cmd.Parameters.Add(new SqliteParameter(":count", count));
cmd.Parameters.Add(new SQLiteParameter(":start", start));
cmd.Parameters.Add(new SQLiteParameter(":count", count));
using (IDataReader reader = cmd.ExecuteReader())
{
@@ -359,7 +355,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
override public void Initialise()
{
Initialise("URI=file:Asset.db,version=3");
Initialise("URI=file:Asset.db");
}
/// <summary>
@@ -380,9 +376,9 @@ namespace OpenSim.Data.SQLite
{
lock (this)
{
using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(DeleteAssetSQL, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":UUID", uuid.ToString()));
cmd.ExecuteNonQuery();
}
}

View File

@@ -34,11 +34,7 @@ using log4net;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -50,7 +46,7 @@ namespace OpenSim.Data.SQLite
private List<string> m_ColumnNames;
private int m_LastExpire;
protected static SqliteConnection m_Connection;
protected static SQLiteConnection m_Connection;
private static bool m_initialized = false;
protected virtual Assembly Assembly
@@ -65,9 +61,9 @@ namespace OpenSim.Data.SQLite
if (!m_initialized)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
m_Connection = new SqliteConnection(connectionString);
m_Connection = new SQLiteConnection(connectionString);
m_Connection.Open();
Migration m = new Migration(m_Connection, Assembly, "AuthStore");
@@ -83,9 +79,9 @@ namespace OpenSim.Data.SQLite
ret.Data = new Dictionary<string, object>();
IDataReader result;
using (SqliteCommand cmd = new SqliteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID"))
using (SQLiteCommand cmd = new SQLiteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID"))
{
cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":PrincipalID", principalID.ToString()));
result = ExecuteReader(cmd, m_Connection);
}
@@ -137,7 +133,7 @@ namespace OpenSim.Data.SQLite
foreach (object o in data.Data.Values)
values[i++] = o.ToString();
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
if (Get(data.PrincipalID) != null)
{
@@ -150,13 +146,13 @@ namespace OpenSim.Data.SQLite
if (!first)
update += ", ";
update += "`" + field + "` = :" + field;
cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field]));
cmd.Parameters.Add(new SQLiteParameter(":" + field, data.Data[field]));
first = false;
}
update += " where UUID = :UUID";
cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":UUID", data.PrincipalID.ToString()));
cmd.CommandText = update;
try
@@ -180,9 +176,9 @@ namespace OpenSim.Data.SQLite
String.Join("`, `", fields) +
"`) values (:UUID, :" + String.Join(", :", fields) + ")";
cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":UUID", data.PrincipalID.ToString()));
foreach (string field in fields)
cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field]));
cmd.Parameters.Add(new SQLiteParameter(":" + field, data.Data[field]));
cmd.CommandText = insert;
@@ -206,7 +202,7 @@ namespace OpenSim.Data.SQLite
public bool SetDataItem(UUID principalID, string item, string value)
{
using (SqliteCommand cmd = new SqliteCommand("update `" + m_Realm +
using (SQLiteCommand cmd = new SQLiteCommand("update `" + m_Realm +
"` set `" + item + "` = " + value + " where UUID = '" + principalID.ToString() + "'"))
{
if (ExecuteNonQuery(cmd, m_Connection) > 0)
@@ -221,7 +217,7 @@ namespace OpenSim.Data.SQLite
if (System.Environment.TickCount - m_LastExpire > 30000)
DoExpire();
using (SqliteCommand cmd = new SqliteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() +
using (SQLiteCommand cmd = new SQLiteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() +
"', '" + token + "', datetime('now', 'localtime', '+" + lifetime.ToString() + " minutes'))"))
{
if (ExecuteNonQuery(cmd, m_Connection) > 0)
@@ -236,7 +232,7 @@ namespace OpenSim.Data.SQLite
if (System.Environment.TickCount - m_LastExpire > 30000)
DoExpire();
using (SqliteCommand cmd = new SqliteCommand("update tokens set validity = datetime('now', 'localtime', '+" + lifetime.ToString() +
using (SQLiteCommand cmd = new SQLiteCommand("update tokens set validity = datetime('now', 'localtime', '+" + lifetime.ToString() +
" minutes') where UUID = '" + principalID.ToString() + "' and token = '" + token + "' and validity > datetime('now', 'localtime')"))
{
if (ExecuteNonQuery(cmd, m_Connection) > 0)
@@ -248,7 +244,7 @@ namespace OpenSim.Data.SQLite
private void DoExpire()
{
using (SqliteCommand cmd = new SqliteCommand("delete from tokens where validity < datetime('now', 'localtime')"))
using (SQLiteCommand cmd = new SQLiteCommand("delete from tokens where validity < datetime('now', 'localtime')"))
ExecuteNonQuery(cmd, m_Connection);
m_LastExpire = System.Environment.TickCount;

View File

@@ -33,11 +33,7 @@ using System.Threading;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -56,7 +52,7 @@ namespace OpenSim.Data.SQLite
public bool Delete(UUID principalID, string name)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm);
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());

View File

@@ -30,11 +30,8 @@ using System.Collections.Generic;
using System.Data;
using System.Reflection;
using log4net;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
@@ -46,7 +43,7 @@ namespace OpenSim.Data.SQLite
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private SqliteConnection m_connection;
private SQLiteConnection m_connection;
private string m_connectionString;
private FieldInfo[] m_Fields;
@@ -69,13 +66,13 @@ namespace OpenSim.Data.SQLite
public void Initialise(string connectionString)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
m_connectionString = connectionString;
m_log.Info("[ESTATE DB]: Sqlite - connecting: "+m_connectionString);
m_connection = new SqliteConnection(m_connectionString);
m_connection = new SQLiteConnection(m_connectionString);
m_connection.Open();
Migration m = new Migration(m_connection, Assembly, "EstateStore");
@@ -103,7 +100,7 @@ namespace OpenSim.Data.SQLite
{
string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_map left join estate_settings on estate_map.EstateID = estate_settings.EstateID where estate_settings.EstateID is not null and RegionID = :RegionID";
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddWithValue(":RegionID", regionID.ToString());
@@ -112,7 +109,7 @@ namespace OpenSim.Data.SQLite
}
}
private EstateSettings DoLoad(SqliteCommand cmd, UUID regionID, bool create)
private EstateSettings DoLoad(SQLiteCommand cmd, UUID regionID, bool create)
{
EstateSettings es = new EstateSettings();
es.OnSave += StoreEstateSettings;
@@ -121,7 +118,7 @@ namespace OpenSim.Data.SQLite
{
r = cmd.ExecuteReader();
}
catch (SqliteException)
catch (SQLiteException)
{
m_log.Error("[SQLITE]: There was an issue loading the estate settings. This can happen the first time running OpenSimulator with CSharpSqlite the first time. OpenSimulator will probably crash, restart it and it should be good to go.");
}
@@ -188,7 +185,7 @@ namespace OpenSim.Data.SQLite
{
List<string> names = new List<string>(FieldList);
using (SqliteCommand cmd = m_connection.CreateCommand())
using (SQLiteCommand cmd = m_connection.CreateCommand())
{
if (es.EstateID < 100)
{
@@ -238,7 +235,7 @@ namespace OpenSim.Data.SQLite
foreach (string f in fields)
terms.Add(f+" = :"+f);
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "update estate_settings set " + String.Join(", ", terms.ToArray()) + " where EstateID = :EstateID";
cmd.Parameters.AddWithValue(":EstateID", es.EstateID);
@@ -273,7 +270,7 @@ namespace OpenSim.Data.SQLite
IDataReader r;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "select * from estateban where EstateID = :EstateID";
cmd.Parameters.AddWithValue(":EstateID", es.EstateID);
@@ -297,7 +294,7 @@ namespace OpenSim.Data.SQLite
private void SaveBanList(EstateSettings es)
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "delete from estateban where EstateID = :EstateID";
cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString());
@@ -323,7 +320,7 @@ namespace OpenSim.Data.SQLite
void SaveUUIDList(uint EstateID, string table, UUID[] data)
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "delete from "+table+" where EstateID = :EstateID";
cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString());
@@ -350,7 +347,7 @@ namespace OpenSim.Data.SQLite
List<UUID> uuids = new List<UUID>();
IDataReader r;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID";
cmd.Parameters.AddWithValue(":EstateID", EstateID);
@@ -376,7 +373,7 @@ namespace OpenSim.Data.SQLite
{
string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_settings where estate_settings.EstateID = :EstateID";
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddWithValue(":EstateID", estateID.ToString());
@@ -403,7 +400,7 @@ namespace OpenSim.Data.SQLite
string sql = "select EstateID from estate_settings where estate_settings.EstateName = :EstateName";
IDataReader r;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddWithValue(":EstateName", search);
@@ -427,7 +424,7 @@ namespace OpenSim.Data.SQLite
string sql = "select EstateID from estate_settings";
IDataReader r;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = sql;
@@ -450,7 +447,7 @@ namespace OpenSim.Data.SQLite
string sql = "select EstateID from estate_settings where estate_settings.EstateOwner = :EstateOwner";
IDataReader r;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = sql;
cmd.Parameters.AddWithValue(":EstateOwner", ownerID);
@@ -469,10 +466,10 @@ namespace OpenSim.Data.SQLite
public bool LinkRegion(UUID regionID, int estateID)
{
using(SqliteTransaction transaction = m_connection.BeginTransaction())
using(SQLiteTransaction transaction = m_connection.BeginTransaction())
{
// Delete any existing estate mapping for this region.
using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using(SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "delete from estate_map where RegionID = :RegionID";
cmd.Transaction = transaction;
@@ -481,7 +478,7 @@ namespace OpenSim.Data.SQLite
cmd.ExecuteNonQuery();
}
using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using(SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)";
cmd.Transaction = transaction;

View File

@@ -31,11 +31,7 @@ using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -44,11 +40,11 @@ namespace OpenSim.Data.SQLite
/// </summary>
public class SQLiteFramework
{
protected Object m_lockObject = new Object();
protected object m_lockObject = new Object();
protected SQLiteFramework(string connectionString)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
}
//////////////////////////////////////////////////////////////
@@ -56,17 +52,10 @@ namespace OpenSim.Data.SQLite
// All non queries are funneled through one connection
// to increase performance a little
//
protected int ExecuteNonQuery(SqliteCommand cmd, SqliteConnection connection)
protected int ExecuteNonQuery(SQLiteCommand cmd, SQLiteConnection connection)
{
lock (connection)
{
/*
SqliteConnection newConnection =
(SqliteConnection)((ICloneable)connection).Clone();
newConnection.Open();
cmd.Connection = newConnection;
*/
cmd.Connection = connection;
//Console.WriteLine("XXX " + cmd.CommandText);
@@ -74,12 +63,12 @@ namespace OpenSim.Data.SQLite
}
}
protected IDataReader ExecuteReader(SqliteCommand cmd, SqliteConnection connection)
protected IDataReader ExecuteReader(SQLiteCommand cmd, SQLiteConnection connection)
{
lock (connection)
{
//SqliteConnection newConnection =
// (SqliteConnection)((ICloneable)connection).Clone();
//SQLiteConnection newConnection =
// (SQLiteConnection)((ICloneable)connection).Clone();
//newConnection.Open();
//cmd.Connection = newConnection;

View File

@@ -31,11 +31,7 @@ using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -53,7 +49,7 @@ namespace OpenSim.Data.SQLite
public FriendsData[] GetFriends(string userID)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm);
cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString());
@@ -69,7 +65,7 @@ namespace OpenSim.Data.SQLite
public override bool Delete(string principalID, string friend)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm);
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());

View File

@@ -30,11 +30,8 @@ using System.Collections.Generic;
using System.Data;
using System.Reflection;
using log4net;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using OpenMetaverse;
using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
@@ -52,7 +49,7 @@ namespace OpenSim.Data.SQLite
protected string m_Realm;
protected FieldInfo m_DataField = null;
protected static SqliteConnection m_Connection;
protected static SQLiteConnection m_Connection;
private static bool m_initialized;
protected virtual Assembly Assembly
@@ -67,14 +64,14 @@ namespace OpenSim.Data.SQLite
if (!m_initialized)
{
m_Connection = new SqliteConnection(connectionString);
m_Connection = new SQLiteConnection(connectionString);
//Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString));
m_Connection.Open();
if (storeName != String.Empty)
{
//SqliteConnection newConnection =
// (SqliteConnection)((ICloneable)m_Connection).Clone();
//SQLiteConnection newConnection =
// (SQLiteConnection)((ICloneable)m_Connection).Clone();
//newConnection.Open();
//Migration m = new Migration(newConnection, Assembly, storeName);
@@ -132,11 +129,11 @@ namespace OpenSim.Data.SQLite
List<string> terms = new List<string>();
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
for (int i = 0 ; i < fields.Length ; i++)
{
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
cmd.Parameters.Add(new SQLiteParameter(":" + fields[i], keys[i]));
terms.Add("`" + fields[i] + "` = :" + fields[i]);
}
@@ -151,7 +148,7 @@ namespace OpenSim.Data.SQLite
}
}
protected T[] DoQuery(SqliteCommand cmd)
protected T[] DoQuery(SQLiteCommand cmd)
{
IDataReader reader = ExecuteReader(cmd, m_Connection);
if (reader == null)
@@ -215,7 +212,7 @@ namespace OpenSim.Data.SQLite
public virtual T[] Get(string where)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
string query = String.Format("select * from {0} where {1}",
m_Realm, where);
@@ -228,7 +225,7 @@ namespace OpenSim.Data.SQLite
public virtual bool Store(T row)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
string query = "";
List<String> names = new List<String>();
@@ -238,7 +235,7 @@ namespace OpenSim.Data.SQLite
{
names.Add(fi.Name);
values.Add(":" + fi.Name);
cmd.Parameters.Add(new SqliteParameter(":" + fi.Name, fi.GetValue(row).ToString()));
cmd.Parameters.Add(new SQLiteParameter(":" + fi.Name, fi.GetValue(row).ToString()));
}
if (m_DataField != null)
@@ -250,7 +247,7 @@ namespace OpenSim.Data.SQLite
{
names.Add(kvp.Key);
values.Add(":" + kvp.Key);
cmd.Parameters.Add(new SqliteParameter(":" + kvp.Key, kvp.Value));
cmd.Parameters.Add(new SQLiteParameter(":" + kvp.Key, kvp.Value));
}
}
@@ -277,11 +274,11 @@ namespace OpenSim.Data.SQLite
List<string> terms = new List<string>();
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
for (int i = 0 ; i < fields.Length ; i++)
{
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
cmd.Parameters.Add(new SQLiteParameter(":" + fields[i], keys[i]));
terms.Add("`" + fields[i] + "` = :" + fields[i]);
}

View File

@@ -33,7 +33,7 @@ using System.Threading;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
using Mono.Data.Sqlite;
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -69,7 +69,7 @@ namespace OpenSim.Data.SQLite
public void DeleteOld()
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("delete from {0} where TMStamp < datetime('now', '-2 day') ", m_Realm);

View File

@@ -31,11 +31,7 @@ using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -54,7 +50,7 @@ namespace OpenSim.Data.SQLite
public bool Delete(UUID agentID, UUID muteID, string muteName)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = "delete from MuteList where `AgentID` = :AgentID and `MuteID` = :MuteID and `MuteName` = :MuteName";

View File

@@ -32,11 +32,8 @@ using System.Drawing;
using System.IO;
using System.Reflection;
using log4net;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
@@ -66,18 +63,18 @@ namespace OpenSim.Data.SQLite
private const string regionSpawnPointsSelect = "select * from spawn_points";
private DataSet ds;
private SqliteDataAdapter primDa;
private SqliteDataAdapter shapeDa;
private SqliteDataAdapter itemsDa;
private SqliteDataAdapter terrainDa;
private SqliteDataAdapter landDa;
private SqliteDataAdapter landAccessListDa;
private SqliteDataAdapter regionSettingsDa;
private SqliteDataAdapter regionWindlightDa;
private SqliteDataAdapter regionEnvironmentDa;
private SqliteDataAdapter regionSpawnPointsDa;
private SQLiteDataAdapter primDa;
private SQLiteDataAdapter shapeDa;
private SQLiteDataAdapter itemsDa;
private SQLiteDataAdapter terrainDa;
private SQLiteDataAdapter landDa;
private SQLiteDataAdapter landAccessListDa;
private SQLiteDataAdapter regionSettingsDa;
private SQLiteDataAdapter regionWindlightDa;
private SQLiteDataAdapter regionEnvironmentDa;
private SQLiteDataAdapter regionSpawnPointsDa;
private SqliteConnection m_conn;
private SQLiteConnection m_conn;
private String m_connectionString;
protected virtual Assembly Assembly
@@ -113,46 +110,46 @@ namespace OpenSim.Data.SQLite
{
try
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
m_connectionString = connectionString;
ds = new DataSet("Region");
m_log.Info("[SQLITE REGION DB]: Sqlite - connecting: " + connectionString);
m_conn = new SqliteConnection(m_connectionString);
m_conn = new SQLiteConnection(m_connectionString);
m_conn.Open();
SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn);
primDa = new SqliteDataAdapter(primSelectCmd);
SQLiteCommand primSelectCmd = new SQLiteCommand(primSelect, m_conn);
primDa = new SQLiteDataAdapter(primSelectCmd);
SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn);
shapeDa = new SqliteDataAdapter(shapeSelectCmd);
// SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa);
SQLiteCommand shapeSelectCmd = new SQLiteCommand(shapeSelect, m_conn);
shapeDa = new SQLiteDataAdapter(shapeSelectCmd);
// SQLiteCommandBuilder shapeCb = new SQLiteCommandBuilder(shapeDa);
SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn);
itemsDa = new SqliteDataAdapter(itemsSelectCmd);
SQLiteCommand itemsSelectCmd = new SQLiteCommand(itemsSelect, m_conn);
itemsDa = new SQLiteDataAdapter(itemsSelectCmd);
SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn);
terrainDa = new SqliteDataAdapter(terrainSelectCmd);
SQLiteCommand terrainSelectCmd = new SQLiteCommand(terrainSelect, m_conn);
terrainDa = new SQLiteDataAdapter(terrainSelectCmd);
SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn);
landDa = new SqliteDataAdapter(landSelectCmd);
SQLiteCommand landSelectCmd = new SQLiteCommand(landSelect, m_conn);
landDa = new SQLiteDataAdapter(landSelectCmd);
SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn);
landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd);
SQLiteCommand landAccessListSelectCmd = new SQLiteCommand(landAccessListSelect, m_conn);
landAccessListDa = new SQLiteDataAdapter(landAccessListSelectCmd);
SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn);
regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd);
SQLiteCommand regionSettingsSelectCmd = new SQLiteCommand(regionSettingsSelect, m_conn);
regionSettingsDa = new SQLiteDataAdapter(regionSettingsSelectCmd);
SqliteCommand regionWindlightSelectCmd = new SqliteCommand(regionWindlightSelect, m_conn);
regionWindlightDa = new SqliteDataAdapter(regionWindlightSelectCmd);
SQLiteCommand regionWindlightSelectCmd = new SQLiteCommand(regionWindlightSelect, m_conn);
regionWindlightDa = new SQLiteDataAdapter(regionWindlightSelectCmd);
SqliteCommand regionEnvironmentSelectCmd = new SqliteCommand(regionEnvironmentSelect, m_conn);
regionEnvironmentDa = new SqliteDataAdapter(regionEnvironmentSelectCmd);
SQLiteCommand regionEnvironmentSelectCmd = new SQLiteCommand(regionEnvironmentSelect, m_conn);
regionEnvironmentDa = new SQLiteDataAdapter(regionEnvironmentSelectCmd);
SqliteCommand regionSpawnPointsSelectCmd = new SqliteCommand(regionSpawnPointsSelect, m_conn);
regionSpawnPointsDa = new SqliteDataAdapter(regionSpawnPointsSelectCmd);
SQLiteCommand regionSpawnPointsSelectCmd = new SQLiteCommand(regionSpawnPointsSelect, m_conn);
regionSpawnPointsDa = new SQLiteDataAdapter(regionSpawnPointsSelectCmd);
// This actually does the roll forward assembly stuff
Migration m = new Migration(m_conn, Assembly, "RegionStore");
@@ -404,25 +401,25 @@ namespace OpenSim.Data.SQLite
// remove region's spawnpoints
using (
SqliteCommand cmd =
new SqliteCommand("delete from spawn_points where RegionID=:RegionID",
SQLiteCommand cmd =
new SQLiteCommand("delete from spawn_points where RegionID=:RegionID",
m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionID", rs.RegionUUID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":RegionID", rs.RegionUUID.ToString()));
cmd.ExecuteNonQuery();
}
}
foreach (SpawnPoint sp in rs.SpawnPoints())
{
using (SqliteCommand cmd = new SqliteCommand("insert into spawn_points(RegionID, Yaw, Pitch, Distance)" +
using (SQLiteCommand cmd = new SQLiteCommand("insert into spawn_points(RegionID, Yaw, Pitch, Distance)" +
"values ( :RegionID, :Yaw, :Pitch, :Distance)", m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionID", rs.RegionUUID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Yaw", sp.Yaw));
cmd.Parameters.Add(new SqliteParameter(":Pitch", sp.Pitch));
cmd.Parameters.Add(new SqliteParameter(":Distance", sp.Distance));
cmd.Parameters.Add(new SQLiteParameter(":RegionID", rs.RegionUUID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":Yaw", sp.Yaw));
cmd.Parameters.Add(new SQLiteParameter(":Pitch", sp.Pitch));
cmd.Parameters.Add(new SQLiteParameter(":Distance", sp.Distance));
cmd.ExecuteNonQuery();
}
}
@@ -662,12 +659,13 @@ namespace OpenSim.Data.SQLite
byte[] data = (byte[])primRow["lnkstBinData"];
group.LinksetData = LinksetData.FromBin(data);
}
if(primRow["StartStr"] is not DBNull)
{
group.RezStringParameter = (string)primRow["StartStr"];
}
createdObjects.Add(group.UUID, group);
retvals.Add(group);
LoadItems(prim);
}
}
catch (Exception e)
@@ -764,9 +762,9 @@ namespace OpenSim.Data.SQLite
{
lock (ds)
{
using (SqliteCommand cmd = new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID", m_conn))
using (SQLiteCommand cmd = new SQLiteCommand("delete from terrain where RegionUUID=:RegionUUID", m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
cmd.ExecuteNonQuery();
}
@@ -781,11 +779,11 @@ namespace OpenSim.Data.SQLite
m_log.DebugFormat("{0} Storing terrain format {1}", LogHeader, terrainDBRevision);
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(sql, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision));
cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":Revision", terrainDBRevision));
cmd.Parameters.Add(new SQLiteParameter(":Heightfield", terrainDBblob));
cmd.ExecuteNonQuery();
}
}
@@ -801,9 +799,9 @@ namespace OpenSim.Data.SQLite
lock (ds)
{
using (
SqliteCommand cmd = new SqliteCommand("delete from bakedterrain where RegionUUID=:RegionUUID", m_conn))
SQLiteCommand cmd = new SQLiteCommand("delete from bakedterrain where RegionUUID=:RegionUUID", m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
cmd.ExecuteNonQuery();
}
@@ -818,11 +816,11 @@ namespace OpenSim.Data.SQLite
m_log.DebugFormat("{0} Storing bakedterrain format {1}", LogHeader, terrainDBRevision);
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(sql, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision));
cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":Revision", terrainDBRevision));
cmd.Parameters.Add(new SQLiteParameter(":Heightfield", terrainDBblob));
cmd.ExecuteNonQuery();
}
}
@@ -852,9 +850,9 @@ namespace OpenSim.Data.SQLite
String sql = "select RegionUUID, Revision, Heightfield from terrain" +
" where RegionUUID=:RegionUUID order by Revision desc";
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(sql, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
using (IDataReader row = cmd.ExecuteReader())
{
@@ -887,9 +885,9 @@ namespace OpenSim.Data.SQLite
String sql = "select RegionUUID, Revision, Heightfield from bakedterrain" +
" where RegionUUID=:RegionUUID";
using (SqliteCommand cmd = new SqliteCommand(sql, m_conn))
using (SQLiteCommand cmd = new SQLiteCommand(sql, m_conn))
{
cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":RegionUUID", regionID.ToString()));
using (IDataReader row = cmd.ExecuteReader())
{
@@ -913,15 +911,15 @@ namespace OpenSim.Data.SQLite
// Can't use blanket SQL statements when using SqlAdapters unless you re-read the data into the adapter
// after you're done.
// replaced below code with the SqliteAdapter version.
//using (SqliteCommand cmd = new SqliteCommand("delete from land where UUID=:UUID", m_conn))
//using (SQLiteCommand cmd = new SQLiteCommand("delete from land where UUID=:UUID", m_conn))
//{
// cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString()));
// cmd.Parameters.Add(new SQLiteParameter(":UUID", globalID.ToString()));
// cmd.ExecuteNonQuery();
//}
//using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:UUID", m_conn))
//using (SQLiteCommand cmd = new SQLiteCommand("delete from landaccesslist where LandUUID=:UUID", m_conn))
//{
// cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString()));
// cmd.Parameters.Add(new SQLiteParameter(":UUID", globalID.ToString()));
// cmd.ExecuteNonQuery();
//}
@@ -970,9 +968,9 @@ namespace OpenSim.Data.SQLite
}
// I know this caused someone issues before, but OpenSim is unusable if we leave this stuff around
//using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:LandUUID", m_conn))
//using (SQLiteCommand cmd = new SQLiteCommand("delete from landaccesslist where LandUUID=:LandUUID", m_conn))
//{
// cmd.Parameters.Add(new SqliteParameter(":LandUUID", parcel.LandData.GlobalID.ToString()));
// cmd.Parameters.Add(new SQLiteParameter(":LandUUID", parcel.LandData.GlobalID.ToString()));
// cmd.ExecuteNonQuery();
// }
@@ -1050,7 +1048,7 @@ namespace OpenSim.Data.SQLite
regionSettingsDa.Update(ds, "regionsettings");
regionWindlightDa.Update(ds, "regionwindlight");
}
catch (SqliteException SqlEx)
catch (SQLiteException SqlEx)
{
throw new Exception(
"There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!",
@@ -1251,6 +1249,7 @@ namespace OpenSim.Data.SQLite
createCol(prims, "sopanims", typeof(byte[]));
createCol(prims, "lnkstBinData", typeof(byte[]));
createCol(prims, "StartStr", typeof(string));
// Add in contraints
prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] };
@@ -1297,8 +1296,8 @@ namespace OpenSim.Data.SQLite
createCol(shapes, "LastAttachPoint", typeof(Int32));
// text TODO: this isn't right, but I'm not sure the right
// way to specify this as a blob atm
createCol(shapes, "Texture", typeof(Byte[]));
createCol(shapes, "ExtraParams", typeof(Byte[]));
createCol(shapes, "Texture", typeof(byte[]));
createCol(shapes, "ExtraParams", typeof(byte[]));
createCol(shapes, "Media", typeof(String));
createCol(shapes, "MatOvrd", typeof(byte[]));
shapes.PrimaryKey = new DataColumn[] { shapes.Columns["UUID"] };
@@ -2217,6 +2216,11 @@ namespace OpenSim.Data.SQLite
row["lnkstBinData"] = prim.ParentGroup.LinksetData.ToBin();
else
row["lnkstBinData"] = null;
if (prim.IsRoot)
row["StartStr"] = prim.ParentGroup.RezStringParameter;
else
row["StartStr"] = null;
}
/// <summary>
@@ -2581,7 +2585,7 @@ namespace OpenSim.Data.SQLite
/// front. If we just have a list of b, c, etc... we can
/// generate these strings instead of typing them out.
/// </remarks>
private static SqliteCommand createInsertCommand(string table, DataTable dt)
private static SQLiteCommand createInsertCommand(string table, DataTable dt)
{
string[] cols = new string[dt.Columns.Count];
for (int i = 0; i < dt.Columns.Count; i++)
@@ -2597,13 +2601,13 @@ namespace OpenSim.Data.SQLite
sql += String.Join(", :", cols);
sql += ")";
// m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql);
SqliteCommand cmd = new SqliteCommand(sql);
SQLiteCommand cmd = new SQLiteCommand(sql);
// this provides the binding for all our parameters, so
// much less code than it used to be
foreach (DataColumn col in dt.Columns)
{
cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType));
cmd.Parameters.Add(createSQLiteParameter(col.ColumnName, col.DataType));
}
return cmd;
}
@@ -2616,7 +2620,7 @@ namespace OpenSim.Data.SQLite
/// <param name="pk"></param>
/// <param name="dt"></param>
/// <returns>the created command</returns>
private static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt)
private static SQLiteCommand createUpdateCommand(string table, string pk, DataTable dt)
{
string sql = "update " + table + " set ";
string subsql = String.Empty;
@@ -2631,14 +2635,14 @@ namespace OpenSim.Data.SQLite
}
sql += subsql;
sql += " where " + pk;
SqliteCommand cmd = new SqliteCommand(sql);
SQLiteCommand cmd = new SQLiteCommand(sql);
// this provides the binding for all our parameters, so
// much less code than it used to be
foreach (DataColumn col in dt.Columns)
{
cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType));
cmd.Parameters.Add(createSQLiteParameter(col.ColumnName, col.DataType));
}
return cmd;
}
@@ -2650,7 +2654,7 @@ namespace OpenSim.Data.SQLite
/// <param name="pk"></param>
/// <param name="dt"></param>
/// <returns>the created command</returns>
private static SqliteCommand createUpdateCommand(string table, string pk1, string pk2, DataTable dt)
private static SQLiteCommand createUpdateCommand(string table, string pk1, string pk2, DataTable dt)
{
string sql = "update " + table + " set ";
string subsql = String.Empty;
@@ -2665,14 +2669,14 @@ namespace OpenSim.Data.SQLite
}
sql += subsql;
sql += " where " + pk1 + " and " + pk2;
SqliteCommand cmd = new SqliteCommand(sql);
SQLiteCommand cmd = new SQLiteCommand(sql);
// this provides the binding for all our parameters, so
// much less code than it used to be
foreach (DataColumn col in dt.Columns)
{
cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType));
cmd.Parameters.Add(createSQLiteParameter(col.ColumnName, col.DataType));
}
return cmd;
}
@@ -2715,7 +2719,7 @@ namespace OpenSim.Data.SQLite
///<summary>
/// This is a convenience function that collapses 5 repetitive
/// lines for defining SqliteParameters to 2 parameters:
/// lines for defining SQLiteParameters to 2 parameters:
/// column name and database type.
///
/// It assumes certain conventions like :param as the param
@@ -2724,9 +2728,9 @@ namespace OpenSim.Data.SQLite
/// for us.
///</summary>
///<returns>a built sqlite parameter</returns>
private static SqliteParameter createSqliteParameter(string name, Type type)
private static SQLiteParameter createSQLiteParameter(string name, Type type)
{
SqliteParameter param = new SqliteParameter();
SQLiteParameter param = new SQLiteParameter();
param.ParameterName = ":" + name;
param.DbType = dbtypeFromType(type);
param.SourceColumn = name;
@@ -2739,7 +2743,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupPrimCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupPrimCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]);
da.InsertCommand.Connection = conn;
@@ -2747,8 +2751,8 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand = createUpdateCommand("prims", "UUID=:UUID", ds.Tables["prims"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from prims where UUID = :UUID");
delete.Parameters.Add(createSqliteParameter("UUID", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from prims where UUID = :UUID");
delete.Parameters.Add(createSQLiteParameter("UUID", typeof(String)));
delete.Connection = conn;
da.DeleteCommand = delete;
}
@@ -2758,7 +2762,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupItemsCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("primitems", ds.Tables["primitems"]);
da.InsertCommand.Connection = conn;
@@ -2766,8 +2770,8 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand = createUpdateCommand("primitems", "itemID = :itemID", ds.Tables["primitems"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from primitems where itemID = :itemID");
delete.Parameters.Add(createSqliteParameter("itemID", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from primitems where itemID = :itemID");
delete.Parameters.Add(createSQLiteParameter("itemID", typeof(String)));
delete.Connection = conn;
da.DeleteCommand = delete;
}
@@ -2777,7 +2781,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupTerrainCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupTerrainCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("terrain", ds.Tables["terrain"]);
da.InsertCommand.Connection = conn;
@@ -2788,7 +2792,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupLandCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupLandCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("land", ds.Tables["land"]);
da.InsertCommand.Connection = conn;
@@ -2796,8 +2800,8 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand = createUpdateCommand("land", "UUID=:UUID", ds.Tables["land"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from land where UUID=:UUID");
delete.Parameters.Add(createSqliteParameter("UUID", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from land where UUID=:UUID");
delete.Parameters.Add(createSQLiteParameter("UUID", typeof(String)));
da.DeleteCommand = delete;
da.DeleteCommand.Connection = conn;
}
@@ -2807,7 +2811,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupLandAccessCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupLandAccessCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("landaccesslist", ds.Tables["landaccesslist"]);
da.InsertCommand.Connection = conn;
@@ -2815,14 +2819,14 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand = createUpdateCommand("landaccesslist", "LandUUID=:landUUID", "AccessUUID=:AccessUUID", ds.Tables["landaccesslist"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from landaccesslist where LandUUID= :LandUUID and AccessUUID= :AccessUUID");
delete.Parameters.Add(createSqliteParameter("LandUUID", typeof(String)));
delete.Parameters.Add(createSqliteParameter("AccessUUID", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from landaccesslist where LandUUID= :LandUUID and AccessUUID= :AccessUUID");
delete.Parameters.Add(createSQLiteParameter("LandUUID", typeof(String)));
delete.Parameters.Add(createSQLiteParameter("AccessUUID", typeof(String)));
da.DeleteCommand = delete;
da.DeleteCommand.Connection = conn;
}
private void setupRegionSettingsCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupRegionSettingsCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("regionsettings", ds.Tables["regionsettings"]);
da.InsertCommand.Connection = conn;
@@ -2835,7 +2839,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupRegionWindlightCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupRegionWindlightCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("regionwindlight", ds.Tables["regionwindlight"]);
da.InsertCommand.Connection = conn;
@@ -2843,20 +2847,20 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand.Connection = conn;
}
private void setupRegionEnvironmentCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupRegionEnvironmentCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("regionenvironment", ds.Tables["regionenvironment"]);
da.InsertCommand.Connection = conn;
da.UpdateCommand = createUpdateCommand("regionenvironment", "region_id=:region_id", ds.Tables["regionenvironment"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from regionenvironment where region_id= :region_id");
delete.Parameters.Add(createSqliteParameter("region_id", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from regionenvironment where region_id= :region_id");
delete.Parameters.Add(createSQLiteParameter("region_id", typeof(String)));
da.DeleteCommand = delete;
da.DeleteCommand.Connection = conn;
}
private void setupRegionSpawnPointsCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupRegionSpawnPointsCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("spawn_points", ds.Tables["spawn_points"]);
da.InsertCommand.Connection = conn;
@@ -2869,7 +2873,7 @@ namespace OpenSim.Data.SQLite
/// </summary>
/// <param name="da"></param>
/// <param name="conn"></param>
private void setupShapeCommands(SqliteDataAdapter da, SqliteConnection conn)
private void setupShapeCommands(SQLiteDataAdapter da, SQLiteConnection conn)
{
da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]);
da.InsertCommand.Connection = conn;
@@ -2877,8 +2881,8 @@ namespace OpenSim.Data.SQLite
da.UpdateCommand = createUpdateCommand("primshapes", "UUID=:UUID", ds.Tables["primshapes"]);
da.UpdateCommand.Connection = conn;
SqliteCommand delete = new SqliteCommand("delete from primshapes where UUID = :UUID");
delete.Parameters.Add(createSqliteParameter("UUID", typeof(String)));
SQLiteCommand delete = new SQLiteCommand("delete from primshapes where UUID = :UUID");
delete.Parameters.Add(createSQLiteParameter("UUID", typeof(String)));
delete.Connection = conn;
da.DeleteCommand = delete;
}

View File

@@ -31,11 +31,7 @@ using System.Collections.Generic;
using System.Data;
using OpenMetaverse;
using OpenSim.Framework;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -66,7 +62,7 @@ namespace OpenSim.Data.SQLite
if (words.Length > 2)
return new UserAccountData[0];
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
if (words.Length == 1)
{

View File

@@ -30,11 +30,8 @@ using System.Collections.Generic;
using System.Data;
using System.Reflection;
using log4net;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
@@ -47,7 +44,7 @@ namespace OpenSim.Data.SQLite
private static readonly ILog m_log =
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private SqliteConnection m_connection;
private SQLiteConnection m_connection;
private string m_connectionString;
private Dictionary<string, FieldInfo> m_FieldMap =
@@ -69,13 +66,13 @@ namespace OpenSim.Data.SQLite
public void Initialise(string connectionString)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
m_connectionString = connectionString;
m_log.Info("[PROFILES_DATA]: Sqlite - connecting: "+m_connectionString);
m_connection = new SqliteConnection(m_connectionString);
m_connection = new SQLiteConnection(m_connectionString);
m_connection.Open();
Migration m = new Migration(m_connection, Assembly, "UserProfiles");
@@ -94,7 +91,7 @@ namespace OpenSim.Data.SQLite
string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = :Id";
IDataReader reader = null;
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", creatorId);
@@ -190,7 +187,7 @@ namespace OpenSim.Data.SQLite
ad.ExpirationDate = (int)epochexp.TotalSeconds;
try {
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":ClassifiedId", ad.ClassifiedId.ToString());
@@ -230,7 +227,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":ClassifiedId", recordId.ToString());
@@ -257,7 +254,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":AdId", ad.ClassifiedId.ToString());
@@ -303,7 +300,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", avatarId.ToString());
@@ -340,7 +337,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":CreatorId", avatarId.ToString());
@@ -416,7 +413,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
int top_pick;
int.TryParse(pick.TopPick.ToString(), out top_pick);
@@ -459,7 +456,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":PickId", pickId.ToString());
@@ -487,7 +484,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", notes.UserId.ToString());
@@ -533,7 +530,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
@@ -562,7 +559,7 @@ namespace OpenSim.Data.SQLite
query += "SELECT * FROM userprofile WHERE ";
query += "useruuid = :Id";
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", props.UserId.ToString());
@@ -639,7 +636,7 @@ namespace OpenSim.Data.SQLite
query += ":profileFirstImage, ";
query += ":profileFirstText)";
using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand put = (SQLiteCommand)m_connection.CreateCommand())
{
put.CommandText = query;
put.Parameters.AddWithValue(":userId", props.UserId.ToString());
@@ -678,7 +675,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":profileURL", props.WebUrl);
@@ -715,7 +712,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":WantMask", up.WantToMask);
@@ -751,7 +748,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":ImViaEmail", pref.IMViaEmail);
@@ -785,7 +782,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue("?Id", pref.UserId.ToString());
@@ -803,7 +800,7 @@ namespace OpenSim.Data.SQLite
query = "INSERT INTO usersettings VALUES ";
query += "(:Id,'false','false', :Email)";
using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand put = (SQLiteCommand)m_connection.CreateCommand())
{
put.Parameters.AddWithValue(":Id", pref.UserId.ToString());
put.Parameters.AddWithValue(":Email", pref.EMail);
@@ -835,7 +832,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", props.UserId.ToString());
@@ -856,7 +853,7 @@ namespace OpenSim.Data.SQLite
query += ":DataKey,";
query += ":DataVal) ";
using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand put = (SQLiteCommand)m_connection.CreateCommand())
{
put.Parameters.AddWithValue(":Id", props.UserId.ToString());
put.Parameters.AddWithValue(":TagId", props.TagId.ToString());
@@ -891,7 +888,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":UserId", props.UserId.ToString());
@@ -921,7 +918,7 @@ namespace OpenSim.Data.SQLite
try
{
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = string.Format(query, "\"classifieds\"");
cmd.Parameters.AddWithValue(":Id", avatarId.ToString());
@@ -935,7 +932,7 @@ namespace OpenSim.Data.SQLite
}
}
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = string.Format(query, "\"userpicks\"");
cmd.Parameters.AddWithValue(":Id", avatarId.ToString());
@@ -951,7 +948,7 @@ namespace OpenSim.Data.SQLite
query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = :Id";
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
using (SQLiteCommand cmd = (SQLiteCommand)m_connection.CreateCommand())
{
cmd.CommandText = query;
cmd.Parameters.AddWithValue(":Id", avatarId.ToString());

View File

@@ -27,11 +27,7 @@
using System;
using System.Data;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
namespace OpenSim.Data.SQLite
{
@@ -85,7 +81,7 @@ namespace OpenSim.Data.SQLite
/// front. If we just have a list of b, c, etc... we can
/// generate these strings instead of typing them out.
/// </remarks>
public static SqliteCommand createInsertCommand(string table, DataTable dt)
public static SQLiteCommand createInsertCommand(string table, DataTable dt)
{
string[] cols = new string[dt.Columns.Count];
@@ -101,13 +97,13 @@ namespace OpenSim.Data.SQLite
sql += ") values (:";
sql += String.Join(", :", cols);
sql += ")";
SqliteCommand cmd = new SqliteCommand(sql);
SQLiteCommand cmd = new SQLiteCommand(sql);
// this provides the binding for all our parameters, so
// much less code than it used to be
foreach (DataColumn col in dt.Columns)
{
cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType));
cmd.Parameters.Add(createSQLiteParameter(col.ColumnName, col.DataType));
}
return cmd;
}
@@ -119,7 +115,7 @@ namespace OpenSim.Data.SQLite
/// <param name="pk"></param>
/// <param name="dt"></param>
/// <returns>the created command</returns>
public static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt)
public static SQLiteCommand createUpdateCommand(string table, string pk, DataTable dt)
{
string sql = "update " + table + " set ";
string subsql = String.Empty;
@@ -134,14 +130,14 @@ namespace OpenSim.Data.SQLite
}
sql += subsql;
sql += " where " + pk;
SqliteCommand cmd = new SqliteCommand(sql);
SQLiteCommand cmd = new SQLiteCommand(sql);
// this provides the binding for all our parameters, so
// much less code than it used to be
foreach (DataColumn col in dt.Columns)
{
cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType));
cmd.Parameters.Add(createSQLiteParameter(col.ColumnName, col.DataType));
}
return cmd;
}
@@ -188,7 +184,7 @@ namespace OpenSim.Data.SQLite
///<summary>
/// <para>
/// This is a convenience function that collapses 5 repetitive
/// lines for defining SqliteParameters to 2 parameters:
/// lines for defining SQLiteParameters to 2 parameters:
/// column name and database type.
/// </para>
///
@@ -202,9 +198,9 @@ namespace OpenSim.Data.SQLite
/// <param name="name"></param>
/// <param name="type"></param>
///<returns>a built sqlite parameter</returns>
public static SqliteParameter createSqliteParameter(string name, Type type)
public static SQLiteParameter createSQLiteParameter(string name, Type type)
{
SqliteParameter param = new SqliteParameter();
SQLiteParameter param = new SQLiteParameter();
param.ParameterName = ":" + name;
param.DbType = dbtypeFromType(type);
param.SourceColumn = name;

View File

@@ -29,11 +29,7 @@ using System;
using System.Data;
using System.Reflection;
using System.Collections.Generic;
#if CSharpSqlite
using Community.CsharpSqlite.Sqlite;
#else
using Mono.Data.Sqlite;
#endif
using System.Data.SQLite;
using log4net;
using OpenMetaverse;
using OpenSim.Framework;
@@ -52,7 +48,7 @@ namespace OpenSim.Data.SQLite
public SQLiteXInventoryData(string conn, string realm)
{
DllmapConfigHelper.RegisterAssembly(typeof(SqliteConnection).Assembly);
DllmapConfigHelper.RegisterAssembly(typeof(SQLiteConnection).Assembly);
m_Folders = new SqliteFolderHandler(
conn, "inventoryfolders", "XInventoryStore");
@@ -193,11 +189,11 @@ namespace OpenSim.Data.SQLite
UUID oldParent = retrievedItems[0].parentFolderID;
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where inventoryID = :InventoryID", m_Realm);
cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParent));
cmd.Parameters.Add(new SqliteParameter(":InventoryID", id));
cmd.Parameters.Add(new SQLiteParameter(":ParentFolderID", newParent));
cmd.Parameters.Add(new SQLiteParameter(":InventoryID", id));
if (ExecuteNonQuery(cmd, m_Connection) == 0)
return false;
@@ -211,12 +207,12 @@ namespace OpenSim.Data.SQLite
public XInventoryItem[] GetActiveGestures(UUID principalID)
{
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = :uuid and assetType = :type and flags = 1", m_Realm);
cmd.Parameters.Add(new SqliteParameter(":uuid", principalID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":type", (int)AssetType.Gesture));
cmd.Parameters.Add(new SQLiteParameter(":uuid", principalID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":type", (int)AssetType.Gesture));
return DoQuery(cmd);
}
@@ -226,11 +222,11 @@ namespace OpenSim.Data.SQLite
{
IDataReader reader;
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("select inventoryCurrentPermissions from inventoryitems where avatarID = :PrincipalID and assetID = :AssetID", m_Realm);
cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString()));
cmd.Parameters.Add(new SqliteParameter(":AssetID", assetID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":PrincipalID", principalID.ToString()));
cmd.Parameters.Add(new SQLiteParameter(":AssetID", assetID.ToString()));
reader = ExecuteReader(cmd, m_Connection);
}
@@ -275,11 +271,11 @@ namespace OpenSim.Data.SQLite
UUID oldParentFolderUUID = folders[0].parentFolderID;
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where folderID = :FolderID", m_Realm);
cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParentFolderID));
cmd.Parameters.Add(new SqliteParameter(":FolderID", id));
cmd.Parameters.Add(new SQLiteParameter(":ParentFolderID", newParentFolderID));
cmd.Parameters.Add(new SQLiteParameter(":FolderID", id));
if (ExecuteNonQuery(cmd, m_Connection) == 0)
return false;
@@ -307,10 +303,10 @@ namespace OpenSim.Data.SQLite
// m_log.DebugFormat("[MYSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID);
// Util.PrintCallStack();
using (SqliteCommand cmd = new SqliteCommand())
using (SQLiteCommand cmd = new SQLiteCommand())
{
cmd.CommandText = "update inventoryfolders set version=version+1 where folderID = :folderID";
cmd.Parameters.Add(new SqliteParameter(":folderID", folderID));
cmd.Parameters.Add(new SQLiteParameter(":folderID", folderID));
if(ExecuteNonQuery(cmd, m_Connection) == 0)
return false;

View File

@@ -145,7 +145,7 @@ namespace OpenSim.Framework
}
else // New style version contains no spaces, just version number
{
return $"{Channel} {m_viewerInternal}";
return $"{Channel} {m_viewerInternal}";
}
}
}
@@ -372,7 +372,7 @@ namespace OpenSim.Framework
OSDMap urls = (OSDMap)tmpOSD;
foreach (KeyValuePair<String, OSD> kvp in urls)
{
ServiceURLs[kvp.Key] = kvp.Value;
ServiceURLs[kvp.Key] = kvp.Value.AsString();
//System.Console.WriteLine("XXX " + kvp.Key + "=" + ServiceURLs[kvp.Key]);
}
}

View File

@@ -142,6 +142,7 @@ namespace OpenSim.Framework
sb.AppendASCII($"{kvp.Key} {kvp.Value.Value} {kvp.Value.Key}\n");
return OSUTF8Cached.GetArrayAndRelease(sb);
}
/*
public bool Validate(AnimationSetValidator val)
{

View File

@@ -41,8 +41,8 @@ namespace OpenSim.Framework.AssetLoader.Filesystem
{
public class AssetLoaderFileSystem : IAssetLoader
{
private static readonly UUID LIBRARY_OWNER_ID = new UUID("11111111-1111-0000-0000-000100bba000");
private static readonly string LIBRARY_OWNER_IDstr = "11111111-1111-0000-0000-000100bba000";
private const string LIBRARY_OWNER_IDstr = "11111111-1111-0000-0000-000100bba000";
private static readonly UUID LIBRARY_OWNER_ID = new UUID(LIBRARY_OWNER_IDstr);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -78,9 +78,8 @@ namespace OpenSim.Framework.AssetLoader.Filesystem
if (fInfo.Exists)
{
FileStream fStream = new FileStream(path, FileMode.Open, FileAccess.Read);
byte[] idata = new byte[numBytes];
BinaryReader br = new BinaryReader(fStream);
idata = br.ReadBytes((int)numBytes);
byte[] idata = br.ReadBytes((int)numBytes);
br.Close();
fStream.Close();
info.Data = idata;

View File

@@ -685,10 +685,6 @@ namespace OpenSim.Framework
int index = kvp.Value.FindIndex(delegate(AvatarAttachment a) { return a.ItemID.Equals(itemID); });
if (index >= 0)
{
//m_log.DebugFormat(
// "[AVATAR APPEARANCE]: Detaching attachment {0}, index {1}, point {2}",
// m_attachments[kvp.Key][index].ItemID, index, m_attachments[kvp.Key][index].AttachPoint);
// Remove it from the list of attachments at that attach point
m_attachments[kvp.Key].RemoveAt(index);
@@ -700,7 +696,29 @@ namespace OpenSim.Framework
}
}
}
return false;
}
public bool RemoveAttachment(int attachPoint, UUID itemID)
{
lock (m_attachments)
{
if(m_attachments.TryGetValue(attachPoint, out List<AvatarAttachment> lst) && lst is not null)
{
int index = lst.FindIndex(delegate(AvatarAttachment a) { return a.ItemID.Equals(itemID); });
if (index >= 0)
{
// Remove it from the list of attachments at that attach point
lst.RemoveAt(index);
// And remove the list if there are no more attachments here
if (lst.Count == 0)
m_attachments.Remove(attachPoint);
return true;
}
}
}
return false;
}

View File

@@ -216,7 +216,6 @@ namespace OpenSim.Framework
}
}
}
}
/// <summary>
@@ -312,7 +311,6 @@ namespace OpenSim.Framework
return controldata;
}
public void UnpackUpdateMessage(OSDMap args)
{
OSD osdtmp;

View File

@@ -32,11 +32,15 @@ namespace OpenSim.Framework
public class Constants
{
public const int MaxAgentAttachments = 38;
public const int MaxAgentAnimatedObjectAttachments = 3;
public const int MaxAgentGroups = 60;
public const int MaxEstateAccessIds = 500;
public const int MaxEstateManagers = 20;
public const int MaxProfilePicks = 20;
// 'RegionSize' is the legacy region size.
// DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionInfo.RegionSize[XYZ] as a region might not
// be the legacy region size.
@@ -59,6 +63,8 @@ namespace OpenSim.Framework
public const float MinWaterHeight = 0;
public const float MaxWaterHeight = 8000f;
public const float DefaultLandingBorderBuffer = 5.0f;
public const int MaxTextureResolution = 2048;
public static readonly string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f"; //plywood

View File

@@ -34,21 +34,19 @@ namespace OpenSim.Framework
/// A double dictionary that is thread abort safe.
/// </summary>
/// <remarks>
/// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within
/// a finally section (which can't be interrupted by Thread.Abort()).
/// </remarks>
public class DoubleDictionaryThreadAbortSafe<TKey1, TKey2, TValue>
{
Dictionary<TKey1, TValue> Dictionary1;
Dictionary<TKey2, TValue> Dictionary2;
readonly Dictionary<TKey1, TValue> Dictionary1;
readonly Dictionary<TKey2, TValue> Dictionary2;
private TValue[] m_array;
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
readonly ReaderWriterLockSlim rwLock = new();
public DoubleDictionaryThreadAbortSafe()
{
Dictionary1 = new Dictionary<TKey1,TValue>();
Dictionary2 = new Dictionary<TKey2,TValue>();
Dictionary1 = [];
Dictionary2 = [];
m_array = null;
}
@@ -61,133 +59,102 @@ namespace OpenSim.Framework
~DoubleDictionaryThreadAbortSafe()
{
if(rwLock != null)
rwLock.Dispose();
rwLock?.Dispose();
}
public void Add(TKey1 key1, TKey2 key2, TValue value)
{
bool gotLock = false;
rwLock.EnterWriteLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterWriteLock();
gotLock = true;
}
Dictionary1[key1] = value;
Dictionary2[key2] = value;
m_array = null;
}
finally
{
if (gotLock)
rwLock.ExitWriteLock();
Dictionary1[key1] = value;
Dictionary2[key2] = value;
m_array = null;
}
finally { rwLock.ExitWriteLock(); }
}
public bool Remove(TKey1 key1, TKey2 key2)
{
bool success;
bool gotLock = false;
rwLock.EnterWriteLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterWriteLock();
gotLock = true;
}
success = Dictionary1.Remove(key1);
bool success = Dictionary1.Remove(key1);
success &= Dictionary2.Remove(key2);
m_array = null;
return success;
}
finally
{
if (gotLock)
rwLock.ExitWriteLock();
}
finally { rwLock.ExitWriteLock(); }
}
return success;
public bool Remove(TKey1 key1, TKey2 key2, out TValue value)
{
rwLock.EnterWriteLock();
try
{
bool success = Dictionary1.Remove(key1, out value);
success &= Dictionary2.Remove(key2);
m_array = null;
return success;
}
finally { rwLock.ExitWriteLock(); }
}
public bool Remove(TKey1 key1)
{
bool found = false;
bool gotLock = false;
rwLock.EnterWriteLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterWriteLock();
gotLock = true;
}
// This is an O(n) operation!
TValue value;
if (Dictionary1.TryGetValue(key1, out value))
{
if (Dictionary1.Remove(key1, out TValue value))
{
m_array = null;
foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
{
if (kvp.Value.Equals(value))
{
try { }
finally
{
Dictionary1.Remove(key1);
Dictionary2.Remove(kvp.Key);
m_array = null;
}
found = true;
break;
Dictionary2.Remove(kvp.Key);
return true;
}
}
}
return false;
}
finally
{
if (gotLock)
rwLock.ExitWriteLock();
}
finally { rwLock.ExitWriteLock(); }
}
return found;
public bool Remove(TKey1 key1, out TValue value)
{
rwLock.EnterWriteLock();
try
{
// This is an O(n) operation!
if (Dictionary1.Remove(key1, out value))
{
m_array = null;
foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
{
if (kvp.Value.Equals(value))
{
Dictionary2.Remove(kvp.Key);
return true;
}
}
}
return false;
}
finally { rwLock.ExitWriteLock(); }
}
public bool Remove(TKey2 key2)
{
bool found = false;
bool gotLock = false;
rwLock.EnterWriteLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterWriteLock();
gotLock = true;
}
// This is an O(n) operation!
TValue value;
if (Dictionary2.TryGetValue(key2, out value))
if (Dictionary2.Remove(key2, out TValue value))
{
m_array = null;
foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
{
if (kvp.Value.Equals(value))
@@ -195,49 +162,51 @@ namespace OpenSim.Framework
try { }
finally
{
Dictionary2.Remove(key2);
Dictionary1.Remove(kvp.Key);
m_array = null;
}
found = true;
break;
return true;
}
}
}
return false;
}
finally
{
if (gotLock)
rwLock.ExitWriteLock();
}
finally { rwLock.ExitWriteLock(); }
}
return found;
public bool Remove(TKey2 key2, out TValue value)
{
rwLock.EnterWriteLock();
try
{
// This is an O(n) operation!
if (Dictionary2.Remove(key2, out value))
{
m_array = null;
foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
{
if (kvp.Value.Equals(value))
{
Dictionary1.Remove(kvp.Key);
return true;
}
}
}
return false;
}
finally { rwLock.ExitWriteLock(); }
}
public void Clear()
{
bool gotLock = false;
rwLock.EnterWriteLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterWriteLock();
gotLock = true;
Dictionary1.Clear();
Dictionary2.Clear();
m_array = null;
}
}
finally
{
if (gotLock)
rwLock.ExitWriteLock();
Dictionary1.Clear();
Dictionary2.Clear();
m_array = null;
}
finally { rwLock.ExitWriteLock(); }
}
public int Count
@@ -247,68 +216,42 @@ namespace OpenSim.Framework
public bool ContainsKey(TKey1 key)
{
return Dictionary1.ContainsKey(key);
rwLock.EnterReadLock();
try
{
return Dictionary1.ContainsKey(key);
}
finally { rwLock.ExitReadLock(); }
}
public bool ContainsKey(TKey2 key)
{
return Dictionary2.ContainsKey(key);
rwLock.EnterReadLock();
try
{
return Dictionary2.ContainsKey(key);
}
finally { rwLock.ExitReadLock(); }
}
public bool TryGetValue(TKey1 key, out TValue value)
{
bool success;
bool gotLock = false;
rwLock.EnterReadLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterReadLock();
gotLock = true;
}
success = Dictionary1.TryGetValue(key, out value);
return Dictionary1.TryGetValue(key, out value);
}
finally
{
if (gotLock)
rwLock.ExitReadLock();
}
return success;
finally { rwLock.ExitReadLock(); }
}
public bool TryGetValue(TKey2 key, out TValue value)
{
bool success;
bool gotLock = false;
rwLock.EnterReadLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterReadLock();
gotLock = true;
}
success = Dictionary2.TryGetValue(key, out value);
return Dictionary2.TryGetValue(key, out value);
}
finally
{
if (gotLock)
rwLock.ExitReadLock();
}
return success;
finally { rwLock.ExitReadLock(); }
}
public void ForEach(Action<TValue> action)
@@ -323,76 +266,44 @@ namespace OpenSim.Framework
public void ForEach(Action<KeyValuePair<TKey1, TValue>> action)
{
bool gotLock = false;
rwLock.EnterReadLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterReadLock();
gotLock = true;
}
foreach (KeyValuePair<TKey1, TValue> entry in Dictionary1)
action(entry);
}
finally
{
if (gotLock)
rwLock.ExitReadLock();
}
finally { rwLock.ExitReadLock(); }
}
public void ForEach(Action<KeyValuePair<TKey2, TValue>> action)
{
bool gotLock = false;
rwLock.EnterReadLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterReadLock();
gotLock = true;
}
foreach (KeyValuePair<TKey2, TValue> entry in Dictionary2)
action(entry);
}
finally
{
if (gotLock)
rwLock.ExitReadLock();
}
finally { rwLock.ExitReadLock(); }
}
public TValue FindValue(Predicate<TValue> predicate)
{
TValue[] values = GetArray();
int len = values.Length;
for (int i = 0; i < len; ++i)
for (int i = 0; i < values.Length; ++i)
{
if (predicate(values[i]))
return values[i];
}
return default(TValue);
return default;
}
public IList<TValue> FindAll(Predicate<TValue> predicate)
{
IList<TValue> list = new List<TValue>();
IList<TValue> list = [];
TValue[] values = GetArray();
int len = values.Length;
for (int i = 0; i < len; ++i)
for (int i = 0; i < values.Length; ++i)
{
if (predicate(values[i]))
list.Add(values[i]);
@@ -402,21 +313,11 @@ namespace OpenSim.Framework
public int RemoveAll(Predicate<TValue> predicate)
{
IList<TKey1> list = new List<TKey1>();
bool gotUpgradeableLock = false;
IList<TKey1> list = [];
rwLock.EnterUpgradeableReadLock();
try
{
// Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
// the acquision inside the main try. The inner finally block is needed because thread aborts cannot
// interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
try {}
finally
{
rwLock.EnterUpgradeableReadLock();
gotUpgradeableLock = true;
}
foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
{
if (predicate(kvp.Value))
@@ -430,83 +331,43 @@ namespace OpenSim.Framework
list2.Add(kvp.Key);
}
bool gotWriteLock = false;
try
{
try {}
finally
{
rwLock.EnterWriteLock();
gotWriteLock = true;
rwLock.EnterWriteLock();
for (int i = 0; i < list.Count; i++)
Dictionary1.Remove(list[i]);
for (int i = 0; i < list.Count; i++)
Dictionary1.Remove(list[i]);
for (int i = 0; i < list2.Count; i++)
Dictionary2.Remove(list2[i]);
m_array = null;
}
}
finally
{
if (gotWriteLock)
rwLock.ExitWriteLock();
for (int i = 0; i < list2.Count; i++)
Dictionary2.Remove(list2[i]);
m_array = null;
return list.Count;
}
finally { rwLock.ExitWriteLock(); }
}
finally
{
if (gotUpgradeableLock)
rwLock.ExitUpgradeableReadLock();
}
finally { rwLock.ExitUpgradeableReadLock(); }
return list.Count;
}
public TValue[] GetArray()
{
bool gotupLock = false;
rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
rwLock.EnterUpgradeableReadLock();
gotupLock = true;
}
if (m_array == null)
{
bool gotwritelock = false;
rwLock.EnterWriteLock();
try
{
try { }
finally
{
rwLock.EnterWriteLock();
gotwritelock = true;
}
m_array = new TValue[Dictionary1.Count];
Dictionary1.Values.CopyTo(m_array, 0);
}
finally
{
if (gotwritelock)
rwLock.ExitWriteLock();
}
finally { rwLock.ExitWriteLock(); }
}
return m_array;
}
catch
{
return new TValue[0];
}
finally
{
if (gotupLock)
rwLock.ExitUpgradeableReadLock();
}
catch { return []; }
finally { rwLock.ExitUpgradeableReadLock(); }
}
}
}

View File

@@ -410,8 +410,7 @@ namespace OpenSim.Framework
{
if (ban is null)
return;
if (!IsBanned(ban.BannedUserID, 32) &&
(l_EstateBans.Count < (int)Constants.EstateAccessLimits.EstateBans)) //Ignore age-based bans
if (!IsBanned(ban.BannedUserID, 32) && (l_EstateBans.Count < (int)Constants.EstateAccessLimits.EstateBans)) //Ignore age-based bans
l_EstateBans.Add(ban);
}

View File

@@ -67,10 +67,7 @@ namespace OpenSim.Framework
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private void CheckTimer()
{
if (m_purgeTimer == null)
{
m_purgeTimer = new Timer(Purge, null, m_expire, Timeout.Infinite);
}
m_purgeTimer ??= new Timer(Purge, null, m_expire, Timeout.Infinite);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
@@ -106,17 +103,9 @@ namespace OpenSim.Framework
private void Purge(object ignored)
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
if (m_expireControl.Count == 0)
{
DisposeTimer();
@@ -134,16 +123,9 @@ namespace OpenSim.Framework
if(expired.Count > 0)
{
bool gotWriteLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotWriteLock = true;
}
valuesArrayCache = null;
foreach (TKey1 key in expired)
{
@@ -151,11 +133,8 @@ namespace OpenSim.Framework
m_values.Remove(key);
}
}
finally
{
if (gotWriteLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
if (m_expireControl.Count == 0)
DisposeTimer();
else
@@ -164,11 +143,7 @@ namespace OpenSim.Framework
else
m_purgeTimer.Change(m_expire, Timeout.Infinite);
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
@@ -179,28 +154,17 @@ namespace OpenSim.Framework
public void Add(TKey1 key, TValue1 val)
{
bool gotLock = false;
int now = (int)(Util.GetTimeStampMS() - m_startTS) + m_expire;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
m_expireControl[key] = now;
m_values[key] = val;
valuesArrayCache = null;
CheckTimer();
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
@@ -217,7 +181,6 @@ namespace OpenSim.Framework
public void Add(TKey1 key, TValue1 val, int expireMS)
{
bool gotLock = false;
int now;
if (expireMS > 0)
{
@@ -227,78 +190,44 @@ namespace OpenSim.Framework
else
now = int.MinValue;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
m_expireControl[key] = now;
m_values[key] = val;
valuesArrayCache = null;
CheckTimer();
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
public bool Remove(TKey1 key)
{
bool success;
bool gotLock = false;
m_rwLock.EnterWriteLock();
try
{
try {}
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
success = m_expireControl.Remove(key);
bool success = m_expireControl.Remove(key);
success |= m_values.Remove(key);
if(success)
valuesArrayCache = null;
if (m_expireControl.Count == 0)
DisposeTimer();
return success;
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
return success;
finally { m_rwLock.ExitWriteLock(); }
}
public void Clear()
{
bool gotLock = false;
m_rwLock.EnterWriteLock();
try
{
try {}
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
DisposeTimer();
m_expireControl.Clear();
m_values.Clear();
valuesArrayCache = null;
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
public int Count
@@ -314,22 +243,12 @@ namespace OpenSim.Framework
public bool ContainsKey(TKey1 key)
{
bool gotLock = false;
m_rwLock.EnterReadLock();
try
{
try { }
finally
{
m_rwLock.EnterReadLock();
gotLock = true;
}
return m_expireControl.ContainsKey(key);
}
finally
{
if (gotLock)
m_rwLock.ExitReadLock();
}
finally { m_rwLock.ExitReadLock(); }
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
@@ -340,26 +259,14 @@ namespace OpenSim.Framework
public bool ContainsKey(TKey1 key, int expireMS)
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
if(m_expireControl.ContainsKey(key))
{
bool gotWriteLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotWriteLock = true;
}
int now;
if(expireMS > 0)
{
@@ -372,68 +279,33 @@ namespace OpenSim.Framework
m_expireControl[key] = now;
return true;
}
finally
{
if (gotWriteLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
return false;
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
public bool TryGetValue(TKey1 key, out TValue1 value)
{
bool gotLock = false;
m_rwLock.EnterReadLock();
try
{
try {}
finally
{
m_rwLock.EnterReadLock();
gotLock = true;
}
return m_values.TryGetValue(key, out value);
}
finally
{
if (gotLock)
m_rwLock.ExitReadLock();
}
finally { m_rwLock.ExitReadLock(); }
}
public bool TryGetValue(TKey1 key, int expireMS, out TValue1 value)
{
bool success;
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
if(m_values.TryGetValue(key, out value))
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
success = m_values.TryGetValue(key, out value);
if(success)
{
bool gotWriteLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotWriteLock = true;
}
int now;
if(expireMS > 0)
{
@@ -444,56 +316,30 @@ namespace OpenSim.Framework
now = int.MinValue;
m_expireControl[key] = now;
return true;
}
finally
{
if (gotWriteLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
return false;
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
return success;
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
public ref TValue1 TryGetOrDefaultValue(TKey1 key, out bool existed)
{
bool gotLock = false;
m_rwLock.ExitUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.ExitUpgradeableReadLock();
gotLock = true;
}
return ref CollectionsMarshal.GetValueRefOrAddDefault(m_values, key, out existed);
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
public ref TValue1 TryGetOrDefaultValue(TKey1 key, int expireMS, out bool existed)
{
bool gotLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
ref TValue1 ret = ref CollectionsMarshal.GetValueRefOrAddDefault(m_values, key, out existed);
int now;
if (expireMS > 0)
@@ -507,26 +353,16 @@ namespace OpenSim.Framework
m_expireControl[key] = now;
return ref ret;
}
finally
{
if (gotLock)
m_rwLock.EnterWriteLock();
}
finally { m_rwLock.EnterWriteLock(); }
}
public TValue1[] Values
{
get
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
if(valuesArrayCache == null)
{
valuesArrayCache = new TValue1[m_values.Count];
@@ -534,11 +370,7 @@ namespace OpenSim.Framework
}
return valuesArrayCache;
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
}
@@ -547,22 +379,12 @@ namespace OpenSim.Framework
{
get
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
return m_values.Keys;
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
}
*/

View File

@@ -60,10 +60,7 @@ namespace OpenSim.Framework
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
private void CheckTimer()
{
if (m_purgeTimer == null)
{
m_purgeTimer = new Timer(Purge, null, m_expire, Timeout.Infinite);
}
m_purgeTimer ??= new Timer(Purge, null, m_expire, Timeout.Infinite);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
@@ -99,17 +96,9 @@ namespace OpenSim.Framework
private void Purge(object ignored)
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
if (m_dictionary.Count == 0)
{
DisposeTimer();
@@ -127,24 +116,14 @@ namespace OpenSim.Framework
if (expired.Count > 0)
{
bool gotWriteLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotWriteLock = true;
}
foreach (Tkey1 key in expired)
m_dictionary.Remove(key);
}
finally
{
if (gotWriteLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
if (m_dictionary.Count == 0)
DisposeTimer();
else
@@ -153,40 +132,24 @@ namespace OpenSim.Framework
else
m_purgeTimer.Change(m_expire, Timeout.Infinite);
}
finally
{
if (gotLock)
m_rwLock.ExitUpgradeableReadLock();
}
finally { m_rwLock.ExitUpgradeableReadLock(); }
}
public void Add(Tkey1 key)
{
bool gotLock = false;
int now = (int)(Util.GetTimeStampMS() - m_startTS) + m_expire;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
m_dictionary[key] = now;
CheckTimer();
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
public void Add(Tkey1 key, int expireMS)
{
bool gotLock = false;
int now;
if (expireMS > 0)
{
@@ -196,71 +159,38 @@ namespace OpenSim.Framework
else
now = int.MinValue;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
m_dictionary[key] = now;
CheckTimer();
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
public bool Remove(Tkey1 key)
{
bool success;
bool gotLock = false;
m_rwLock.EnterWriteLock();
try
{
try {}
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
success = m_dictionary.Remove(key);
bool success = m_dictionary.Remove(key);
if(m_dictionary.Count == 0)
DisposeTimer();
return success;
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
return success;
}
public void Clear()
{
bool gotLock = false;
m_rwLock.EnterWriteLock();
try
{
try {}
finally
{
m_rwLock.EnterWriteLock();
gotLock = true;
}
m_dictionary.Clear();
DisposeTimer();
}
finally
{
if (gotLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
public int Count
@@ -270,46 +200,24 @@ namespace OpenSim.Framework
public bool ContainsKey(Tkey1 key)
{
bool gotLock = false;
m_rwLock.EnterReadLock();
try
{
try { }
finally
{
m_rwLock.EnterReadLock();
gotLock = true;
}
return m_dictionary.ContainsKey(key);
}
finally
{
if (gotLock)
m_rwLock.ExitReadLock();
}
finally { m_rwLock.ExitReadLock(); }
}
public bool ContainsKey(Tkey1 key, int expireMS)
{
bool gotLock = false;
m_rwLock.EnterUpgradeableReadLock();
try
{
try { }
finally
{
m_rwLock.EnterUpgradeableReadLock();
gotLock = true;
}
if (m_dictionary.ContainsKey(key))
{
bool gotWriteLock = false;
m_rwLock.EnterWriteLock();
try
{
try { }
finally
{
m_rwLock.EnterWriteLock();
gotWriteLock = true;
}
int now;
if (expireMS > 0)
{
@@ -322,44 +230,21 @@ namespace OpenSim.Framework
m_dictionary[key] = now;
return true;
}
finally
{
if (gotWriteLock)
m_rwLock.ExitWriteLock();
}
finally { m_rwLock.ExitWriteLock(); }
}
return false;
}
finally
{
if (gotLock)
m_rwLock.EnterUpgradeableReadLock();
}
finally { m_rwLock.EnterUpgradeableReadLock(); }
}
public bool TryGetValue(Tkey1 key, out int value)
{
bool success;
bool gotLock = false;
m_rwLock.EnterReadLock();
try
{
try {}
finally
{
m_rwLock.EnterReadLock();
gotLock = true;
}
success = m_dictionary.TryGetValue(key, out value);
return m_dictionary.TryGetValue(key, out value);
}
finally
{
if (gotLock)
m_rwLock.ExitReadLock();
}
return success;
finally { m_rwLock.ExitReadLock(); }
}
}
}

View File

@@ -71,7 +71,7 @@ namespace OpenSim.Framework
Flags = OSHTTPURIFlags.None;
try
{
Uri m_checkuri = new Uri(uri);
Uri m_checkuri = new(uri);
if(m_checkuri.Scheme != Uri.UriSchemeHttp && m_checkuri.Scheme != Uri.UriSchemeHttps)
return;
@@ -368,10 +368,11 @@ namespace OpenSim.Framework
private string m_SearchURL = string.Empty;
private string m_DestinationGuideURL = string.Empty;
private string m_economyURL = string.Empty;
private string[] m_StunServers = null;
public GridInfo (IConfigSource config, string defaultURI = "")
{
string[] sections = new string[] {"Const", "Startup", "Hypergrid"};
string[] sections = ["Const", "Startup", "Hypergrid"];
string gatekeeper = Util.GetConfigVarFromSections<string>(config, "GatekeeperURI", sections, string.Empty);
if (string.IsNullOrEmpty(gatekeeper))
@@ -416,8 +417,7 @@ namespace OpenSim.Framework
OSHHTPHost tmp = new OSHHTPHost(alias[i].Trim(), false);
if (tmp.IsValidHost)
{
if (m_gateKeeperAlias == null)
m_gateKeeperAlias = new HashSet<OSHHTPHost>();
m_gateKeeperAlias ??= new HashSet<OSHHTPHost>();
m_gateKeeperAlias.Add(tmp);
}
}
@@ -431,6 +431,7 @@ namespace OpenSim.Framework
m_gridUrlAlias[i++] = a.URI;
}
string home = Util.GetConfigVarFromSections<string>(config, "HomeURI", sections, string.Empty);
if (string.IsNullOrEmpty(home))
@@ -458,14 +459,13 @@ namespace OpenSim.Framework
OSHHTPHost tmp = new OSHHTPHost(alias[i].Trim(), false);
if (tmp.IsValidHost)
{
if (m_homeURLAlias == null)
m_homeURLAlias = new HashSet<OSHHTPHost>();
m_homeURLAlias ??= new HashSet<OSHHTPHost>();
m_homeURLAlias.Add(tmp);
}
}
}
string[] namessections = new string[] { "Const", "GridInfo", "SimulatorFeatures" };
string[] namessections = ["Const", "GridInfo", "SimulatorFeatures"];
m_GridName = Util.GetConfigVarFromSections<string>(config, "GridName", namessections, string.Empty);
if (string.IsNullOrEmpty(m_GridName))
m_GridName = Util.GetConfigVarFromSections<string>(config, "gridname", namessections, string.Empty);
@@ -504,7 +504,8 @@ namespace OpenSim.Framework
m_DestinationGuideURL = tmpuri.URI;
}
m_economyURL = Util.GetConfigVarFromSections<string>(config, "economy", new string[] { "Economy", "GridInfo" });
m_economyURL = Util.GetConfigVarFromSections<string>(config, "economy", ["Economy", "GridInfo"]);
if (!string.IsNullOrEmpty(m_economyURL))
{
tmpuri = new OSHTTPURI(m_economyURL.Trim(), true);
@@ -515,6 +516,20 @@ namespace OpenSim.Framework
}
m_economyURL = tmpuri.URI;
}
string stunservers = Util.GetConfigVarFromSections<string>(config, "StunServers", ["Const", "Startup", "GridInfo", "SimulatorFeatures"], string.Empty);
if (!string.IsNullOrWhiteSpace(stunservers))
{
string[] stuns = stunservers.Split(',');
List<string> stunsarr = new List<string>(stuns.Length);
for (int i = 0; i < stuns.Length; ++i)
{
OSHHTPHost tmp = new OSHHTPHost(stuns[i].Trim(), false);
if (tmp.IsValidHost)
stunsarr.Add("stun:" + tmp.HostAndPort);
}
m_StunServers = stunsarr.Count > 0 ? stunsarr.ToArray() : null;
}
}
public bool HasHGConfig
@@ -685,6 +700,7 @@ namespace OpenSim.Framework
}
}
public string GridName
{
get { return m_GridName; }
@@ -756,5 +772,27 @@ namespace OpenSim.Framework
m_log.Error((tmp.IsValidHost ? "Could not resolve EconomyURL" : "EconomyURL is a invalid host ") + value ?? "");
}
}
public string[] StunServers
{
get { return m_StunServers; }
set
{
if(value.Length > 0)
{
List<string> values = new List<string>(value.Length);
for (int i = 0; i < value.Length; ++i)
{
OSHHTPHost tmp = new OSHHTPHost(value[i].Trim(), false);
if (tmp.IsValidHost)
values.Add("stun:" + tmp.HostAndPort);
}
m_StunServers = values.Count > 0 ? values.ToArray() : null;
}
else
m_StunServers = null;
}
}
}
}

View File

@@ -1396,7 +1396,8 @@ namespace OpenSim.Framework
void SendRegionHandle(UUID regoinID, ulong handle);
void SendParcelInfo(RegionInfo info, LandData land, UUID parcelID, uint x, uint y);
void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, Vector3 lookAt);
//void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, Vector3 lookAt, int options);
void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, int options); //lookat does nothing
void SendDirPlacesReply(UUID queryID, DirPlacesReplyData[] data);
void SendDirPeopleReply(UUID queryID, DirPeopleReplyData[] data);
@@ -1427,7 +1428,7 @@ namespace OpenSim.Framework
void GroupMembershipAddReplace(UUID GroupID,ulong GroupPowers);
void SendAvatarNotesReply(UUID targetID, string text);
void SendAvatarPicksReply(UUID targetID, Dictionary<UUID, string> picks);
void SendPickInfoReply(UUID pickID,UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled);
void SendPickInfoReply(UUID pickID, UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3d posGlobal, int sortOrder, bool enabled);
void SendAvatarClassifiedReply(UUID targetID, Dictionary<UUID, string> classifieds);

View File

@@ -794,8 +794,8 @@ namespace OpenSim.Framework
public static byte[] ulongToByteArray(ulong uLongValue)
{
return new byte[8]
{
return
[
(byte)(uLongValue >> 56),
(byte)(uLongValue >> 48),
(byte)(uLongValue >> 40),
@@ -804,18 +804,18 @@ namespace OpenSim.Framework
(byte)(uLongValue >> 16),
(byte)(uLongValue >> 8),
(byte)uLongValue
};
];
}
public static byte[] uintToByteArray(uint value)
{
return new byte[4]
{
return
[
(byte)(value >> 24),
(byte)(value >> 16),
(byte)(value >> 8),
(byte)value
};
];
}
static readonly char[] base64Chars = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O',

View File

@@ -119,7 +119,7 @@ namespace OpenSim.Framework
// Copy the temporary stream to the network stream
formDataStream.Seek(0, SeekOrigin.Begin);
using (Stream requestStream = request.GetRequestStream())
formDataStream.CopyStream(requestStream, (int)formDataStream.Length);
formDataStream.CopyTo(requestStream, (int)formDataStream.Length);
}
#endregion Stream Writing

View File

@@ -320,9 +320,7 @@ namespace OpenSim.Framework
get
{
PrimitiveBaseShape boxShape = CreateBox();
boxShape.SetScale(0.5f);
return boxShape;
}
}
@@ -1508,7 +1506,7 @@ namespace OpenSim.Framework
return null;
if (RenderMaterials.overrides is null || RenderMaterials.overrides.Length == 0)
return new byte[] { 0 }; // store so outdated viewer caches can be updated
return [0]; // store so outdated viewer caches can be updated
int nentries = 0;
for (int i = 0; i < RenderMaterials.overrides.Length; i++)
@@ -1517,7 +1515,7 @@ namespace OpenSim.Framework
nentries++;
}
if(nentries == 0)
return new byte[] { 0 };
return [0];
osUTF8 sb = OSUTF8Cached.Acquire();
sb.Append((byte)nentries);
@@ -1541,6 +1539,7 @@ namespace OpenSim.Framework
if (data is null || data.Length < 1)
return;
int nentries = data[0];
if (nentries > 128)
return;

View File

@@ -683,39 +683,45 @@ namespace OpenSim.Framework
private void DoDefaultLandingSanityChecks()
{
// Sanity Check Default Landing
float buffer_zone = 5f;
bool ValuesCapped = false;
// Minimum Positions
if (DefaultLandingPoint.X < buffer_zone)
if (DefaultLandingPoint.X < Constants.DefaultLandingBorderBuffer)
{
DefaultLandingPoint.X = buffer_zone;
DefaultLandingPoint.X = Constants.DefaultLandingBorderBuffer;
ValuesCapped = true;
}
if (DefaultLandingPoint.Y < buffer_zone)
if (DefaultLandingPoint.Y < Constants.DefaultLandingBorderBuffer)
{
DefaultLandingPoint.Y = buffer_zone;
DefaultLandingPoint.Y = Constants.DefaultLandingBorderBuffer;
ValuesCapped = true;
}
// Maximum Positions
if (DefaultLandingPoint.X > RegionSizeX - buffer_zone)
if (DefaultLandingPoint.X > RegionSizeX - Constants.DefaultLandingBorderBuffer)
{
DefaultLandingPoint.X = RegionSizeX - buffer_zone;
DefaultLandingPoint.X = RegionSizeX - Constants.DefaultLandingBorderBuffer;
ValuesCapped = true;
}
if (DefaultLandingPoint.Y > RegionSizeY - buffer_zone)
if (DefaultLandingPoint.Y > RegionSizeY - Constants.DefaultLandingBorderBuffer)
{
DefaultLandingPoint.Y = RegionSizeY - buffer_zone;
DefaultLandingPoint.Y = RegionSizeY - Constants.DefaultLandingBorderBuffer;
ValuesCapped = true;
}
// Height
if (DefaultLandingPoint.Z < 0f)
DefaultLandingPoint.Z = 0f;
if (DefaultLandingPoint.Z < Constants.MinSimulationHeight)
{
DefaultLandingPoint.Z = Constants.MinSimulationHeight;
ValuesCapped = true;
}
else if (DefaultLandingPoint.Z > Constants.MaxSimulationHeight)
{
DefaultLandingPoint.Z = Constants.MaxSimulationHeight;
ValuesCapped = true;
}
if (ValuesCapped)
m_log.WarnFormat("[RegionInfo]: The default landing location for {0} has been capped to {1}", RegionName, DefaultLandingPoint);

View File

@@ -44,20 +44,19 @@ namespace OpenSim.Framework
// The point is an absolute position, so we need the relative
// location to the spawn point
Vector3 offset = point - pos;
Distance = Vector3.Mag(offset);
Distance = offset.Length();
// Next we need to rotate this vector into the spawn point's
// coordinate system
rot.W = -rot.W;
offset *= rot;
Vector3 dir = Vector3.Normalize(offset);
offset.Normalize();
// Get the bearing (yaw)
Yaw = MathF.Atan2(dir.Y, dir.X);
Yaw = MathF.Atan2(offset.Y, offset.X);
// Get the elevation (pitch)
Pitch = -MathF.Atan2(dir.Z, MathF.Sqrt(dir.X * dir.X + dir.Y * dir.Y));
Pitch = -MathF.Atan2(offset.Z, MathF.Sqrt(offset.X * offset.X + offset.Y * offset.Y));
}
public Vector3 GetLocation(Vector3 pos, Quaternion rot)
@@ -65,8 +64,8 @@ namespace OpenSim.Framework
Quaternion y = Quaternion.CreateFromEulers(0, 0, Yaw);
Quaternion p = Quaternion.CreateFromEulers(0, Pitch, 0);
Vector3 dir = new Vector3(1, 0, 0) * p * y;
Vector3 offset = dir * (float)Distance;
Vector3 dir = Vector3.UnitX * p * y;
Vector3 offset = dir * Distance;
offset *= rot;

View File

@@ -286,7 +286,8 @@ namespace OpenSim.Framework
responseMessage = client.Send(request, HttpCompletionOption.ResponseHeadersRead);
responseMessage.EnsureSuccessStatusCode();
Stream respStream = responseMessage.Content.ReadAsStream();
using CancellationTokenSource cts = new(30000);
using Stream respStream = responseMessage.Content.ReadAsStream(cts.Token);
int length = respStream.Read(_readbuf, 0, BufferSize);
while (length > 0)
{
@@ -402,6 +403,5 @@ namespace OpenSim.Framework
client?.Dispose();
}
}
}
}

View File

@@ -66,6 +66,14 @@ namespace OpenSim.Framework.Serialization.External
{ "MediaID", (ld, xtr) => ld.MediaID = UUID.Parse(xtr.ReadElementString("MediaID")) },
{ "MediaURL", (ld, xtr) => ld.MediaURL = xtr.ReadElementString("MediaURL") },
{ "MusicURL", (ld, xtr) => ld.MusicURL = xtr.ReadElementString("MusicURL") },
{ "MediaType", (ld, xtr) => ld.MediaType = xtr.ReadElementString("MediaType") },
{ "MediaDesc", (ld, xtr) => ld.MediaDescription = xtr.ReadElementString("MediaDesc") },
{ "MediaH", (ld, xtr) => ld.MediaHeight = Convert.ToInt32(xtr.ReadElementString("MediaH")) },
{ "MediaW", (ld, xtr) => ld.MediaWidth = Convert.ToInt32(xtr.ReadElementString("MediaW")) },
{ "MediaLoop", (ld, xtr) => ld.MediaLoop = Convert.ToBoolean(xtr.ReadElementString("MediaLoop")) },
{ "ObscureMOAP", (ld, xtr) => ld.ObscureMedia = Convert.ToBoolean(xtr.ReadElementString("ObscureMOAP")) },
{ "OwnerID", (ld, xtr) => ld.OwnerID = UUID.Parse(xtr.ReadElementString("OwnerID")) },
{ "ParcelAccessList", ProcessParcelAccessList },
@@ -114,21 +122,20 @@ namespace OpenSim.Framework.Serialization.External
{
if (!xtr.IsEmptyElement)
{
while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement)
_ = xtr.Read();
while (xtr.IsStartElement("ParcelAccessEntry"))
{
LandAccessEntry lae = new LandAccessEntry();
xtr.ReadStartElement("ParcelAccessEntry");
ExternalRepresentationUtils.ExecuteReadProcessors<LandAccessEntry>(lae, m_laeProcessors, xtr);
xtr.ReadEndElement();
ld.ParcelAccessList.Add(lae);
if (!xtr.IsEmptyElement)
{
_ = xtr.Read();
LandAccessEntry lae = new();
ExternalRepresentationUtils.ExecuteReadProcessors<LandAccessEntry>(lae, m_laeProcessors, xtr);
ld.ParcelAccessList.Add(lae);
}
_ = xtr.Read();
}
}
xtr.Read();
_ = xtr.Read();
}
/// <summary>
@@ -209,6 +216,13 @@ namespace OpenSim.Framework.Serialization.External
xtw.WriteElementString("MediaURL", landData.MediaURL);
xtw.WriteElementString("MusicURL", landData.MusicURL);
xtw.WriteElementString("MediaType", landData.MediaType);
xtw.WriteElementString("MediaDesc", landData.MediaDescription);
xtw.WriteElementString("MediaH", Convert.ToString(landData.MediaHeight));
xtw.WriteElementString("MediaW", Convert.ToString(landData.MediaWidth));
xtw.WriteElementString("MediaLoop", Convert.ToString(landData.MediaLoop));
xtw.WriteElementString("ObscureMOAP", Convert.ToString(landData.ObscureMedia));
UUID ownerID = options.ContainsKey("wipe-owners") ? UUID.Zero : landData.OwnerID;
xtw.WriteElementString("OwnerID", ownerID.ToString());

View File

@@ -179,7 +179,7 @@ namespace OpenSim.Framework.Servers
{
m_log.Info("[STARTUP]: Beginning startup processing");
m_log.Info("[STARTUP]: version: " + m_version);
m_log.Info("[STARTUP]: Version: " + m_version);
m_log.Info($"[STARTUP]: Operating system version: {Environment.OSVersion}, .NET platform {Util.RuntimePlatformStr}, Runtime {Environment.Version}");
m_log.Info($"[STARTUP]: Processor Architecture: {RuntimeInformation.ProcessArchitecture}({(BitConverter.IsLittleEndian ? "le" : "be")} {(Environment.Is64BitProcess ? "64" : "32")}bit)");
try

View File

@@ -372,7 +372,7 @@ namespace OpenSim.Framework.Servers.HttpServer
public void RemoveWebSocketHandler(string servicepath)
{
m_WebSocketHandlers.TryRemove(servicepath, out WebSocketRequestDelegate dummy);
m_WebSocketHandlers.TryRemove(servicepath, out _);
}
public List<string> GetStreamHandlerKeys()
@@ -526,7 +526,7 @@ namespace OpenSim.Framework.Servers.HttpServer
public void RemoveIndexPHPMethodHandler(string key)
{
m_indexPHPmethods.TryRemove(key, out SimpleStreamMethod sh);
m_indexPHPmethods.TryRemove(key, out _);
}
public SimpleStreamMethod TryGetIndexPHPMethodHandler(string key)
@@ -543,7 +543,7 @@ namespace OpenSim.Framework.Servers.HttpServer
public void RemoveGlobalPMethodHandler(string key)
{
m_globalMethods.TryRemove(key, out SimpleStreamMethod sh);
m_globalMethods.TryRemove(key, out _);
}
public bool TryGetGlobalMethodHandler(string key, out SimpleStreamMethod sh)

View File

@@ -219,6 +219,7 @@ namespace OSHttpServer
/// </remarks>
public virtual void Cleanup()
{
if (StreamPassedOff)
return;

View File

@@ -61,12 +61,11 @@ namespace OSHttpServer
private void OnFreeContext(object sender, DisconnectedEventArgs e)
{
var imp = sender as HttpClientContext;
if (imp == null || imp.contextID < 0)
return;
m_activeContexts.TryRemove(imp.contextID, out HttpClientContext dummy);
imp.Close();
if (sender is HttpClientContext imp && imp.contextID >= 0)
{
m_activeContexts.TryRemove(imp.contextID, out _);
imp.Close();
}
}

View File

@@ -125,23 +125,53 @@ namespace OSHttpServer
private async void AcceptLoop()
{
try
while (true)
{
while (true)
if (m_shutdown)
{
if (m_shutdown)
{
m_shutdownEvent.Set();
break;
}
m_shutdownEvent?.Set();
break;
}
Socket socket = await m_listener.AcceptSocketAsync(m_CancellationSource.Token).ConfigureAwait(false); ;
Socket socket = null;
try
{
socket = await m_listener.AcceptSocketAsync(m_CancellationSource.Token).ConfigureAwait(false);
if (!socket.Connected)
{
socket.Dispose();
continue;
}
}
catch (OperationCanceledException)
{
m_shutdownEvent?.Set();
break;
}
catch (SocketException snssErr)
{
m_logWriter.Write(this, LogPrio.Debug, "OSHTTP Accept wait ignoring error: " + snssErr.Message);
socket?.Dispose();
continue;
}
catch (Exception err)
{
m_logWriter.Write(this, LogPrio.Debug, "OSHTTP Accept wait fatal error: " + err.Message);
ExceptionThrown?.Invoke(this, err);
}
if (m_shutdown)
{
m_shutdownEvent?.Set();
socket?.Dispose();
break;
}
if(socket == null)
continue;
try
{
socket.NoDelay = true;
if (!OnAcceptingSocket(socket))
@@ -149,6 +179,7 @@ namespace OSHttpServer
socket.Disconnect(true);
continue;
}
if (socket.Connected)
{
m_logWriter.Write(this, LogPrio.Debug, $"Accepted connection from: {socket.RemoteEndPoint}");
@@ -159,17 +190,24 @@ namespace OSHttpServer
m_contextFactory.CreateContext(socket);
}
else
socket.Dispose();
socket?.Dispose();
}
catch (OperationCanceledException)
{
m_shutdownEvent?.Set();
break;
}
catch (SocketException snssErr)
{
m_logWriter.Write(this, LogPrio.Debug, "OSHTTP Accept processing ignoring error: " + snssErr.Message);
socket?.Dispose();
continue;
}
catch (Exception err)
{
m_logWriter.Write(this, LogPrio.Debug, "OSHTTP Accept processing fatal error: " + err.Message);
ExceptionThrown?.Invoke(this, err);
}
}
catch (OperationCanceledException)
{
m_shutdownEvent.Set();
}
catch (Exception err)
{
m_logWriter.Write(this, LogPrio.Debug, err.Message);
ExceptionThrown?.Invoke(this, err);
}
}

View File

@@ -63,7 +63,7 @@ namespace OpenSim.Framework.Servers
protected DateTime m_startuptime;
protected string m_startupDirectory = Environment.CurrentDirectory;
protected string m_pidFile = String.Empty;
protected string m_pidFile = string.Empty;
protected ServerStatsCollector m_serverStatsCollector;
@@ -86,11 +86,11 @@ namespace OpenSim.Framework.Servers
try
{
string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
string pidstring = Environment.ProcessId.ToString();
using (FileStream fs = File.Create(path))
{
Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
byte[] buf = Encoding.ASCII.GetBytes(pidstring);
fs.Write(buf, 0, buf.Length);
}
@@ -116,8 +116,7 @@ namespace OpenSim.Framework.Servers
{
m_log.Error($"[SERVER BASE]: Error whilst removing {m_pidFile}", e);
}
m_pidFile = String.Empty;
m_pidFile = string.Empty;
}
}

View File

@@ -172,7 +172,7 @@ namespace OpenSim.Framework
public TaskInventoryItem()
{
ScriptRunning = true;
CreationDate = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
CreationDate = (uint)Util.UnixTimeSinceEpoch();
}
}
}

View File

@@ -26,12 +26,10 @@
*/
using System;
using System.Collections;
using System.IO;
using System.IO.Compression;
using System.Reflection;
using OpenMetaverse;
using System.Runtime.CompilerServices;
using log4net;
@@ -80,7 +78,6 @@ namespace OpenSim.Framework
private readonly int m_mapStride;
private readonly int m_mapPatchsStride;
// legacy CompressionFactor
public float CompressionFactor { get; private set; }
@@ -103,6 +100,7 @@ namespace OpenSim.Framework
public float this[int x, int y]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return m_heightmap[x, y]; }
set
{
@@ -117,7 +115,9 @@ namespace OpenSim.Framework
public float this[int x, int y, int z]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return this[x, y]; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set { this[x, y] = value; }
}
@@ -244,7 +244,7 @@ namespace OpenSim.Framework
m_heightmap[xx, yy] = pHeight;
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTainted()
{
return m_taints.IsTaited();
@@ -253,7 +253,7 @@ namespace OpenSim.Framework
// Return 'true' of the patch that contains these region coordinates has been modified.
// Note that checking the taint clears it.
// There is existing code that relies on this feature.
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAt(int xx, int yy, bool clearOnTest)
{
yy /= Constants.TerrainPatchSize;
@@ -261,45 +261,45 @@ namespace OpenSim.Framework
return m_taints.Get(indx, clearOnTest);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAt(int xx, int yy)
{
yy /= Constants.TerrainPatchSize;
return m_taints.Get(xx / Constants.TerrainPatchSize + yy * m_taintSizeX);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAtPatch(int xx, int yy, bool clearOnTest)
{
int indx = xx + yy * m_taintSizeX;
return m_taints.Get(indx, clearOnTest);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAtPatch(int xx, int yy)
{
return m_taints.Get(xx + yy * m_taintSizeX);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAtPatch(int indx, bool clearOnTest)
{
return m_taints.Get(indx, clearOnTest);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAtPatchWithClear(int indx)
{
return m_taints.GetAndClear(indx);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool IsTaintedAtPatch(int indx)
{
return m_taints.Get(indx);
}
[System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetAndClearNextTaint(int startIndex)
{
return m_taints.GetAndClearNextTrue(startIndex);
@@ -361,31 +361,35 @@ namespace OpenSim.Framework
public unsafe void GetPatchMinMax(int px, int py, out float zmin, out float zmax)
{
zmax = float.MinValue;
zmin = float.MaxValue;
int mpy = Constants.TerrainPatchSize * py;
float min, max;
fixed (float* map = m_heightmap)
{
float* p = map + px * m_mapPatchsStride;
float* pend = p + m_mapPatchsStride;
while (p < pend)
float* p = map + px * m_mapPatchsStride + Constants.TerrainPatchSize * py;
float* endp = p + m_mapPatchsStride;
min = max = *p;
float* y = p;
int j = Constants.TerrainPatchSize - 1;
do
{
float* yt = p + mpy;
float* ytend = yt + 16;
while(yt < ytend)
do
{
float val = *yt;
if (val > zmax)
zmax = val;
else if (val < zmin)
zmin = val;
yt++;
float val = *y++;
if (val > max)
max = val;
else if (val < min)
min = val;
}
while(--j > 0);
p += m_mapStride;
y = p;
j = Constants.TerrainPatchSize;
}
while (p < endp);
}
zmin = min;
zmax = max;
}
public unsafe void GetPatchBlock(float* block, int px, int py, float sub, float premult)
@@ -467,7 +471,6 @@ namespace OpenSim.Framework
for (int jj = 0; jj < SizeY; jj++)
{
m_heightmap[ii, jj] = (float)pTerrain[ii, jj];
}
}
// m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ);
@@ -628,37 +631,34 @@ namespace OpenSim.Framework
Array ret = null;
try
{
using (MemoryStream inp = new MemoryStream((2 * sizeof(Int32)) + (SizeX * SizeY * sizeof(float))))
using (MemoryStream inp = new MemoryStream((2 * sizeof(int)) + (SizeX * SizeY * sizeof(float))))
{
using (BinaryWriter bw = new BinaryWriter(inp))
{
bw.Write((Int32)SizeX);
bw.Write((Int32)SizeY);
bw.Write(SizeX);
bw.Write(SizeY);
for (int yy = 0; yy < SizeY; yy++)
for (int xx = 0; xx < SizeX; xx++)
{
//bw.Write((float)m_heightmap[xx, yy]);
bw.Write(MathF.Round(m_heightmap[xx, yy], 3, MidpointRounding.AwayFromZero));
}
bw.Flush();
inp.Seek(0, SeekOrigin.Begin);
using (MemoryStream outputStream = new MemoryStream())
{
using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress))
{
inp.CopyStream(compressionStream, int.MaxValue);
compressionStream.Close();
ret = outputStream.ToArray();
}
}
using MemoryStream outputStream = new MemoryStream();
using GZipStream compressionStream = new(outputStream, CompressionMode.Compress);
inp.CopyTo(compressionStream);
compressionStream.Flush();
ret = outputStream.ToArray();
}
}
m_log.Debug($"{LogHeader} V2DGzip {ret.Length} bytes");
}
catch (Exception ex)
{
m_log.Error($"{LogHeader} V2DGzip error: {ex.Message}");
}
catch {}
m_log.DebugFormat("{0} V2DGzip {1} bytes", LogHeader, ret.Length);
return ret;
}
@@ -785,7 +785,6 @@ namespace OpenSim.Framework
{
using (GZipStream decompressionStream = new GZipStream(inputStream, CompressionMode.Decompress))
{
decompressionStream.Flush();
decompressionStream.CopyTo(outputStream);
}
}

View File

@@ -1337,17 +1337,32 @@ namespace OpenSim.Framework
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SHA1Hash(byte[] data)
{
byte[] hash = ComputeSHA1Hash(data);
return bytesToHexString(hash, false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static byte[] ComputeSHA1Hash(byte[] src)
{
return SHA1.HashData(src);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SHA256Hash(byte[] data)
{
return SHA256Hash(data.AsSpan());
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static string SHA256Hash(ReadOnlySpan<byte> data)
{
byte[] hash = SHA256.HashData(data);
return bytesToHexString(hash, false);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static UUID ComputeSHA1UUID(string src)
{
@@ -2531,7 +2546,9 @@ namespace OpenSim.Framework
(byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
(byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
(byte)x, (byte)(x >> 8), 0, 0,
(byte)y, (byte)(y >> 8), 0, 0 };
(byte)y, (byte)(y >> 8), 0, 0
};
return new UUID(bytes, 0);
}
@@ -2542,7 +2559,8 @@ namespace OpenSim.Framework
(byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24),
(byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56),
(byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8),
(byte)y, (byte)(y >> 8), 0, 0 };
(byte)y, (byte)(y >> 8), 0, 0
};
return new UUID(bytes, 0);
}
@@ -3480,7 +3498,7 @@ namespace OpenSim.Framework
finally
{
Interlocked.Decrement(ref numRunningThreadFuncs);
activeThreads.TryRemove(threadFuncNum, out ThreadInfo dummy);
activeThreads.TryRemove(threadFuncNum, out _);
if (loggingEnabled && threadInfo.LogThread)
m_log.Debug($"Exit threadfunc {threadFuncNum} ({FormatDuration(threadInfo.Elapsed())}");
callback = null;
@@ -3520,7 +3538,7 @@ namespace OpenSim.Framework
catch (Exception)
{
Interlocked.Decrement(ref numQueuedThreadFuncs);
activeThreads.TryRemove(threadFuncNum, out ThreadInfo dummy);
activeThreads.TryRemove(threadFuncNum, out _);
throw;
}
}

View File

@@ -316,7 +316,6 @@ namespace OpenSim.Framework
return;
OSD otmp;
if (map.TryGetValue("day_cycle", out otmp) && otmp is OSDMap)
{
Cycle = new DayCycle();

View File

@@ -26,6 +26,7 @@
*/
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Specialized;
using System.Globalization;
@@ -36,6 +37,7 @@ using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web;
using System.Xml;
using System.Xml.Serialization;
@@ -47,7 +49,6 @@ using OpenSim.Framework.ServiceAuth;
using System.Net.Http;
using System.Security.Authentication;
using System.Runtime.CompilerServices;
using System.Threading;
namespace OpenSim.Framework
{
@@ -339,7 +340,7 @@ namespace OpenSim.Framework
output = output[..MaxRequestDiagLength] + "...";
}
m_log.DebugFormat($"[LOGHTTP]: {context}{Util.BinaryToASCII(output)}");
m_log.Debug($"[LOGHTTP]: {context} {Util.BinaryToASCII(output)}");
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -424,7 +425,8 @@ namespace OpenSim.Framework
responseMessage = client.Send(request, HttpCompletionOption.ResponseHeadersRead);
responseMessage.EnsureSuccessStatusCode();
Stream resStream = responseMessage.Content.ReadAsStream();
using CancellationTokenSource cts = new(30000);
Stream resStream = responseMessage.Content.ReadAsStream(cts.Token);
if (resStream is not null)
{
using StreamReader reader = new(resStream);
@@ -532,7 +534,6 @@ namespace OpenSim.Framework
{
int reqnum = RequestNumber++;
string method = (data is not null && data["RequestMethod"] is not null) ? data["RequestMethod"] : "unknown";
if (DebugLevel >= 3)
m_log.Debug($"[LOGHTTP]: HTTP OUT {reqnum} ServiceForm '{method}' to {url}");
@@ -590,7 +591,8 @@ namespace OpenSim.Framework
responseMessage = client.Send(request, HttpCompletionOption.ResponseHeadersRead);
responseMessage.EnsureSuccessStatusCode();
using StreamReader reader = new(responseMessage.Content.ReadAsStream());
using CancellationTokenSource cts = new(30000);
using StreamReader reader = new(responseMessage.Content.ReadAsStream(cts.Token));
string responseStr = reader.ReadToEnd();
rcvlen = responseStr.Length;
if (DebugLevel >= 5)
@@ -804,18 +806,18 @@ namespace OpenSim.Framework
/// </remarks>
public static int CopyStream(this Stream copyFrom, Stream copyTo, int maximumBytesToCopy)
{
byte[] buffer = new byte[4096];
byte[] buffer = ArrayPool<byte>.Shared.Rent(8196);
int readBytes;
int totalCopiedBytes = 0;
while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(4096, maximumBytesToCopy))) > 0)
while ((readBytes = copyFrom.Read(buffer, 0, Math.Min(8196, maximumBytesToCopy))) > 0)
{
int writeBytes = Math.Min(maximumBytesToCopy, readBytes);
copyTo.Write(buffer, 0, writeBytes);
totalCopiedBytes += writeBytes;
maximumBytesToCopy -= writeBytes;
}
ArrayPool<byte>.Shared.Return(buffer);
return totalCopiedBytes;
}
@@ -1149,7 +1151,7 @@ namespace OpenSim.Framework
HttpResponseMessage responseMessage = null;
HttpRequestMessage request = null;
HttpClient client = null;
string respstring = String.Empty;
string respstring = string.Empty;
int sendlen = 0;
int rcvlen = 0;
try
@@ -1161,7 +1163,7 @@ namespace OpenSim.Framework
auth?.AddAuthorization(request.Headers);
request.Headers.ExpectContinue = false;
request.Headers.TransferEncodingChunked = false; if (timeoutsecs > 0)
request.Headers.TransferEncodingChunked = false;
if (keepalive)
{
@@ -1192,7 +1194,8 @@ namespace OpenSim.Framework
if ((responseMessage.Content.Headers.ContentLength is long contentLength) && contentLength != 0)
{
using StreamReader reader = new(responseMessage.Content.ReadAsStream());
using CancellationTokenSource cts = new(30000);
using StreamReader reader = new(responseMessage.Content.ReadAsStream(cts.Token));
respstring = reader.ReadToEnd();
rcvlen = respstring.Length;
}
@@ -1277,7 +1280,8 @@ namespace OpenSim.Framework
if ((responseMessage.Content.Headers.ContentLength is long contentLength) && contentLength != 0)
{
using StreamReader reader = new(responseMessage.Content.ReadAsStream());
using CancellationTokenSource cts = new(30000);
using StreamReader reader = new(responseMessage.Content.ReadAsStream(cts.Token));
respstring = reader.ReadToEnd();
}
}
@@ -1427,7 +1431,8 @@ namespace OpenSim.Framework
if ((responseMessage.Content.Headers.ContentLength is long contentLength) && contentLength != 0)
{
rcvlen = (int)contentLength;
using Stream respStream = responseMessage.Content.ReadAsStream();
using CancellationTokenSource cts = new(30000);
using Stream respStream = responseMessage.Content.ReadAsStream(cts.Token);
deserial = XMLResponseHelper.LogAndDeserialize<TResponse>(
reqnum, respStream, contentLength);
}
@@ -1521,7 +1526,8 @@ namespace OpenSim.Framework
if ((responseMessage.Content.Headers.ContentLength is long contentLength) && contentLength != 0)
{
rcvlen = (int)contentLength;
using Stream respStream = responseMessage.Content.ReadAsStream();
using CancellationTokenSource cts = new(30000);
using Stream respStream = responseMessage.Content.ReadAsStream(cts.Token);
deserial = XMLResponseHelper.LogAndDeserialize<TResponse>(
reqnum, respStream, contentLength);
}

View File

@@ -73,37 +73,26 @@ namespace OpenSim
// First line, hook the appdomain to the crash reporter
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
System.AppContext.SetSwitch("System.Drawing.EnableUnixSupport", true);
Culture.SetCurrentCulture();
Culture.SetDefaultCurrentCulture();
ServicePointManager.DefaultConnectionLimit = 32;
ServicePointManager.MaxServicePointIdleTime = 30000;
AppContext.SetSwitch("System.Drawing.EnableUnixSupport", true);
try { ServicePointManager.DnsRefreshTimeout = 5000; } catch { }
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
// Add the arguments supplied when running the application to the configuration
ArgvConfigSource configSource = new ArgvConfigSource(args);
// Configure Log4Net
configSource.AddSwitch("Startup", "logconfig");
string logConfigFile = configSource.Configs["Startup"].GetString("logconfig", String.Empty);
if (!string.IsNullOrEmpty(logConfigFile))
/*
// pre load System.Drawing.Common.dll for the platform
// this will fail if a newer version is present on GAC, bin folder, etc, since LoadFrom only accepts the path, if it cannot find it elsewhere
string targetdll = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),"lib",
(Util.IsWindows() ? "win" : "linux"), "System.Drawing.Common.dll");
try
{
XmlConfigurator.Configure(new System.IO.FileInfo(logConfigFile));
m_log.InfoFormat("[OPENSIM MAIN]: configured log4net using \"{0}\" as configuration file",
logConfigFile);
Assembly asmb = Assembly.LoadFrom(targetdll);
}
else
catch (Exception e)
{
XmlConfigurator.Configure(new System.IO.FileInfo("OpenSim.exe.config"));
m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config");
m_log.Error("Failed to load System.Drawing.Common.dll for current platform" + e.Message);
throw;
}
// temporay set the platform dependent System.Drawing.Common.dll
*/
string targetdll = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"System.Drawing.Common.dll");
string src = targetdll + (Util.IsWindows() ? ".win" : ".linux");
@@ -125,44 +114,53 @@ namespace OpenSim
throw;
}
m_log.InfoFormat(
"[OPENSIM MAIN]: System Locale is {0}", System.Threading.Thread.CurrentThread.CurrentCulture);
if(!Util.IsWindows())
ServicePointManager.DefaultConnectionLimit = 32;
ServicePointManager.MaxServicePointIdleTime = 30000;
try { ServicePointManager.DnsRefreshTimeout = 5000; } catch { }
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
// Add the arguments supplied when running the application to the configuration
ArgvConfigSource configSource = new ArgvConfigSource(args);
// Configure Log4Net
configSource.AddSwitch("Startup", "logconfig");
string logConfigFile = configSource.Configs["Startup"].GetString("logconfig", string.Empty);
if (!string.IsNullOrEmpty(logConfigFile))
{
string monoThreadsPerCpu = System.Environment.GetEnvironmentVariable("MONO_THREADS_PER_CPU");
m_log.InfoFormat(
"[OPENSIM MAIN]: Environment variable MONO_THREADS_PER_CPU is {0}", monoThreadsPerCpu ?? "unset");
XmlConfigurator.Configure(new System.IO.FileInfo(logConfigFile));
m_log.Info($"[OPENSIM MAIN]: configured log4net using \"{logConfigFile}\" as configuration file");
}
else
{
XmlConfigurator.Configure(new System.IO.FileInfo("OpenSim.exe.config"));
m_log.Info("[OPENSIM MAIN]: configured log4net using default OpenSim.exe.config");
}
// Verify the Threadpool allocates or uses enough worker and IO completion threads
// .NET 2.0, workerthreads default to 50 * numcores
// .NET 3.0, workerthreads defaults to 250 * numcores
// .NET 4.0, workerthreads are dynamic based on bitness and OS resources
// Max IO Completion threads are 1000 on all 3 CLRs
//
// Mono 2.10.9 to at least Mono 3.1, workerthreads default to 100 * numcores, iocp threads to 4 * numcores
m_log.Info($"[OPENSIM MAIN]: System Locale is {System.Threading.Thread.CurrentThread.CurrentCulture}");
int workerThreadsMin = 500;
int workerThreadsMax = 1000; // may need further adjustment to match other CLR
int workerThreadsMax = 1000;
int iocpThreadsMin = 1000;
int iocpThreadsMax = 2000; // may need further adjustment to match other CLR
int iocpThreadsMax = 2000;
System.Threading.ThreadPool.GetMinThreads(out int currentMinWorkerThreads, out int currentMinIocpThreads);
m_log.InfoFormat(
"[OPENSIM MAIN]: Runtime gave us {0} min worker threads and {1} min IOCP threads",
currentMinWorkerThreads, currentMinIocpThreads);
m_log.Info(
$"[OPENSIM MAIN]: Runtime gave us {currentMinWorkerThreads} min worker threads and {currentMinIocpThreads} min IOCP threads");
System.Threading.ThreadPool.GetMaxThreads(out int workerThreads, out int iocpThreads);
m_log.InfoFormat("[OPENSIM MAIN]: Runtime gave us {0} max worker threads and {1} max IOCP threads", workerThreads, iocpThreads);
m_log.Info($"[OPENSIM MAIN]: Runtime gave us {workerThreads} max worker threads and {iocpThreads} max IOCP threads");
if (workerThreads < workerThreadsMin)
{
workerThreads = workerThreadsMin;
m_log.InfoFormat("[OPENSIM MAIN]: Bumping up max worker threads to {0}",workerThreads);
m_log.Info($"[OPENSIM MAIN]: Bumping up max worker threads to {workerThreads}");
}
if (workerThreads > workerThreadsMax)
{
workerThreads = workerThreadsMax;
m_log.InfoFormat("[OPENSIM MAIN]: Limiting max worker threads to {0}",workerThreads);
m_log.Info($"[OPENSIM MAIN]: Limiting max worker threads to {workerThreads}");
}
// Increase the number of IOCP threads available.
@@ -170,20 +168,19 @@ namespace OpenSim
if (iocpThreads < iocpThreadsMin)
{
iocpThreads = iocpThreadsMin;
m_log.InfoFormat("[OPENSIM MAIN]: Bumping up max IOCP threads to {0}",iocpThreads);
m_log.Info($"[OPENSIM MAIN]: Bumping up max IOCP threads to {iocpThreads}");
}
// Make sure we don't overallocate IOCP threads and thrash system resources
if ( iocpThreads > iocpThreadsMax )
{
iocpThreads = iocpThreadsMax;
m_log.InfoFormat("[OPENSIM MAIN]: Limiting max IOCP completion threads to {0}",iocpThreads);
m_log.Info($"[OPENSIM MAIN]: Limiting max IOCP completion threads to {iocpThreads}");
}
// set the resulting worker and IO completion thread counts back to ThreadPool
if ( System.Threading.ThreadPool.SetMaxThreads(workerThreads, iocpThreads) )
{
m_log.InfoFormat(
"[OPENSIM MAIN]: Threadpool set to {0} max worker threads and {1} max IOCP threads",
workerThreads, iocpThreads);
m_log.Info(
$"[OPENSIM MAIN]: Threadpool set to {workerThreads} max worker threads and {iocpThreads} max IOCP threads");
}
else
{
@@ -192,17 +189,17 @@ namespace OpenSim
// Check if the system is compatible with OpenSimulator.
// Ensures that the minimum system requirements are met
string supported = String.Empty;
if (Util.IsEnvironmentSupported(ref supported))
string error = string.Empty;
if (Util.IsEnvironmentSupported(ref error))
{
m_log.Info("[OPENSIM MAIN]: Environment is supported by OpenSimulator.");
}
else
{
m_log.Warn("[OPENSIM MAIN]: Environment is not supported by OpenSimulator (" + supported + ")\n");
m_log.Warn($"[OPENSIM MAIN]: Environment is not supported by OpenSimulator: {error}\n");
}
m_log.InfoFormat("Default culture changed to {0}",Culture.GetDefaultCurrentCulture().DisplayName);
m_log.Info($"Default culture changed to {Culture.GetDefaultCurrentCulture().DisplayName}");
// Configure nIni aliases and localles
@@ -362,7 +359,7 @@ namespace OpenSim
}
catch (Exception e)
{
m_log.ErrorFormat("Command error: {0}", e);
m_log.Error($"Command error: {e}");
}
}
}
@@ -386,22 +383,19 @@ namespace OpenSim
// TODO: Add config option to allow users to turn off error reporting
// TODO: Post error report (disabled for now)
string msg = String.Empty;
msg += "\r\n";
msg += "APPLICATION EXCEPTION DETECTED: " + e.ToString() + "\r\n";
msg += "\r\n";
string msg = $"\r\nAPPLICATION EXCEPTION DETECTED: {e}\r\n\r\n";
msg += "Exception: " + e.ExceptionObject.ToString() + "\r\n";
Exception ex = (Exception) e.ExceptionObject;
Exception ex = (Exception)e.ExceptionObject;
msg += $"Exception: {ex}\r\n";
if (ex.InnerException != null)
{
msg += "InnerException: " + ex.InnerException.ToString() + "\r\n";
msg += $"InnerException: {ex.InnerException}\r\n";
}
msg += "\r\n";
msg += "Application is terminating: " + e.IsTerminating.ToString() + "\r\n";
msg += $"\r\nApplication is terminating: {e.IsTerminating}\r\n";
m_log.ErrorFormat("[APPLICATION]: {0}", msg);
m_log.Error("[APPLICATION]: + msg");
if (m_saveCrashDumps)
{
@@ -422,7 +416,7 @@ namespace OpenSim
}
catch (Exception e2)
{
m_log.ErrorFormat("[CRASH LOGGER CRASHED]: {0}", e2);
m_log.Error($"[CRASH LOGGER CRASHED]: {e2}");
}
}

View File

@@ -299,6 +299,10 @@ namespace OpenSim
+ " --mergeReplaceObjects if scene as a object with same id, replace it\n"
+ " without this option, skip loading that object\n"
+ "--skip-assets will load the OAR but ignore the assets it contains.\n"
+ "--force-assets will load the OAR and try to upload the assets it contains.\n"
+ " it will REPLACE those assets on local region cache\n"
+ " it will also try to upload them to service, but that only work if they are not present"
+ " Avoid using this except for some recovery attempts"
+ "--default-user will use this user for any objects with an owner whose UUID is not found in the grid.\n"
+ "--no-objects suppresses the addition of any objects (good for loading only the terrain).\n"
+ "--rotation specified rotation to be applied to the oar. Specified in degrees.\n"
@@ -520,8 +524,8 @@ namespace OpenSim
{
RegionInfo regionInfo = presence.Scene.RegionInfo;
if (presence.Firstname.ToLower().Equals(mainParams[2].ToLower()) &&
presence.Lastname.ToLower().Equals(mainParams[3].ToLower()))
if (presence.Firstname.Equals(mainParams[2], StringComparison.OrdinalIgnoreCase) &&
presence.Lastname.Equals(mainParams[3],StringComparison.OrdinalIgnoreCase))
{
MainConsole.Instance.Output(
"Kicking user: {0,-16} {1,-16} {2,-37} in region: {3,-16}",

View File

@@ -226,11 +226,11 @@ namespace OpenSim
throw new Exception("CombineContiguousRegions not suported");
}
string pidFile = startupConfig.GetString("PIDFile", String.Empty);
if (pidFile != String.Empty)
string pidFile = startupConfig.GetString("PIDFile", string.Empty);
if (pidFile.Length > 0)
CreatePIDFile(pidFile);
userStatsURI = startupConfig.GetString("Stats_URI", String.Empty);
userStatsURI = startupConfig.GetString("Stats_URI", string.Empty);
m_securePermissionsLoading = startupConfig.GetBoolean("SecurePermissionsLoading", true);
@@ -355,7 +355,7 @@ namespace OpenSim
// Sure is not the right place for this but do the job...
// Must always be called before (all) / the HTTP servers starting for the Certs creation or renewals.
if(startupConfig.GetBoolean("EnableSelfsignedCertSupport", false))
if (startupConfig.GetBoolean("EnableSelfsignedCertSupport", false))
{
if(!File.Exists("SSL\\ssl\\"+ startupConfig.GetString("CertFileName") +".p12") || startupConfig.GetBoolean("CertRenewOnStartup"))
{

View File

@@ -34,11 +34,11 @@ using System.IO;
using System.Net;
using System.Threading;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using Nini.Config;
using log4net;
using OpenSim.Framework;
@@ -855,6 +855,8 @@ namespace OpenSim.Region.ClientStack.Linden
int skipedMeshs = 0;
float primScaleMin = m_ModelCost.PrimScaleMin;
OSD tmp;
// build prims from instances
for (int i = 0; i < instance_list.Count; i++)
{
@@ -870,71 +872,58 @@ namespace OpenSim.Region.ClientStack.Linden
}
OSDArray face_list = (OSDArray)inner_instance_list["face_list"];
PrimitiveBaseShape pbs = null;
if (inner_instance_list.ContainsKey("mesh")) // seems to happen always but ...
if (inner_instance_list.TryGetValue("mesh", out tmp)) // seems to happen always but ...
{
int meshindx = inner_instance_list["mesh"].AsInteger();
if (meshAssets.Count > meshindx)
int meshindx = tmp.AsInteger();
if (meshindx >= 0 && meshAssets.Count > meshindx)
{
if(meshesSides != null && meshesSides.Length > meshindx)
pbs = PrimitiveBaseShape.CreateMesh(meshesSides[i], meshAssets[meshindx]);
pbs = PrimitiveBaseShape.CreateMesh(meshesSides[meshindx], meshAssets[meshindx]);
else
pbs = PrimitiveBaseShape.CreateMesh(face_list.Count, meshAssets[meshindx]);
}
}
if(pbs == null) // fallback
pbs = PrimitiveBaseShape.CreateBox();
Primitive.TextureEntry textureEntry
= new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE);
pbs ??= PrimitiveBaseShape.CreateBox(); //fallback
Primitive.TextureEntry textureEntry = new(Primitive.TextureEntry.WHITE_TEXTURE);
for (uint face = 0; face < face_list.Count; face++)
{
OSDMap faceMap = (OSDMap)face_list[(int)face];
Primitive.TextureEntryFace f = textureEntry.CreateFace(face); //clone the default
if (faceMap.ContainsKey("fullbright"))
f.Fullbright = faceMap["fullbright"].AsBoolean();
if (faceMap.ContainsKey("diffuse_color"))
f.RGBA = faceMap["diffuse_color"].AsColor4();
int textureNum = faceMap["image"].AsInteger();
float imagerot = faceMap["imagerot"].AsInteger();
float offsets = (float)faceMap["offsets"].AsReal();
float offsett = (float)faceMap["offsett"].AsReal();
float scales = (float)faceMap["scales"].AsReal();
float scalet = (float)faceMap["scalet"].AsReal();
if (faceMap.TryGetBool("fullbright", out bool fullbright))
f.Fullbright = fullbright;
if (imagerot != 0)
if (faceMap.TryGetColor4("diffuse_color", out Color4 rgba))
f.RGBA = rgba;
if(faceMap.TryGetInt("image", out int textureNum) && textureNum >= 0 && textureNum < textures.Count)
f.TextureID = textures[textureNum];
if(faceMap.TryGetFloat("imagerot", out float imagerot) && imagerot != 0)
f.Rotation = imagerot;
if (offsets != 0)
if(faceMap.TryGetFloat("offsets", out float offsets) && offsets != 0)
f.OffsetU = offsets;
if (offsett != 0)
if(faceMap.TryGetFloat("offsett", out float offsett) && offsett != 0)
f.OffsetV = offsett;
if (scales != 0)
if(faceMap.TryGetFloat("scales", out float scales) && scales != 0)
f.RepeatU = scales;
if (scalet != 0)
if(faceMap.TryGetFloat("scalet", out float scalet) && scalet != 0)
f.RepeatV = scalet;
if (textures.Count > textureNum)
f.TextureID = textures[textureNum];
textureEntry.FaceTextures[face] = f;
}
if(face_list.Count > 0)
{
int last = face_list.Count - 1;
// we do need a better te compacting code
textureEntry.DefaultTexture = textureEntry.FaceTextures[last];
textureEntry.FaceTextures[last] = null;
pbs.TextureEntry = textureEntry.GetBytes(last);
}
pbs.TextureEntry = textureEntry.GetBytes(face_list.Count);
Vector3 position = inner_instance_list["position"].AsVector3();
Quaternion rotation = inner_instance_list["rotation"].AsQuaternion();
@@ -2058,28 +2047,23 @@ namespace OpenSim.Region.ClientStack.Linden
return;
}
OSDMap resp = new OSDMap();
bool fail = true;
IClientAPI client = null;
ScenePresence sp;
IGroupsModule m_GroupsModule;
OSDMap resp;
UUID groupID = UUID.Zero;
while(true)
{
m_GroupsModule = m_Scene.RequestModuleInterface<IGroupsModule>();
IGroupsModule m_GroupsModule = m_Scene.RequestModuleInterface<IGroupsModule>();
if(m_GroupsModule == null)
break;
m_Scene.TryGetScenePresence(m_AgentID, out sp);
m_Scene.TryGetScenePresence(m_AgentID, out ScenePresence sp);
if(sp == null || sp.IsChildAgent || sp.IsDeleted)
break;
if(sp.IsInTransit && !sp.IsInLocalTransit)
break;
client = sp.ControllingClient;
IClientAPI client = sp.ControllingClient;
OSDMap req;
try
@@ -2093,10 +2077,13 @@ namespace OpenSim.Region.ClientStack.Linden
}
OSD tmp;
if(!req.TryGetValue("group_id", out tmp) || !(tmp is OSDUUID))
if(!req.TryGetValue("group_id", out tmp) || tmp is not OSDUUID)
break;
groupID = tmp.AsUUID();
if(groupID.IsZero())
groupID = sp.ControllingClient.ActiveGroupId;
if(groupID.IsZero())
break;
@@ -2110,44 +2097,63 @@ namespace OpenSim.Region.ClientStack.Linden
int memberCount = members.Count;
Dictionary<string,int> titles = new Dictionary<string,int>();
int i = 0;
ulong defaultPowers = 0;
Dictionary<string,int> titles = [];
Dictionary<ulong,int> powers = [];
// build titles array and index
roles.Sort(CompareRolesByMembersDesc);
OSDArray osdtitles = new OSDArray();
int i = 0;
OSDArray osdtitles = [];
foreach(GroupRolesData grd in roles)
{
if(grd.Title == null)
continue;
string title = grd.Title;
if(i==0)
defaultPowers = grd.Powers;
ref int powerentry = ref CollectionsMarshal.GetValueRefOrAddDefault(powers, grd.Powers, out bool _);
powerentry++;
if(!titles.ContainsKey(title))
if(grd.Title == null)
{
titles[title] = i++;
osdtitles.Add(new OSDString(title));
if(!titles.ContainsKey(string.Empty))
{
titles[string.Empty] = i++;
osdtitles.Add(new OSDString(string.Empty));
}
}
else if(!titles.ContainsKey(grd.Title))
{
titles[grd.Title] = i++;
osdtitles.Add(new OSDString(grd.Title));
}
}
if(titles.Count == 0)
break;
OSDMap osdmembers = new OSDMap();
ulong defaultPowers = 0;
int maxPowers = -1;
foreach(KeyValuePair<ulong,int> kvp in powers)
{
if (kvp.Value > maxPowers)
{
defaultPowers = kvp.Key;
maxPowers = kvp.Value;
}
}
OSDMap osdmembers = [];
foreach(GroupMembersData gmd in members)
{
OSDMap m = new OSDMap();
OSDMap m = [];
if(gmd.OnlineStatus != null && gmd.OnlineStatus != "")
m["last_login"] = new OSDString(gmd.OnlineStatus);
if(gmd.AgentPowers != defaultPowers)
m["powers"] = new OSDString((gmd.AgentPowers).ToString("X"));
if(gmd.Title != null && titles.ContainsKey(gmd.Title) && titles[gmd.Title] != 0)
m["title"] = new OSDInteger(titles[gmd.Title]);
if(gmd.Title != null)
{
if(titles.TryGetValue(gmd.Title, out int value) && value != 0)
m["title"] = new OSDInteger(value);
}
else if(titles.TryGetValue(string.Empty, out int ovalue) && ovalue != 0)
m["title"] = new OSDInteger(ovalue);
if(gmd.IsOwner)
m["owner"] = new OSDString("true");
if(gmd.Contribution != 0)
@@ -2156,30 +2162,35 @@ namespace OpenSim.Region.ClientStack.Linden
osdmembers[(gmd.AgentID).ToString()] = m;
}
OSDMap osddefaults = new OSDMap();
osddefaults["default_powers"] = new OSDString(defaultPowers.ToString("X"));
OSDMap osddefaults = new()
{
["default_powers"] = new OSDString(defaultPowers.ToString("X"))
};
resp["group_id"] = new OSDUUID(groupID);
resp["agent_id"] = new OSDUUID(m_AgentID);
resp["member_count"] = new OSDInteger(memberCount);
resp["defaults"] = osddefaults;
resp["titles"] = osdtitles;
resp["members"] = osdmembers;
resp = new OSDMap
{
["group_id"] = new OSDUUID(groupID),
["agent_id"] = new OSDUUID(m_AgentID),
["member_count"] = new OSDInteger(memberCount),
["defaults"] = osddefaults,
["titles"] = osdtitles,
["members"] = osdmembers
};
fail = false;
break;
httpResponse.RawBuffer = Util.UTF8NBGetbytes(OSDParser.SerializeLLSDXmlString(resp));
httpResponse.StatusCode = (int)HttpStatusCode.OK;
return;
}
if(fail)
resp = new()
{
resp["group_id"] = new OSDUUID(groupID);
resp["agent_id"] = new OSDUUID(m_AgentID);
resp["member_count"] = new OSDInteger(0);
resp["defaults"] = new OSDMap();
resp["titles"] = new OSDArray();
resp["members"] = new OSDMap();
}
["group_id"] = new OSDUUID(groupID),
["agent_id"] = new OSDUUID(m_AgentID),
["member_count"] = new OSDInteger(0),
["defaults"] = new OSDMap(),
["titles"] = new OSDArray(),
["members"] = new OSDMap()
};
httpResponse.RawBuffer = Util.UTF8NBGetbytes(OSDParser.SerializeLLSDXmlString(resp));
httpResponse.StatusCode = (int)HttpStatusCode.OK;
}

View File

@@ -168,6 +168,5 @@ namespace OpenSim.Region.ClientStack.Linden
{
_ = new BunchOfCaps(m_Scene, agentID, caps, ConfigOptions);
}
}
}

View File

@@ -28,6 +28,7 @@
using System;
using System.IO;
using System.IO.Compression;
using System.Collections;
using System.Collections.Generic;
using System.Text;
@@ -35,27 +36,22 @@ using System.Text;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenSim.Framework;
using OpenSim.Region.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Framework.Capabilities;
using System.IO.Compression;
using Nini.Config;
using log4net;
using System.Reflection;
using OSDArray = OpenMetaverse.StructuredData.OSDArray;
using OSDMap = OpenMetaverse.StructuredData.OSDMap;
using Nini.Config;
namespace OpenSim.Region.ClientStack.Linden
{
public struct ModelPrimLimits
{
}
public class ModelCost
{
//private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
// upload fee defaults
// fees are normalized to 1.0
@@ -102,7 +98,6 @@ namespace OpenSim.Region.ClientStack.Linden
public float PhysicalPrimScaleMax = 10f;
public int ObjectLinkedPartsMax = 512;
public ModelCost(Scene scene)
{
PrimScaleMin = scene.m_minNonphys;
@@ -164,7 +159,7 @@ namespace OpenSim.Region.ClientStack.Linden
if (ObjectLinkedPartsMax != 0 && numberInstances > ObjectLinkedPartsMax)
{
error = "Model would have more than " + ObjectLinkedPartsMax.ToString() + " linked prims";
error = $"Model would have more than {ObjectLinkedPartsMax} linked prims";
return false;
}
@@ -183,7 +178,7 @@ namespace OpenSim.Region.ClientStack.Linden
// textures cost
if (resources.texture_list != null && resources.texture_list.Array.Count > 0)
{
float textures_cost = (float)(resources.texture_list.Array.Count * basicCost);
float textures_cost = resources.texture_list.Array.Count * basicCost;
textures_cost *= ModelTextureCostFactor;
itmp = (int)(textures_cost + 0.5f); // round
@@ -199,7 +194,7 @@ namespace OpenSim.Region.ClientStack.Linden
bool curskeleton;
bool curAvatarPhys;
List<ameshCostParam> meshsCosts = new List<ameshCostParam>();
List<ameshCostParam> meshsCosts = [];
if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0)
{
@@ -236,7 +231,7 @@ namespace OpenSim.Region.ClientStack.Linden
int mesh;
int skipedSmall = 0;
List<string> skipedSmall = [];
for (int i = 0; i < numberInstances; i++)
{
Hashtable inst = (Hashtable)resources.instance_list.Array[i];
@@ -253,13 +248,14 @@ namespace OpenSim.Region.ClientStack.Linden
if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin)
{
skipedSmall++;
//m_log.WarnFormat("[MESHUPLOADER]: Mesh {0} below min scale", inst["mesh_name"].ToString());
skipedSmall.Add(inst["mesh_name"].ToString());
continue;
}
if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax)
{
error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale";
error = $"Model contains parts with sides larger than {NonPhysicalPrimScaleMax}m. Please ajust scale";
return false;
}
@@ -267,9 +263,9 @@ namespace OpenSim.Region.ClientStack.Linden
{
mesh = (int)inst["mesh"];
if (mesh >= numberMeshs)
if (mesh < 0 || mesh >= numberMeshs)
{
error = "Incoerent model information.";
error = "Incoherent model information.";
return false;
}
@@ -297,18 +293,43 @@ namespace OpenSim.Region.ClientStack.Linden
meshsfee += primCreationCost;
}
if (skipedSmall > 0)
if (skipedSmall.Count > 0)
{
if (skipedSmall > numberInstances / 2)
if (skipedSmall.Count > numberInstances / 2)
{
error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() +
"m minimum allowed size. Please check scalling";
error = $"Model contains too many prims smaller than {PrimScaleMin}m minimum allowed size. Please check scaling";
return false;
}
else
warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() +
" model prims will not upload because they are smaller than " + PrimScaleMin.ToString() +
"m minimum allowed size. Please check scalling ";
{
StringBuilder sb = new(256);
sb.Append(skipedSmall.Count);
sb.Append(" of the requested ");
sb.Append(numberInstances);
sb.Append(" model prims will not upload because they are smaller than ");
sb.Append(PrimScaleMin);
sb.Append("m minimum allowed size. Please check scaling of: ");
// The modal has a limited size, so need to cut somewhere
int i = 0;
int t = skipedSmall[i].Length + 1;
while (t < 119)
{
sb.Append(skipedSmall[i]);
sb.Append(' ');
i++;
if(i >= skipedSmall.Count)
break;
t += skipedSmall[i].Length + 1;
}
if (t >= 119 && i < skipedSmall.Count)
{
int c = t - skipedSmall[i].Length + 1;
sb.Append(skipedSmall[i].AsSpan(0, 117 - c));
sb.Append("...");
}
warning += sb.ToString();
}
}
if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost)
@@ -567,17 +588,10 @@ namespace OpenSim.Region.ClientStack.Linden
{
using (MemoryStream outMs = new MemoryStream())
{
using (MemoryStream inMs = new MemoryStream(data, offset, size))
using (MemoryStream inMs = new MemoryStream(data, offset + 2, size - 2))
{
using (DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress))
{
byte[] readBuffer = new byte[2048];
inMs.Read(readBuffer, 0, 2); // skip first 2 bytes in header
int readLen = 0;
while ((readLen = decompressionStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
outMs.Write(readBuffer, 0, readLen);
}
using DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress);
decompressionStream.CopyTo(outMs);
}
outMs.Seek(0, SeekOrigin.Begin);
decodedMeshOsd = OSDParser.DeserializeLLSDBinary(outMs);
@@ -598,7 +612,7 @@ namespace OpenSim.Region.ClientStack.Linden
if (subMeshOsd is OSDMap)
{
OSDMap subtmpmap = (OSDMap)subMeshOsd;
if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"]))
if (subtmpmap.ContainsKey("NoGeometry"))
continue;
if (!subtmpmap.ContainsKey("Position"))

View File

@@ -199,9 +199,7 @@ namespace OpenSim.Region.ClientStack.Linden
{
lock (queues)
{
if (queues.TryGetValue(agentId, out Queue<byte[]> queue))
return queue;
return null;
return queues.TryGetValue(agentId, out Queue<byte[]> queue) ? queue : null;
}
}
@@ -232,36 +230,6 @@ namespace OpenSim.Region.ClientStack.Linden
return true;
}
//legacy
/*
public bool Enqueue(string ev, UUID avatarID)
{
//m_log.DebugFormat("[EVENTQUEUE]: Enqueuing event for {0} in region {1}", avatarID, m_scene.RegionInfo.RegionName);
try
{
Queue<byte[]> queue = GetQueue(avatarID);
if (queue != null)
{
byte[] evData = Util.UTF8NBGetbytes(ev);
lock (queue)
queue.Enqueue(evData);
}
else
{
m_log.WarnFormat(
"[EVENTQUEUE]: (Enqueue) No queue found for agent {0} in region {1}",
avatarID, m_scene.Name);
}
}
catch (NullReferenceException e)
{
m_log.Error("[EVENTQUEUE] Caught exception: " + e);
return false;
}
return true;
}
*/
public bool Enqueue(byte[] evData, UUID avatarID)
{
//m_log.DebugFormat("[EVENTQUEUE]: Enqueuing event for {0} in region {1}", avatarID, m_scene.RegionInfo.RegionName);

View File

@@ -136,7 +136,7 @@ namespace OpenSim.Region.ClientStack.Linden
m_features["AnimatedObjects"] = new OSDMap()
{
["AnimatedObjectMaxTris"] = OSD.FromInteger(150000),
["MaxAgentAnimatedObjectAttachments"] = OSD.FromInteger(2)
["MaxAgentAnimatedObjectAttachments"] = OSD.FromInteger(Constants.MaxAgentAnimatedObjectAttachments)
};
m_features["BakesOnMeshEnabled"] = true;
@@ -154,6 +154,8 @@ namespace OpenSim.Region.ClientStack.Linden
m_features["MaxTextureResolution"] = OSD.FromInteger(Constants.MaxTextureResolution);
m_features["MaxProfilePicks"] = OSD.FromInteger(Constants.MaxProfilePicks);
m_features["MeshRezEnabled"] = true;
m_features["MeshUploadEnabled"] = true;
m_features["MeshXferEnabled"] = true;
@@ -182,6 +184,7 @@ namespace OpenSim.Region.ClientStack.Linden
if (m_ExportSupported)
extrasMap["ExportSupported"] = true;
m_features["OpenSimExtras"] = extrasMap;
}
}
@@ -432,7 +435,7 @@ namespace OpenSim.Region.ClientStack.Linden
while ((s = sr.ReadLine()) is not null)
{
s = s.Trim(trimc);
if (String.IsNullOrEmpty(s) || s.StartsWith("<!--"))
if (string.IsNullOrEmpty(s) || s.StartsWith("<!--"))
continue;
sb.Append(s);
}

View File

@@ -51,6 +51,7 @@ using AssetLandmark = OpenSim.Framework.AssetLandmark;
using Caps = OpenSim.Framework.Capabilities.Caps;
using PermissionMask = OpenSim.Framework.PermissionMask;
using RegionFlags = OpenMetaverse.RegionFlags;
using System.Linq;
namespace OpenSim.Region.ClientStack.LindenUDP
{
@@ -409,11 +410,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
set { m_startpos = value; }
}
public float StartFar { get; set; }
public float FOV { get; set; } = 1.25f;
public float FOV { get; set; } = 1.04f;
public int viewHeight { get; set; } = 480;
public int viewWidth { get; set; } = 640;
public UUID AgentId { get { return m_agentId; } }
public UUID ScopeId { get { return m_scopeId; } }
public ISceneAgent SceneAgent { get; set; }
@@ -875,7 +875,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
0xff, 0xff, 0, 1, 148 // ID 148 (low frequency bigendian) zero encoded
};
public void SendRegionHandshake()
{
GetViewerCaps(); // make sure this is up to date
@@ -3195,8 +3194,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(viewertime, ThrottleOutPacketType.Task);
}
public void SendViewerEffect(ViewerEffectPacket.EffectBlock[] effectBlocks)
{
ViewerEffectPacket packet = (ViewerEffectPacket)PacketPool.Instance.GetPacket(PacketType.ViewerEffect);
@@ -3217,16 +3214,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
AvatarPropertiesReplyPacket avatarReply = (AvatarPropertiesReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPropertiesReply);
avatarReply.AgentData.AgentID = m_agentId;
avatarReply.AgentData.AvatarID = avatarID;
if (aboutText != null)
avatarReply.PropertiesData.AboutText = Util.StringToBytes1024(aboutText);
else
avatarReply.PropertiesData.AboutText = Utils.EmptyBytes;
avatarReply.PropertiesData.AboutText = aboutText == null ? [] : Util.StringToBytes1024(aboutText);
avatarReply.PropertiesData.BornOn = Util.StringToBytes256(bornOn);
avatarReply.PropertiesData.CharterMember = membershipType;
if (flAbout != null)
avatarReply.PropertiesData.FLAboutText = Util.StringToBytes256(flAbout);
else
avatarReply.PropertiesData.FLAboutText = Utils.EmptyBytes;
avatarReply.PropertiesData.FLAboutText = flAbout == null ? [] : Util.StringToBytes256(flAbout);
avatarReply.PropertiesData.Flags = flags;
avatarReply.PropertiesData.FLImageID = flImageID;
avatarReply.PropertiesData.ImageID = imageID;
@@ -3410,7 +3401,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
*/
}
public void SendPartPhysicsProprieties(ISceneEntity entity)
{
IEventQueue eq = Scene.RequestModuleInterface<IEventQueue>();
@@ -3687,14 +3677,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(reply, ThrottleOutPacketType.Land);
}
public void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, Vector3 lookAt)
public void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, int options)
{
ScriptTeleportRequestPacket packet = (ScriptTeleportRequestPacket)PacketPool.Instance.GetPacket(PacketType.ScriptTeleportRequest);
packet.Header.Zerocoded = true;
packet.Data.ObjectName = Utils.StringToBytes(objName);
packet.Data.SimName = Utils.StringToBytes(simName);
packet.Data.SimPosition = pos;
packet.Data.LookAt = lookAt;
packet.Data.LookAt = Vector3.Zero;
packet.Options = options == 3 ? [] : [ new(){Flags = (uint)options } ];
OutPacket(packet, ThrottleOutPacketType.Task);
}
@@ -4183,7 +4174,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
an.Data = new AvatarNotesReplyPacket.DataBlock
{
TargetID = targetID,
Notes = Utils.StringToBytes(text)
Notes = Utils.StringToBytes(text, 1022)
};
OutPacket(an, ThrottleOutPacketType.Task);
@@ -4199,17 +4190,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
TargetID = targetID
};
ap.Data = new AvatarPicksReplyPacket.DataBlock[picks.Count];
if(picks.Count > 0)
{
List<KeyValuePair<UUID, string>> spicks = picks.ToList();
spicks.Sort((a,b) => string.Compare(a.Value, b.Value));
int i = 0;
foreach (KeyValuePair<UUID, string> pick in picks)
{
ap.Data[i++] = new AvatarPicksReplyPacket.DataBlock
int npicks = spicks.Count > Constants.MaxProfilePicks ? Constants.MaxProfilePicks : spicks.Count;
int maxtrlen = (LLUDPServer.MTU - 128) / npicks - 1;
if( maxtrlen > 128)
maxtrlen = 128;
ap.Data = new AvatarPicksReplyPacket.DataBlock[npicks];
for(int i = 0; i < npicks; ++i)
{
PickID = pick.Key,
PickName = Utils.StringToBytes(pick.Value)
};
KeyValuePair<UUID, string> pick = spicks[i];
ap.Data[i] = new AvatarPicksReplyPacket.DataBlock
{
PickID = pick.Key,
PickName = Utils.StringToBytes(pick.Value, maxtrlen)
};
}
}
else
ap.Data = [];
OutPacket(ap, ThrottleOutPacketType.Task);
}
@@ -4323,7 +4326,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
OutPacket(muteListUpdate, ThrottleOutPacketType.Task);
}
public void SendPickInfoReply(UUID pickID, UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled)
public void SendPickInfoReply(UUID pickID, UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3d posGlobal, int sortOrder, bool enabled)
{
PickInfoReplyPacket pickInfoReply = (PickInfoReplyPacket)PacketPool.Instance.GetPacket(PacketType.PickInfoReply);
@@ -4335,13 +4338,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
CreatorID = creatorID,
TopPick = topPick,
ParcelID = parcelID,
Name = Utils.StringToBytes(name),
Desc = Utils.StringToBytes(desc),
Name = Utils.StringToBytes(name, 128),
Desc = Utils.StringToBytes(desc, 1022),
SnapshotID = snapshotID,
User = Utils.StringToBytes(user),
OriginalName = Utils.StringToBytes(originalName),
User = Utils.StringToBytes(user, 128), // 64?
OriginalName = Utils.StringToBytes(originalName, 128),
SimName = Utils.StringToBytes(simName),
PosGlobal = new Vector3d(posGlobal),
PosGlobal = posGlobal,
SortOrder = sortOrder,
Enabled = enabled
};
@@ -7531,8 +7534,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
zc.AddShortLimitedUTF8(osUTF8PartText);
//textcolor
byte[] tc = part.GetTextColor().GetBytes(false);
zc.AddBytes(tc, 4);
zc.AddColorArgb(part.TextColorArgb());
}
//media url
@@ -7952,7 +7954,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
else
BlockLengh += extraParamBytes.Length;
byte[] hoverTextColor = null;
osUTF8 osUTF8PartText = part.osUTF8Text;
if (osUTF8PartText != null && osUTF8PartText.Length > 0)
{
@@ -7960,8 +7961,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
BlockLengh += osUTF8PartText.Length;
if (osUTF8PartText[^1] != 0)
++BlockLengh;
hoverTextColor = part.GetTextColor().GetBytes(false);
BlockLengh += hoverTextColor.Length;
BlockLengh += 4;
hastext = true;
}
@@ -8047,7 +8047,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
pathScaleY = 150;
}
// first is primFlags
zc.AddUInt((uint)primflags);
@@ -8089,7 +8088,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
zc.AddBytes(osUTF8PartText.GetArray(), osUTF8PartText.Length);
if (osUTF8PartText[^1] != 0)
zc.AddZeros(1);
zc.AddBytes(hoverTextColor, hoverTextColor.Length);
zc.AddColorArgb(part.TextColorArgb());
}
if (hasmediaurl)
{
@@ -8522,7 +8522,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
)
return true;
float qdelta = MathF.Abs(x.BodyRotation.Dot(m_thisAgentUpdateArgs.BodyRotation));
float qdelta = MathF.Abs(x.BodyRotation.Dot(ref m_thisAgentUpdateArgs.BodyRotation));
return qdelta < QDELTABody; // significant if body rotation above(below cos) threshold
}
@@ -9306,7 +9306,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
avSetStartLocationRequestPacket.StartLocationData.LocationPos.Y = avatar.AbsolutePosition.Y;
}
}
}
c.OnSetStartLocationRequest?.Invoke(c, 0, avSetStartLocationRequestPacket.StartLocationData.LocationPos,
avSetStartLocationRequestPacket.StartLocationData.LocationLookAt,
@@ -9551,7 +9550,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
bool IsPhantom = (data[48] != 0) ? true : false;
handlerUpdatePrimFlags(flags.AgentData.ObjectLocalID, UsePhysics, IsTemporary, IsPhantom, this);
*/
bool UsePhysics = flags.AgentData.UsePhysics;
bool UsePhysics = flags.AgentData.UsePhysics;
bool IsPhantom = flags.AgentData.IsPhantom;
bool IsTemporary = flags.AgentData.IsTemporary;
ObjectFlagUpdatePacket.ExtraPhysicsBlock[] blocks = flags.ExtraPhysics;
@@ -11191,7 +11190,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private static void HandleGetScriptRunning(LLClientView c, Packet Pack)
{
GetScriptRunningPacket scriptRunning = (GetScriptRunningPacket)Pack;
c.OnGetScriptRunning?.Invoke(c, scriptRunning.Script.ObjectID, scriptRunning.Script.ItemID);
}
@@ -11679,13 +11677,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
for (int i = 0; i < blockCount; i++)
{
GroupMembersData m = members[indx++];
groupMembersReply.MemberData[i] = new GroupMembersReplyPacket.MemberDataBlock
{
AgentID = m.AgentID,
Contribution = m.Contribution,
OnlineStatus = Util.StringToBytes256(m.OnlineStatus),
AgentPowers = m.AgentPowers,
Title = Util.StringToBytes256(m.Title),
Title = m.Title == null ? [] : Util.StringToBytes256(m.Title),
IsOwner = m.IsOwner
};
}
@@ -11719,7 +11718,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
RoleID = d.RoleID,
Name = Util.StringToBytes256(d.Name),
Title = Util.StringToBytes256(d.Title),
Title = d.Title == null ? [] : Util.StringToBytes256(d.Title),
Description = Util.StringToBytes1024(d.Description),
Powers = d.Powers,
Members = (uint)d.Members
@@ -12094,13 +12093,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
private static void HandlePickInfoUpdate(LLClientView c, Packet Pack)
{
if(c.OnPickInfoUpdate is null)
return;
PickInfoUpdatePacket pickInfoUpdate = (PickInfoUpdatePacket)Pack;
string name = Utils.BytesToString(pickInfoUpdate.Data.Name);
if(name.Length > 128)
name = name[..128];
string desc = Utils.BytesToString(pickInfoUpdate.Data.Desc);
if(desc.Length > 1022)
desc = desc[..1022];
c.OnPickInfoUpdate?.Invoke(c,
pickInfoUpdate.Data.PickID,
pickInfoUpdate.Data.CreatorID,
pickInfoUpdate.Data.TopPick,
Utils.BytesToString(pickInfoUpdate.Data.Name),
Utils.BytesToString(pickInfoUpdate.Data.Desc),
name,
desc,
pickInfoUpdate.Data.SnapshotID,
pickInfoUpdate.Data.SortOrder,
pickInfoUpdate.Data.Enabled);

View File

@@ -144,30 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
UpdateImageInQueue(imgrequest);
imgrequest.RunUpdate();
// J2KImage imgrequest2 = new J2KImage(this);
// imgrequest2.J2KDecoder = m_j2kDecodeModule;
// imgrequest2.AssetService = m_assetCache;
// imgrequest2.AgentID = m_client.AgentId;
// imgrequest2.InventoryAccessModule = m_client.Scene.RequestModuleInterface<IInventoryAccessModule>();
// imgrequest2.DiscardLevel = newRequest.DiscardLevel;
// imgrequest2.StartPacket = Math.Max(1, newRequest.PacketNumber);
// imgrequest2.Priority = newRequest.Priority;
// imgrequest2.TextureID = newRequest.RequestedAssetID;
// imgrequest2.Priority = newRequest.Priority;
//
// //Add this download to the priority queue
// AddImageToQueue(imgrequest2);
//
// imgrequest2.RunUpdate();
}
// else
// {
// m_log.DebugFormat(
// "[LL IMAGE MANAGER]: Ignoring duplicate of existing request for {0} (sequence {1}) from {2} as its request sequence {3} is not greater",
// newRequest.RequestedAssetID, imgrequest.LastSequence, m_client.Name, newRequest.requestSequence);
// }
}
}
else

View File

@@ -29,6 +29,7 @@ using System;
using System.Runtime.CompilerServices;
using OpenSim.Framework;
using OpenMetaverse;
using System.Runtime.InteropServices;
namespace OpenSim.Region.ClientStack.LindenUDP
{
@@ -88,10 +89,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
public unsafe void AddZeros(int len)
{
zerocount += len;
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
while (zerocount > 0xff)
{
m_dest[pos++] = 0x00;
m_dest[pos++] = 0xff;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = 0xff;
pos++;
zerocount -= 256;
}
}
@@ -101,24 +105,31 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (zerocount > 0)
{
m_dest[pos++] = 0x00;
m_dest[pos++] = (byte)zerocount;
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = (byte)zerocount;
pos++;
}
return pos;
}
public unsafe void AddBytes(byte[] src, int srclen)
{
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
for (int i = 0; i < srclen; ++i)
{
if (src[i] == 0x00)
byte b = src[i];
if (b == 0x00)
{
if (zerocount != 0xff)
zerocount++;
else
{
m_dest[pos++] = 0x00;
m_dest[pos++] = 0xff;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = 0xff;
pos++;
zerocount = 1;
}
}
@@ -126,18 +137,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (zerocount != 0)
{
m_dest[pos++] = 0x00;
m_dest[pos++] = (byte)zerocount;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = (byte)zerocount;
pos++;
zerocount = 0;
}
m_dest[pos++] = src[i];
Unsafe.Add(ref dst, pos) = b;
pos++;
}
}
}
public unsafe void AddBytes(byte* src, int srclen)
{
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
for (int i = 0; i < srclen; ++i)
{
if (src[i] == 0x00)
@@ -146,8 +160,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
zerocount++;
else
{
m_dest[pos++] = 0x00;
m_dest[pos++] = 0xff;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = 0xff;
pos++;
zerocount = 1;
}
}
@@ -155,17 +171,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
{
if (zerocount != 0)
{
m_dest[pos++] = 0x00;
m_dest[pos++] = (byte)zerocount;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = (byte)zerocount;
pos++;
zerocount = 0;
}
m_dest[pos++] = src[i];
Unsafe.Add(ref dst, pos) = src[i];
pos++;
}
}
}
public unsafe void AddByte(byte v)
public void AddByte(byte v)
{
if (v == 0x00)
{
@@ -173,21 +191,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
zerocount++;
else
{
m_dest[pos++] = 0x00;
m_dest[pos++] = 0xff;
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = 0xff;
pos++;
zerocount = 1;
}
}
else
{
ref byte dst = ref MemoryMarshal.GetArrayDataReference(m_dest);
if (zerocount != 0)
{
m_dest[pos++] = 0x00;
m_dest[pos++] = (byte)zerocount;
Unsafe.Add(ref dst, pos) = 0x00;
pos++;
Unsafe.Add(ref dst, pos) = (byte)zerocount;
pos++;
zerocount = 0;
}
m_dest[pos++] = v;
Unsafe.Add(ref dst, pos) = v;
pos++;
}
}
@@ -332,6 +356,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP
AddBytes(b, 16);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void AddColorArgb(int argb)
{
uint ua = (uint)argb ^ 0xff000000;
if(ua == 0)
{
AddZeros(4);
}
else
{
AddByte((byte)(ua >> 16));
AddByte((byte)(ua >> 8));
AddByte((byte)ua);
AddByte((byte)(ua >> 24));
}
}
// maxlen <= 255 and includes null termination byte
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void AddShortString(string str, int maxlen)

Some files were not shown because too many files have changed in this diff Show More