aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ThirdParty
diff options
context:
space:
mode:
authorJohan Berntsson2008-03-04 05:31:54 +0000
committerJohan Berntsson2008-03-04 05:31:54 +0000
commit279e0061c515ee0a03036bef68eea9738273d785 (patch)
tree4502228eb7b87a760e0b0e67aded9d1d870d0bed /ThirdParty
parentAdded copyright heaaders. Minor cleanup. (diff)
downloadopensim-SC_OLD-279e0061c515ee0a03036bef68eea9738273d785.zip
opensim-SC_OLD-279e0061c515ee0a03036bef68eea9738273d785.tar.gz
opensim-SC_OLD-279e0061c515ee0a03036bef68eea9738273d785.tar.bz2
opensim-SC_OLD-279e0061c515ee0a03036bef68eea9738273d785.tar.xz
Merged 3Di code that provides scene and avatar serialization, and plugin support for region move/split/merge. See ThirdParty/3Di/README.txt. Unless the new modules are used there should be no noticeable changes when running OpenSim.
Diffstat (limited to 'ThirdParty')
-rw-r--r--ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs1101
-rw-r--r--ThirdParty/3Di/LoadBalancer/TcpClient.cs240
-rw-r--r--ThirdParty/3Di/LoadBalancer/TcpServer.cs219
-rw-r--r--ThirdParty/3Di/README.txt82
-rw-r--r--ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MonitorGUI/View.pm214
-rw-r--r--ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MyCGI.pm91
-rw-r--r--ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/monitor.cgi202
-rw-r--r--ThirdParty/3Di/RegionMonitor/ServerPlugin/RegionMonitorPlugin.cs129
-rw-r--r--ThirdParty/3Di/RegionProxy/RegionProxyPlugin.cs513
9 files changed, 2791 insertions, 0 deletions
diff --git a/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs b/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs
new file mode 100644
index 0000000..6812777
--- /dev/null
+++ b/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs
@@ -0,0 +1,1101 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.IO;
31using System.Net;
32using System.Xml;
33using System.Text;
34using System.Xml.Serialization;
35using System.Net.Sockets;
36using System.Collections;
37using System.Collections.Generic;
38using System.Diagnostics;
39using System.Threading;
40
41using OpenSim.Framework;
42using OpenSim.Framework.Console;
43using OpenSim.Framework.Servers;
44using OpenSim.Region.Environment;
45using OpenSim.Region.Environment.Scenes;
46using OpenSim.Region.ClientStack;
47
48using Nwc.XmlRpc;
49using Nini.Config;
50
51using Mono.Addins;
52
53using libsecondlife;
54using libsecondlife.Packets;
55
56[assembly:Addin]
57[assembly:AddinDependency ("OpenSim", "0.5")]
58
59namespace OpenSim.ApplicationPlugins.LoadBalancer
60{
61 [Extension("/OpenSim/Startup")]
62 public class LoadBalancerPlugin : IApplicationPlugin
63 {
64 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
65
66 private OpenSimMain simMain;
67 private BaseHttpServer commandServer;
68
69 private List<UDPServer> udpServers;
70 private List<RegionInfo> regionData;
71
72 private int proxyOffset;
73 private string proxyURL;
74 private SceneManager sceneManager;
75 private string serializeDir;
76
77 private TcpServer mTcpServer;
78 private TcpClient mTcpClient;
79
80 public void Initialise(OpenSimMain openSim)
81 {
82 m_log.Info("[BALANCER] "+"Entering Initialize()");
83
84 StartTcpServer();
85 ClientView.SynchronizeClient = new ClientView.SynchronizeClientHandler(SynchronizePackets);
86 AsynchronousSocketListener.PacketHandler = new AsynchronousSocketListener.PacketRecieveHandler(SynchronizePacketRecieve);
87
88 this.sceneManager = openSim.SceneManager;
89 this.udpServers = openSim.UdpServers;
90 this.regionData = openSim.RegionData;
91 this.simMain = openSim;
92 this.commandServer = openSim.HttpServer;
93
94 proxyOffset = Int32.Parse(openSim.ConfigSource.Configs["Network"].GetString("proxy_offset", "0"));
95 proxyURL = openSim.ConfigSource.Configs["Network"].GetString("proxy_url", "");
96 if(proxyURL.Length==0) return;
97
98 serializeDir = openSim.ConfigSource.Configs["Network"].GetString("serialize_dir", "/tmp/");
99
100 commandServer.AddXmlRPCHandler("SerializeRegion", SerializeRegion);
101 commandServer.AddXmlRPCHandler("DeserializeRegion_Move", DeserializeRegion_Move);
102 commandServer.AddXmlRPCHandler("DeserializeRegion_Clone", DeserializeRegion_Clone);
103 commandServer.AddXmlRPCHandler("TerminateRegion", TerminateRegion);
104
105 commandServer.AddXmlRPCHandler("SplitRegion", SplitRegion);
106 commandServer.AddXmlRPCHandler("MergeRegions", MergeRegions);
107 commandServer.AddXmlRPCHandler("UpdatePhysics", UpdatePhysics);
108 commandServer.AddXmlRPCHandler("GetStatus", GetStatus);
109
110 m_log.Info("[BALANCER] "+"Exiting Initialize()");
111 }
112
113 private void StartTcpServer()
114 {
115 Thread server_thread = new Thread(new ThreadStart(
116 delegate {
117 mTcpServer = new TcpServer(10001);
118 mTcpServer.start();
119 }));
120 server_thread.Start();
121 }
122
123 public void Close()
124 {
125 }
126
127 private XmlRpcResponse GetStatus(XmlRpcRequest request)
128 {
129 XmlRpcResponse response = new XmlRpcResponse();
130 try
131 {
132 m_log.Info("[BALANCER] "+"Entering RegionStatus()");
133
134 int src_port = (int)request.Params[0];
135 Scene scene = null;
136 // try to get the scene object
137 RegionInfo src_region = SearchRegionFromPortNum(src_port);
138 if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false)
139 {
140 m_log.Error("[BALANCER] "+"The Scene is not found");
141 return response;
142 }
143 // serialization of client's informations
144 List<ScenePresence> presences = scene.GetScenePresences();
145 int get_scene_presence = presences.Count;
146 int get_scene_presence_filter = 0;
147 foreach (ScenePresence pre in presences)
148 {
149 ClientView client = (ClientView) pre.ControllingClient;
150 //if(pre.MovementFlag!=0 && client.PacketProcessingEnabled==true) {
151 if(client.PacketProcessingEnabled==true) {
152 get_scene_presence_filter++;
153 }
154 }
155 List<ScenePresence> avatars = scene.GetAvatars();
156 int get_avatar = avatars.Count;
157 int get_avatar_filter = 0;
158 string avatar_names = "";
159 foreach (ScenePresence pre in avatars)
160 {
161 ClientView client = (ClientView) pre.ControllingClient;
162 //if(pre.MovementFlag!=0 && client.PacketProcessingEnabled==true) {
163 if(client.PacketProcessingEnabled==true) {
164 get_avatar_filter++;
165 avatar_names += pre.Firstname + " " + pre.Lastname + "; ";
166 }
167 }
168
169 Hashtable responseData = new Hashtable();
170 responseData["get_scene_presence_filter"] = get_scene_presence_filter;
171 responseData["get_scene_presence"] = get_scene_presence;
172 responseData["get_avatar_filter"] = get_avatar_filter;
173 responseData["get_avatar"] = get_avatar;
174 responseData["avatar_names"] = avatar_names;
175 response.Value = responseData;
176
177 m_log.Info("[BALANCER] "+"Exiting RegionStatus()");
178 }
179 catch (Exception e)
180 {
181 m_log.Error("[BALANCER] "+e.ToString());
182 m_log.Error("[BALANCER] "+e.StackTrace);
183 }
184 return response;
185 }
186
187 private XmlRpcResponse SerializeRegion(XmlRpcRequest request)
188 {
189 try
190 {
191 m_log.Info("[BALANCER] "+"Entering SerializeRegion()");
192
193 string src_url = (string)request.Params[0];
194 int src_port = (int)request.Params[1];
195
196 SerializeRegion(src_url, src_port);
197
198 m_log.Info("[BALANCER] "+"Exiting SerializeRegion()");
199 }
200 catch (Exception e)
201 {
202 m_log.Error("[BALANCER] "+e.ToString());
203 m_log.Error("[BALANCER] "+e.StackTrace);
204 }
205
206 return new XmlRpcResponse();
207 }
208
209 private XmlRpcResponse DeserializeRegion_Move(XmlRpcRequest request)
210 {
211 try
212 {
213 m_log.Info("[BALANCER] "+"Entering DeserializeRegion_Move()");
214
215 string src_url = (string)request.Params[0];
216 int src_port = (int)request.Params[1];
217 string dst_url = (string)request.Params[2];
218 int dst_port = (int)request.Params[3];
219
220 DeserializeRegion_Move(src_port, dst_port, src_url, dst_url);
221
222 m_log.Info("[BALANCER] "+"Exiting DeserializeRegion_Move()");
223 }
224 catch (Exception e)
225 {
226 m_log.Error("[BALANCER] "+e.ToString());
227 m_log.Error("[BALANCER] "+e.StackTrace);
228 }
229
230 return new XmlRpcResponse();
231 }
232
233 private XmlRpcResponse DeserializeRegion_Clone(XmlRpcRequest request)
234 {
235 try
236 {
237 m_log.Info("[BALANCER] "+"Entering DeserializeRegion_Clone()");
238
239 string src_url = (string)request.Params[0];
240 int src_port = (int)request.Params[1];
241 string dst_url = (string)request.Params[2];
242 int dst_port = (int)request.Params[3];
243
244 DeserializeRegion_Clone(src_port, dst_port, src_url, dst_url);
245
246 m_log.Info("[BALANCER] "+"Exiting DeserializeRegion_Clone()");
247 }
248 catch (Exception e)
249 {
250 m_log.Error("[BALANCER] "+e.ToString());
251 m_log.Error("[BALANCER] "+e.StackTrace);
252 throw e;
253 }
254
255 return new XmlRpcResponse();
256 }
257
258 private XmlRpcResponse TerminateRegion(XmlRpcRequest request)
259 {
260 try
261 {
262 m_log.Info("[BALANCER] "+"Entering TerminateRegion()");
263
264 int src_port = (int)request.Params[0];
265
266 // backgroud
267 WaitCallback callback = new WaitCallback(TerminateRegion);
268 ThreadPool.QueueUserWorkItem(callback, src_port);
269
270 m_log.Info("[BALANCER] "+"Exiting TerminateRegion()");
271 }
272 catch (Exception e)
273 {
274 m_log.Error("[BALANCER] "+e.ToString());
275 m_log.Error("[BALANCER] "+e.StackTrace);
276 }
277
278 return new XmlRpcResponse();
279 }
280
281 // internal functions
282
283 private void SerializeRegion(string src_url, int src_port)
284 {
285 RegionInfo src_region = null;
286
287 //------------------------------------------
288 // Processing of origin region
289 //------------------------------------------
290
291 // search origin region
292 src_region = SearchRegionFromPortNum(src_port);
293
294 if (src_region == null)
295 {
296 m_log.Error("[BALANCER] "+"Region not found");
297 return;
298 }
299
300 simMain.ProxyCommand(src_region.proxyUrl, "BlockClientMessages", src_url, src_port + proxyOffset);
301
302 // serialization of origin region's data
303 SerializeRegion(src_region, serializeDir);
304 }
305
306 private void DeserializeRegion_Move(int src_port, int dst_port, string src_url, string dst_url)
307 {
308 RegionInfo dst_region = null;
309
310 //------------------------------------------
311 // Processing of destination region
312 //------------------------------------------
313
314 // import the source region's data
315 dst_region = DeserializeRegion(dst_port, true, serializeDir);
316
317 simMain.ProxyCommand(dst_region.proxyUrl, "ChangeRegion", src_port + proxyOffset, src_url, dst_port + proxyOffset, dst_url);
318 simMain.ProxyCommand(dst_region.proxyUrl, "UnblockClientMessages", dst_url, dst_port + proxyOffset);
319 }
320
321 private void DeserializeRegion_Clone(int src_port, int dst_port, string src_url, string dst_url)
322 {
323 RegionInfo dst_region = null;
324
325 //------------------------------------------
326 // Processing of destination region
327 //------------------------------------------
328
329 // import the source region's data
330 dst_region = DeserializeRegion(dst_port, false, serializeDir);
331
332 // Decide who is in charge for each section
333 int[] port = new int[] { src_port, dst_port };
334 string[] url = new string[] { "http://" + src_url + ":" + commandServer.Port, "http://" + dst_url + ":" + commandServer.Port };
335 for(int i=0; i<2; i++) simMain.XmlRpcCommand(url[i], "SplitRegion", i, 2, port[0], port[1], url[0], url[1]);
336
337 // Enable the proxy
338 simMain.ProxyCommand(dst_region.proxyUrl, "AddRegion", src_port + proxyOffset, src_url, dst_port + proxyOffset, dst_url);
339 simMain.ProxyCommand(dst_region.proxyUrl, "UnblockClientMessages", dst_url, dst_port + proxyOffset);
340 }
341
342 private void TerminateRegion(object param)
343 {
344 RegionInfo src_region = null;
345 int src_port = (int)param;
346
347 //------------------------------------------
348 // Processing of remove region
349 //------------------------------------------
350
351 // search origin region
352 src_region = SearchRegionFromPortNum(src_port);
353
354 if (src_region == null)
355 {
356 m_log.Error("[BALANCER] "+"Region not found");
357 return;
358 }
359
360 isSplit = false;
361
362 // remove client resources
363 RemoveAllClientResource(src_region);
364 // remove old region
365 RemoveRegion(src_region.RegionID, src_region.InternalEndPoint.Port);
366
367 m_log.Info("[BALANCER] "+"Region terminated");
368 }
369
370 private RegionInfo SearchRegionFromPortNum(int portnum)
371 {
372 RegionInfo result = null;
373
374 foreach (RegionInfo rinfo in regionData)
375 {
376 if (rinfo.InternalEndPoint.Port == portnum)
377 {
378// m_log.Info("BALANCER",
379// "Region found. Internal Port = {0}, Handle={1}",
380// rinfo.InternalEndPoint.Port, rinfo.RegionHandle);
381 result = rinfo;
382 break;
383 }
384 }
385
386 return result;
387 }
388
389 private UDPServer SearchUDPServerFromPortNum(int portnum)
390 {
391 return udpServers.Find( delegate(UDPServer server) { return (portnum + proxyOffset == ((IPEndPoint) server.Server.LocalEndPoint).Port); });
392 }
393
394 private void SerializeRegion(RegionInfo src_region, string export_dir)
395 {
396 Scene scene = null;
397 List<ScenePresence> presences;
398 string filename;
399 int i = 0;
400
401 // try to get the scene object
402 if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false)
403 {
404 m_log.Error("[BALANCER] "+"The Scene is not found");
405 return;
406 }
407
408 // create export directory
409 DirectoryInfo dirinfo = new DirectoryInfo(export_dir);
410 if (!dirinfo.Exists)
411 {
412 dirinfo.Create();
413 }
414
415 // serialization of client's informations
416 presences = scene.GetScenePresences();
417
418 foreach (ScenePresence pre in presences)
419 {
420 SerializeClient(i, scene, pre, export_dir);
421 i++;
422 }
423
424 // serialization of region data
425 SearializableRegionInfo dst_region = new SearializableRegionInfo(src_region);
426
427 filename = export_dir + "RegionInfo_" + src_region.RegionID.ToString() + ".bin";
428 Util.SerializeToFile(filename, dst_region);
429
430 // backup current scene's entities
431 //scene.Backup();
432
433 m_log.InfoFormat("[BALANCER] "+"region serialization completed [{0}]",
434 src_region.RegionID.ToString());
435 }
436
437 private void SerializeClient(int idx, Scene scene, ScenePresence pre, string export_dir)
438 {
439 string filename;
440 IClientAPI controller = null;
441
442 m_log.InfoFormat("[BALANCER] "+"agent id : {0}", pre.m_uuid);
443
444 uint[] circuits = scene.ClientManager.GetAllCircuits(pre.m_uuid);
445
446 foreach (uint code in circuits)
447 {
448 m_log.InfoFormat("[BALANCER] "+"circuit code : {0}", code);
449
450 if (scene.ClientManager.TryGetClient(code, out controller))
451 {
452 ClientInfo info = controller.GetClientInfo();
453
454 filename = export_dir + "ClientInfo-" + String.Format("{0:0000}", idx) + "_" + controller.CircuitCode.ToString() + ".bin";
455
456 Util.SerializeToFile(filename, info);
457
458 m_log.InfoFormat("[BALANCER] "+"client info serialized [filename={0}]", filename);
459 }
460 }
461
462 //filename = export_dir + "Presence_" + controller.AgentId.ToString() + ".bin";
463 filename = export_dir + "Presence_" + String.Format("{0:0000}", idx) + ".bin";
464
465 Util.SerializeToFile(filename, pre);
466
467 m_log.InfoFormat("[BALANCER] "+"scene presence serialized [filename={0}]", filename);
468 }
469
470 private RegionInfo DeserializeRegion(int dst_port, bool move_flag, string import_dir)
471 {
472 string[] files = null;
473 RegionInfo dst_region = null;
474
475 try
476 {
477 // deserialization of region data
478 files = Directory.GetFiles(import_dir, "RegionInfo_*.bin");
479
480 foreach (string filename in files)
481 {
482 m_log.InfoFormat("[BALANCER] RegionInfo filename = [{0}]", filename);
483
484 dst_region = new RegionInfo((SearializableRegionInfo)Util.DeserializeFromFile(filename));
485
486 m_log.InfoFormat("[BALANCER] "+"RegionID = [{0}]", dst_region.RegionID.ToString());
487 m_log.InfoFormat("[BALANCER] "+"RegionHandle = [{0}]", dst_region.RegionHandle);
488 m_log.InfoFormat("[BALANCER] "+"ProxyUrl = [{0}]", dst_region.proxyUrl);
489 m_log.InfoFormat("[BALANCER] "+"OriginRegionID = [{0}]", dst_region.originRegionID.ToString());
490
491 CreateCloneRegion(dst_region, dst_port, true);
492
493 File.Delete(filename);
494
495 m_log.InfoFormat("[BALANCER] "+"region deserialized [{0}]", dst_region.RegionID);
496 }
497
498 // deserialization of client data
499 DeserializeClient(dst_region, import_dir);
500
501 m_log.InfoFormat("[BALANCER] "+"region deserialization completed [{0}]",
502 dst_region.ToString());
503 }
504 catch (Exception e)
505 {
506 m_log.Error("[BALANCER] "+e.ToString());
507 m_log.Error("[BALANCER] "+e.StackTrace);
508 throw e;
509 }
510
511 return dst_region;
512 }
513
514 private void DeserializeClient(RegionInfo dst_region, string import_dir)
515 {
516 ScenePresence sp = null;
517 ClientInfo data = null;
518 Scene scene = null;
519 string[] files = null;
520 IClientAPI controller = null;
521 UDPServer udpserv = null;
522
523 if (sceneManager.TryGetScene(dst_region.RegionID, out scene))
524 {
525 // search udpserver
526 udpserv = SearchUDPServerFromPortNum(scene.RegionInfo.InternalEndPoint.Port);
527
528 // restore the scene presence
529/*
530 files = Directory.GetFiles(import_dir, "Presence_*.bin");
531 Array.Sort(files);
532
533 foreach (string filename in files)
534 {
535 sp = (ScenePresence)Util.DeserializeFromFile(filename);
536 Console.WriteLine("agent id = {0}", sp.m_uuid);
537
538 scene.m_restorePresences.Add(sp.m_uuid, sp);
539 File.Delete(filename);
540
541 m_log.InfoFormat("[BALANCER] "+"scene presence deserialized [{0}]", sp.m_uuid);
542 }
543*/
544 for (int i = 0; ; i++)
545 {
546 string filename = import_dir + "Presence_" + String.Format("{0:0000}", i) + ".bin";
547
548 if (!File.Exists(filename))
549 {
550 break;
551 }
552
553 sp = (ScenePresence)Util.DeserializeFromFile(filename);
554 Console.WriteLine("agent id = {0}", sp.m_uuid);
555
556 scene.m_restorePresences.Add(sp.m_uuid, sp);
557 File.Delete(filename);
558
559 m_log.InfoFormat("[BALANCER] " + "scene presence deserialized [{0}]", sp.m_uuid);
560
561 // restore the ClientView
562
563 files = Directory.GetFiles(import_dir, "ClientInfo-" + String.Format("{0:0000}", i) + "_*.bin");
564
565 foreach (string fname in files)
566 {
567 int start = fname.IndexOf('_');
568 int end = fname.LastIndexOf('.');
569 uint circuit_code = uint.Parse(fname.Substring(start + 1, end - start - 1));
570 m_log.InfoFormat("[BALANCER] " + "client circuit code = {0}", circuit_code);
571
572 data = (ClientInfo)Util.DeserializeFromFile(fname);
573
574 AgentCircuitData agentdata = new AgentCircuitData(data.agentcircuit);
575 scene.AuthenticateHandler.AddNewCircuit(circuit_code, agentdata);
576
577 udpserv.RestoreClient(agentdata, data.userEP, data.proxyEP);
578
579 // waiting for the scene-presense restored
580 lock (scene.m_restorePresences)
581 {
582 Monitor.Wait(scene.m_restorePresences, 3000);
583 }
584
585 if (scene.ClientManager.TryGetClient(circuit_code, out controller))
586 {
587 m_log.InfoFormat("[BALANCER] " + "get client [{0}]", circuit_code);
588 controller.SetClientInfo(data);
589 }
590
591 File.Delete(fname);
592
593 m_log.InfoFormat("[BALANCER] " + "client info deserialized [{0}]", circuit_code);
594 }
595
596 // backup new scene's entities
597 //scene.Backup();
598 }
599 }
600 }
601
602 private void CreateCloneRegion(RegionInfo dst_region, int dst_port, bool createID_flag)
603 {
604 if (createID_flag)
605 {
606 dst_region.RegionID = LLUUID.Random();
607 }
608
609 // change RegionInfo (memory only)
610 dst_region.InternalEndPoint.Port = dst_port;
611 dst_region.ExternalHostName = proxyURL.Split(new char[] { '/', ':' })[3];
612
613 // Create new region
614 simMain.CreateRegion(dst_region, false);
615 }
616
617 private void RemoveRegion(LLUUID regionID, int port)
618 {
619 Scene killScene;
620 if (sceneManager.TryGetScene(regionID, out killScene))
621 {
622 Console.WriteLine("scene found.");
623
624 if ((sceneManager.CurrentScene != null)
625 && (sceneManager.CurrentScene.RegionInfo.RegionID == killScene.RegionInfo.RegionID))
626 {
627 sceneManager.TrySetCurrentScene("..");
628 }
629
630 m_log.Info("Removing region : " + killScene.RegionInfo.RegionName);
631 regionData.Remove(killScene.RegionInfo);
632 sceneManager.CloseScene(killScene);
633 }
634
635 // Shutting down the UDP server
636 UDPServer udpsvr = SearchUDPServerFromPortNum(port);
637
638 if (udpsvr != null)
639 {
640 udpsvr.Server.Close();
641 udpServers.Remove(udpsvr);
642 }
643 }
644
645 private void RemoveAllClientResource(RegionInfo src_region)
646 {
647 Scene scene = null;
648 List<ScenePresence> presences;
649 IClientAPI controller = null;
650
651 // try to get the scene object
652 if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false)
653 {
654 m_log.Error("[BALANCER] "+"The Scene is not found");
655 return;
656 }
657
658 // serialization of client's informations
659 presences = scene.GetScenePresences();
660
661 // remove all scene presences
662 foreach (ScenePresence pre in presences)
663 {
664 uint[] circuits = scene.ClientManager.GetAllCircuits(pre.m_uuid);
665
666 foreach (uint code in circuits)
667 {
668 m_log.InfoFormat("[BALANCER] "+"circuit code : {0}", code);
669
670 if (scene.ClientManager.TryGetClient(code, out controller))
671 {
672 // stopping clientview thread
673 if (((ClientView)controller).PacketProcessingEnabled)
674 {
675 controller.Stop();
676 ((ClientView)controller).PacketProcessingEnabled = false;
677 }
678 // teminateing clientview thread
679 controller.Terminate();
680 m_log.Info("[BALANCER] "+"client thread stopped");
681 }
682 }
683
684 // remove scene presence
685 scene.RemoveClient(pre.m_uuid);
686 }
687 }
688
689 /*
690 * This section implements scene splitting and synchronization
691 */
692
693 private bool[] isLocalNeighbour;
694 private string[] sceneURL;
695 private int[] regionPortList;
696 private TcpClient[] tcpClientList;
697 private bool isSplit = false;
698
699 private XmlRpcResponse SplitRegion(XmlRpcRequest request)
700 {
701 try
702 {
703 int myID = (int) request.Params[0];
704 int numRegions = (int) request.Params[1];
705 regionPortList = new int[numRegions];
706 sceneURL = new string[numRegions];
707 tcpClientList = new TcpClient[numRegions];
708
709 for(int i=0; i<numRegions; i++)
710 {
711 regionPortList[i]=(int) request.Params[i+2];
712 sceneURL[i]=(string) request.Params[i+2+numRegions];
713 }
714
715 string hostname;
716
717 for(int i=0; i<numRegions; i++)
718 {
719 hostname = sceneURL[i].Split(new char[] { '/', ':' })[3];
720 m_log.InfoFormat("[SPLITSCENE] "+"creating tcp client host:{0}", hostname);
721 tcpClientList[i] = new TcpClient(hostname, 10001);
722 }
723
724 bool isMaster = (myID == 0);
725
726 isLocalNeighbour = new bool[numRegions];
727 for(int i=0; i<numRegions; i++) isLocalNeighbour[i] = (sceneURL[i] == sceneURL[myID]);
728
729 RegionInfo region = SearchRegionFromPortNum(regionPortList[myID]);
730
731 //Console.WriteLine("\n === SplitRegion {0}\n", region.RegionID);
732
733 Scene scene;
734 if (sceneManager.TryGetScene(region.RegionID, out scene))
735 {
736 // Disable event updates, backups etc in the slave(s)
737 if (isMaster) {
738 scene.Region_Status = RegionStatus.Up;
739 }
740 else
741 {
742 scene.Region_Status = RegionStatus.SlaveScene;
743 }
744
745 //Console.WriteLine("=== SplitRegion {0}: Scene found, status {1}", region.RegionID, scene.Region_Status);
746
747 // Disabling half of the avatars in master, and the other half in slave
748
749 int i = 0;
750 List<ScenePresence> presences = scene.GetScenePresences();
751presences.Sort();
752 foreach (ScenePresence pre in presences)
753 {
754 // Divide the presences evenly over the set of subscenes
755 ClientView client = (ClientView) pre.ControllingClient;
756 client.PacketProcessingEnabled = (( (i + myID) % sceneURL.Length) == 0);
757
758 m_log.InfoFormat("[SPLITSCENE] === SplitRegion {0}: SP.PacketEnabled {1}", region.RegionID, client.PacketProcessingEnabled);
759
760 if (!client.PacketProcessingEnabled)
761 {
762 // stopping clientview thread
763 client.Stop();
764 }
765
766 ++i;
767 }
768
769 scene.splitID = myID;
770 scene.SynchronizeScene = new Scene.SynchronizeSceneHandler(SynchronizeScenes);
771 isSplit = true;
772 }
773 else
774 {
775 m_log.Error("[SPLITSCENE] "+String.Format("Scene not found {0}", region.RegionID));
776 }
777 }
778 catch (Exception e)
779 {
780 m_log.Error("[SPLITSCENE] "+e.ToString());
781 m_log.Error("[SPLITSCENE] "+e.StackTrace);
782 }
783
784 return new XmlRpcResponse();
785 }
786
787 private XmlRpcResponse MergeRegions(XmlRpcRequest request)
788 {
789 // This should only be called for the master scene
790 try
791 {
792 m_log.Info("[BALANCER] "+"Entering MergeRegions()");
793
794 string src_url = (string) request.Params[0];
795 int src_port = (int) request.Params[1];
796
797 RegionInfo region = SearchRegionFromPortNum(src_port);
798
799 simMain.ProxyCommand(region.proxyUrl, "BlockClientMessages", src_url, src_port + proxyOffset);
800
801 Scene scene;
802 if (sceneManager.TryGetScene(region.RegionID, out scene))
803 {
804 isSplit = false;
805
806 scene.SynchronizeScene = null;
807 scene.Region_Status = RegionStatus.Up;
808
809 List<ScenePresence> presences = scene.GetScenePresences();
810 foreach (ScenePresence pre in presences)
811 {
812 ClientView client = (ClientView) pre.ControllingClient;
813 if (!client.PacketProcessingEnabled)
814 {
815 client.Restart();
816 client.PacketProcessingEnabled = true;
817 }
818 }
819 }
820
821 // Delete the slave scenes
822 for(int i=1; i<sceneURL.Length; i++)
823 {
824 string url = (sceneURL[i].Split('/')[2]).Split(':')[0]; // get URL part from EP
825 simMain.ProxyCommand(region.proxyUrl, "DeleteRegion", regionPortList[i] + proxyOffset, url);
826 Thread.Sleep(1000);
827 simMain.XmlRpcCommand(sceneURL[i], "TerminateRegion", regionPortList[i]); // TODO: need + proxyOffset?
828 }
829
830 simMain.ProxyCommand(region.proxyUrl, "UnblockClientMessages", src_url, src_port + proxyOffset);
831 }
832 catch (Exception e)
833 {
834 m_log.Error("[BALANCER] "+e.ToString());
835 m_log.Error("[BALANCER] "+e.StackTrace);
836 throw e;
837 }
838
839 return new XmlRpcResponse();
840 }
841
842 private XmlRpcResponse UpdatePhysics(XmlRpcRequest request)
843 {
844 // this callback receives physic scene updates from the other sub-scenes (in split mode)
845
846 int regionPort = (int) request.Params[0];
847 LLUUID scenePresenceID = new LLUUID((byte[]) request.Params[1], 0);
848 LLVector3 position = new LLVector3((byte[]) request.Params[2], 0);
849 LLVector3 velocity = new LLVector3((byte[]) request.Params[3], 0);
850 bool flying = (bool) request.Params[4];
851
852 LocalUpdatePhysics(regionPort, scenePresenceID, position, velocity, flying);
853
854 return new XmlRpcResponse();
855 }
856
857 private void LocalUpdatePhysics(int regionPort, LLUUID scenePresenceID, LLVector3 position, LLVector3 velocity, bool flying)
858 {
859 //m_log.Info("[SPLITSCENE] "+String.Format("UpdatePhysics called {0}", regionID));
860
861 //m_log.Info("[SPLITSCENE] "+"LocalUpdatePhysics [region port:{0}, client:{1}, position:{2}, velocity:{3}, flying:{4}]",
862 // regionPort, scenePresenceID.ToString(), position.ToString(),
863 // velocity.ToString(), flying);
864
865 RegionInfo region = SearchRegionFromPortNum(regionPort);
866
867 // Find and update the scene precense
868 Scene scene;
869 if (sceneManager.TryGetScene(region.RegionID, out scene))
870 {
871 ScenePresence pre = scene.GetScenePresences().Find(delegate(ScenePresence x) { return x.UUID == scenePresenceID; });
872
873 if (pre == null)
874 {
875 m_log.ErrorFormat("[SPLITSCENE] [LocalUpdatePhysics] ScenePresence is missing... ({0})", scenePresenceID.ToString());
876 return;
877 }
878
879// m_log.Info("[SPLITSCENE] "+"LocalUpdatePhysics [region:{0}, client:{1}]",
880// regionID.ToString(), pre.UUID.ToString());
881
882 pre.AbsolutePosition = position;// will set PhysicsActor.Position
883 pre.Velocity = velocity; // will set PhysicsActor.Velocity
884 pre.PhysicsActor.Flying = flying;
885 }
886 }
887
888 object padlock=new object();
889 private void SynchronizeScenes(Scene scene)
890 {
891 if (!isSplit)
892 {
893 return;
894 }
895
896 lock(padlock)
897 {
898 // Callback activated after a physics scene update
899// int i = 0;
900 List<ScenePresence> presences = scene.GetScenePresences();
901 foreach (ScenePresence pre in presences)
902 {
903 ClientView client = (ClientView) pre.ControllingClient;
904
905 // Because data changes by the physics simulation when the client doesn't move,
906 // if MovementFlag is false, It is necessary to synchronize.
907 //if(pre.MovementFlag!=0 && client.PacketProcessingEnabled==true)
908 if(client.PacketProcessingEnabled==true)
909 {
910 //m_log.Info("[SPLITSCENE] "+String.Format("Client moving in {0} {1}", scene.RegionInfo.RegionID, pre.AbsolutePosition));
911
912 for (int i = 0; i < sceneURL.Length; i++)
913 {
914 if (i == scene.splitID)
915 {
916 continue;
917 }
918
919 if(isLocalNeighbour[i])
920 {
921 //m_log.Info("[SPLITSCENE] "+"Synchronize ScenePresence (Local) [region:{0}=>{1}, client:{2}]",
922 // scene.RegionInfo.RegionID, regionPortList[i], pre.UUID.ToString());
923 LocalUpdatePhysics(regionPortList[i], pre.UUID, pre.AbsolutePosition, pre.Velocity, pre.PhysicsActor.Flying);
924 }
925 else
926 {
927 //m_log.Info("[SPLITSCENE] "+"Synchronize ScenePresence (Remote) [region port:{0}, client:{1}, position:{2}, velocity:{3}, flying:{4}]",
928 // regionPortList[i], pre.UUID.ToString(), pre.AbsolutePosition.ToString(),
929 // pre.Velocity.ToString(), pre.PhysicsActor.Flying);
930
931
932 simMain.XmlRpcCommand(sceneURL[i], "UpdatePhysics",
933 regionPortList[i], pre.UUID.GetBytes(),
934 pre.AbsolutePosition.GetBytes(), pre.Velocity.GetBytes(),
935 pre.PhysicsActor.Flying);
936
937/*
938 byte[] buff = new byte[12+12+1];
939
940 Buffer.BlockCopy(pre.AbsolutePosition.GetBytes(), 0, buff, 0, 12);
941 Buffer.BlockCopy(pre.Velocity.GetBytes(), 0, buff, 12, 12);
942 buff[24] = (byte)((pre.PhysicsActor.Flying)?1:0);
943
944 // create header
945 InternalPacketHeader header = new InternalPacketHeader();
946
947 header.type = 1;
948 header.throttlePacketType = 0;
949 header.numbytes = buff.Length;
950 header.agent_id = pre.UUID.UUID;
951 header.region_port = regionPortList[i];
952
953 //Send
954 tcpClientList[i].send(header, buff);
955*/
956 }
957 }
958 }
959// ++i;
960 }
961 }
962 }
963
964 public bool SynchronizePackets(IScene scene, Packet packet, LLUUID agentID, ThrottleOutPacketType throttlePacketType)
965 {
966 if (!isSplit)
967 {
968 return false;
969 }
970
971 Scene localScene = (Scene)scene;
972
973 for (int i = 0; i < sceneURL.Length; i++)
974 {
975 if (i == localScene.splitID)
976 {
977 continue;
978 }
979
980 if(isLocalNeighbour[i])
981 {
982 //m_log.Info("[SPLITSCENE] "+"Synchronize Packet (Local) [type:{0}, client:{1}]",
983 // packet.Type.ToString(), agentID.ToString());
984 LocalUpdatePacket(regionPortList[i], agentID, packet, throttlePacketType);
985 }
986 else
987 {
988 //m_log.Info("[SPLITSCENE] "+"Synchronize Packet (Remote) [type:{0}, client:{1}]",
989 // packet.Type.ToString(), agentID.ToString());
990 // to bytes
991 byte[] buff = packet.ToBytes();
992
993 // create header
994 InternalPacketHeader header = new InternalPacketHeader();
995
996 header.type = 0;
997 header.throttlePacketType = (int)throttlePacketType;
998 header.numbytes = buff.Length;
999 header.agent_id = agentID.UUID;
1000 header.region_port = regionPortList[i];
1001
1002 //Send
1003 tcpClientList[i].send(header, buff);
1004
1005 PacketPool.Instance.ReturnPacket(packet);
1006 }
1007 }
1008
1009 return true;
1010 }
1011
1012 private void LocalUpdatePacket(int regionPort, LLUUID agentID, Packet packet, ThrottleOutPacketType throttlePacketType)
1013 {
1014 Scene scene;
1015
1016 RegionInfo region = SearchRegionFromPortNum(regionPort);
1017
1018// m_log.Info("[SPLITSCENE] "+"LocalUpdatePacket [region port:{0}, client:{1}, packet type:{2}]",
1019// regionPort, agentID.ToString(), packet.GetType().ToString());
1020
1021 if (sceneManager.TryGetScene(region.RegionID, out scene))
1022 {
1023 ScenePresence pre = scene.GetScenePresences().Find(delegate(ScenePresence x) { return x.UUID == agentID; });
1024
1025 if (pre == null)
1026 {
1027 m_log.ErrorFormat("[SPLITSCENE] [LocalUpdatePacket] ScenePresence is missing... ({0})", agentID.ToString());
1028 return;
1029 }
1030
1031 if (((ClientView)pre.ControllingClient).PacketProcessingEnabled==true)
1032 {
1033 pre.ControllingClient.OutPacket(packet, throttlePacketType);
1034 }
1035 else
1036 {
1037 PacketPool.Instance.ReturnPacket(packet);
1038 }
1039 }
1040 }
1041
1042 public void SynchronizePacketRecieve(InternalPacketHeader header, byte[] buff)
1043 {
1044// m_log.Info("[SPLITSCENE] "+"entering SynchronizePacketRecieve[type={0}]", header.type);
1045
1046 if (!isSplit)
1047 {
1048 return;
1049 }
1050
1051 switch (header.type)
1052 {
1053 case 0:
1054
1055 Packet packet = null;
1056 byte[] zero = new byte[3000];
1057 int packetEnd = 0;
1058
1059 // deserialize packet
1060 packetEnd = buff.Length - 1;
1061// packetEnd = buff.Length;
1062
1063 try
1064 {
1065 //m_log.Info("[SPLITSCENE] "+"PacketPool.Instance : {0}", (PacketPool.Instance == null)?"null":"not null");
1066 //m_log.Info("[SPLITSCENE] "+"buff length={0}", buff.Length);
1067
1068 packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero);
1069
1070 LocalUpdatePacket(header.region_port, new LLUUID(header.agent_id),
1071 packet, (ThrottleOutPacketType)header.throttlePacketType);
1072 }
1073 catch (Exception e)
1074 {
1075 m_log.Error("[SPLITSCENE] "+e.ToString());
1076 m_log.Error("[SPLITSCENE] "+e.StackTrace);
1077 }
1078
1079 break;
1080
1081 case 1:
1082
1083 int regionPort = header.region_port;
1084 LLUUID scenePresenceID = new LLUUID(header.agent_id);
1085 LLVector3 position = new LLVector3(buff, 0);
1086 LLVector3 velocity = new LLVector3(buff, 12);
1087 bool flying = ((buff[24] == (byte)1)?true:false);
1088
1089 LocalUpdatePhysics(regionPort, scenePresenceID, position, velocity, flying);
1090
1091 break;
1092
1093 default:
1094 m_log.Info("[SPLITSCENE] "+"Invalid type");
1095 break;
1096 }
1097
1098// m_log.Info("[SPLITSCENE] "+"exiting SynchronizePacketRecieve");
1099 }
1100 }
1101}
diff --git a/ThirdParty/3Di/LoadBalancer/TcpClient.cs b/ThirdParty/3Di/LoadBalancer/TcpClient.cs
new file mode 100644
index 0000000..9f62d33
--- /dev/null
+++ b/ThirdParty/3Di/LoadBalancer/TcpClient.cs
@@ -0,0 +1,240 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.IO;
31using System.Net;
32using System.Net.Sockets;
33using System.Threading;
34using System.Text;
35using System.Runtime.Serialization.Formatters.Binary;
36
37namespace OpenSim.ApplicationPlugins.LoadBalancer {
38 public class AsynchronousClient {
39 private static ManualResetEvent connectDone = new ManualResetEvent(false);
40 private static ManualResetEvent sendDone = new ManualResetEvent(false);
41 private static ManualResetEvent receiveDone = new ManualResetEvent(false);
42 private static String response = String.Empty;
43
44 public static Socket StartClient(string hostname, int port) {
45 try {
46 IPHostEntry ipHostInfo = Dns.GetHostEntry(hostname);
47 IPAddress ipAddress = ipHostInfo.AddressList[0];
48 IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
49
50 Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
51 client.BeginConnect( remoteEP, new AsyncCallback(ConnectCallback), client);
52 connectDone.WaitOne();
53 /*
54 Send(client,"This is a test<EOF>");
55 sendDone.WaitOne();
56 Receive(client);
57 receiveDone.WaitOne();
58 client.Shutdown(SocketShutdown.Both);
59 client.Close();
60 */
61 return client;
62 } catch (Exception e) {
63 Console.WriteLine(e.ToString());
64 throw new Exception("socket error !!");
65 }
66 }
67
68 private static void ConnectCallback(IAsyncResult ar) {
69 try {
70 Socket client = (Socket) ar.AsyncState;
71 client.EndConnect(ar);
72 Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString());
73 connectDone.Set();
74 } catch (Exception e) {
75 Console.WriteLine(e.ToString());
76 }
77 }
78
79/*
80 public static void Receive(Socket client) {
81 try {
82 StateObject state = new StateObject();
83 state.workSocket = client;
84 client.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
85 } catch (Exception e) {
86 Console.WriteLine(e.ToString());
87 }
88 }
89
90 private static void ReceiveCallback( IAsyncResult ar ) {
91 try {
92 StateObject state = (StateObject) ar.AsyncState;
93 Socket client = state.workSocket;
94
95 int bytesRead = client.EndReceive(ar);
96 if (bytesRead > 0) {
97 state.sb.Append(Encoding.ASCII.GetString(state.buffer,0,bytesRead));
98 client.BeginReceive(state.buffer,0,StateObject.BufferSize,0, new AsyncCallback(ReceiveCallback), state);
99 } else {
100 if (state.sb.Length > 1) {
101 response = state.sb.ToString();
102 }
103 receiveDone.Set();
104 }
105 } catch (Exception e) {
106 Console.WriteLine(e.ToString());
107 }
108 }
109*/
110 public static void Send(Socket client, byte[] byteData) {
111 client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client);
112 }
113
114 private static void SendCallback(IAsyncResult ar) {
115 try {
116 Socket client = (Socket) ar.AsyncState;
117 int bytesSent = client.EndSend(ar);
118 //Console.WriteLine("Sent {0} bytes to server.", bytesSent);
119 sendDone.Set();
120 } catch (Exception e) {
121 Console.WriteLine(e.ToString());
122 }
123 }
124 }
125
126public class InternalPacketHeader
127{
128 private byte[] buffer = new byte[32];
129 public int type;
130 public int throttlePacketType;
131 public int numbytes;
132 public Guid agent_id;
133 public int region_port;
134
135 public void FromBytes(byte[] bytes)
136 {
137 int i = 0; // offset
138 try
139 {
140 this.type = (int)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));
141 this.throttlePacketType = (int)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));
142 this.numbytes = (int)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));
143 this.agent_id = new Guid(
144 bytes[i++] | (bytes[i++] << 8) | (bytes[i++] << 16) | bytes[i++] << 24,
145 (short)(bytes[i++] | (bytes[i++] << 8)),
146 (short)(bytes[i++] | (bytes[i++] << 8)),
147 bytes[i++], bytes[i++], bytes[i++], bytes[i++],
148 bytes[i++], bytes[i++], bytes[i++], bytes[i++]);
149 this.region_port = (int)(bytes[i++] + (bytes[i++] << 8) + (bytes[i++] << 16) + (bytes[i++] << 24));
150 }
151 catch (Exception)
152 {
153 throw new Exception("bad format!!!");
154 }
155 }
156
157 public byte[] ToBytes()
158 {
159 int i = 0;
160 this.buffer[i++] = (byte)(this.type % 256);
161 this.buffer[i++] = (byte)((this.type >> 8) % 256);
162 this.buffer[i++] = (byte)((this.type >> 16) % 256);
163 this.buffer[i++] = (byte)((this.type >> 24) % 256);
164
165 this.buffer[i++] = (byte)(this.throttlePacketType % 256);
166 this.buffer[i++] = (byte)((this.throttlePacketType >> 8) % 256);
167 this.buffer[i++] = (byte)((this.throttlePacketType >> 16) % 256);
168 this.buffer[i++] = (byte)((this.throttlePacketType >> 24) % 256);
169
170 this.buffer[i++] = (byte)(this.numbytes % 256);
171 this.buffer[i++] = (byte)((this.numbytes >> 8) % 256);
172 this.buffer[i++] = (byte)((this.numbytes >> 16) % 256);
173 this.buffer[i++] = (byte)((this.numbytes >> 24) % 256);
174
175 // no endian care
176 Buffer.BlockCopy(agent_id.ToByteArray(), 0, this.buffer, i, 16); i += 16;
177
178 this.buffer[i++] = (byte)(this.region_port % 256);
179 this.buffer[i++] = (byte)((this.region_port >> 8) % 256);
180 this.buffer[i++] = (byte)((this.region_port >> 16) % 256);
181 this.buffer[i++] = (byte)((this.region_port >> 24) % 256);
182
183 return this.buffer;
184 }
185}
186 public class TcpClient {
187
188 public static int internalPacketHeaderSize = 4*4 + 16*1;
189
190 private string mHostname;
191 private int mPort;
192 private Socket mConnection;
193 public TcpClient(string hostname, int port) {
194 this.mHostname = hostname;
195 this.mPort = port;
196 this.mConnection = null;
197 }
198 public void connect() {
199 this.mConnection = AsynchronousClient.StartClient(mHostname, mPort);
200 }
201/*
202 public void recevie() {
203 if (mConnection == null) {
204 throw new Exception("client not initialized");
205 }
206 try
207 {
208 AsynchronousClient.Receive(this.mConnection);
209 }
210 catch (Exception e)
211 {
212 Console.WriteLine(e.ToString());
213 mConnection = null;
214 }
215 }
216*/
217 public void send(InternalPacketHeader header, byte[] packet) {
218
219 lock (this)
220 {
221
222 if (mConnection == null) {
223// throw new Exception("client not initialized");
224 connect();
225 }
226
227 AsynchronousClient.Send(this.mConnection, header.ToBytes());
228
229/*
230for (int i = 0; i < 10; i++)
231{
232 Console.Write(packet[i] + " ");
233}
234Console.WriteLine("");
235*/
236 AsynchronousClient.Send(this.mConnection, packet);
237 }
238 }
239 }
240}
diff --git a/ThirdParty/3Di/LoadBalancer/TcpServer.cs b/ThirdParty/3Di/LoadBalancer/TcpServer.cs
new file mode 100644
index 0000000..ee8bcba
--- /dev/null
+++ b/ThirdParty/3Di/LoadBalancer/TcpServer.cs
@@ -0,0 +1,219 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.IO;
31using System.Net;
32using System.Net.Sockets;
33using System.Text;
34using System.Threading;
35using System.Runtime.Serialization.Formatters.Binary;
36
37using OpenSim.Framework.Console;
38
39namespace OpenSim.ApplicationPlugins.LoadBalancer {
40
41 public class StateObject {
42 public Socket workSocket = null;
43 public const int BufferSize = 2048;
44 public byte[] buffer = new byte[BufferSize];
45 public MemoryStream ms_ptr = new MemoryStream();
46 public InternalPacketHeader header = null;
47 }
48
49 public class AsynchronousSocketListener {
50 public static string data = null;
51 public static ManualResetEvent allDone = new ManualResetEvent(false);
52
53#region KIRYU
54 public delegate void PacketRecieveHandler(InternalPacketHeader header, byte[] buff);
55 public static PacketRecieveHandler PacketHandler = null;
56#endregion
57
58 public AsynchronousSocketListener() { }
59
60 public static void StartListening(int port) {
61 IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
62 IPAddress ipAddress = ipHostInfo.AddressList[0];
63 IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
64
65 Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
66 try {
67 listener.Bind(localEndPoint);
68 listener.Listen(100);
69 while (true) {
70 allDone.Reset();
71 listener.BeginAccept( new AsyncCallback(AcceptCallback), listener );
72 allDone.WaitOne();
73 }
74 } catch (Exception e) {
75 Console.WriteLine(e.ToString());
76 }
77 /*
78 Console.WriteLine("\nPress ENTER to continue...");
79 Console.Read();
80 */
81 }
82
83 public static void AcceptCallback(IAsyncResult ar) {
84 allDone.Set();
85 Socket listener = (Socket) ar.AsyncState;
86 Socket handler = listener.EndAccept(ar);
87 StateObject state = new StateObject();
88 state.workSocket = handler;
89 handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
90 }
91
92 public static void ReadCallback(IAsyncResult ar) {
93 String content = String.Empty;
94 StateObject state = (StateObject) ar.AsyncState;
95 Socket handler = state.workSocket;
96
97 try
98 {
99
100 int bytesRead = handler.EndReceive(ar);
101
102 //MainLog.Instance.Verbose("TCPSERVER", "Received packet [{0}]", bytesRead);
103
104 if (bytesRead > 0) {
105 state.ms_ptr.Write(state.buffer, 0, bytesRead);
106 }
107 else
108 {
109 //MainLog.Instance.Verbose("TCPSERVER", "Connection terminated");
110 return;
111 }
112
113 long rest_size = state.ms_ptr.Length;
114 long current_pos = 0;
115 while (rest_size > TcpClient.internalPacketHeaderSize) {
116
117 if ((state.header == null) && (rest_size >= TcpClient.internalPacketHeaderSize))
118 {
119 //MainLog.Instance.Verbose("TCPSERVER", "Processing header");
120
121 // reading header
122 state.header = new InternalPacketHeader();
123
124 byte[] headerbytes = new byte[TcpClient.internalPacketHeaderSize];
125 state.ms_ptr.Position = current_pos;
126 state.ms_ptr.Read(headerbytes, 0, TcpClient.internalPacketHeaderSize);
127 state.ms_ptr.Seek(0, SeekOrigin.End);
128 state.header.FromBytes(headerbytes);
129 }
130
131 if ((state.header != null) && (rest_size >= state.header.numbytes + TcpClient.internalPacketHeaderSize))
132 {
133 //MainLog.Instance.Verbose("TCPSERVER", "Processing body");
134
135 // reading body
136 byte[] packet = new byte[state.header.numbytes];
137 state.ms_ptr.Position = current_pos + TcpClient.internalPacketHeaderSize;
138 state.ms_ptr.Read(packet, 0, state.header.numbytes);
139
140/*
141 for(int i=0; i<state.header.numbytes; i++) {
142 System.Console.Write(packet[i] + " ");
143 }
144 System.Console.WriteLine();
145*/
146
147 state.ms_ptr.Seek(0, SeekOrigin.End);
148 // call loadbarancer function
149 if (PacketHandler != null)
150 {
151 //MainLog.Instance.Verbose("TCPSERVER", "calling PacketHandler");
152 PacketHandler(state.header, packet);
153 }
154 else
155 {
156 //MainLog.Instance.Verbose("TCPSERVER", "PacketHandler not found");
157 }
158
159 int read_size = state.header.numbytes + TcpClient.internalPacketHeaderSize;
160 state.header = null;
161
162 rest_size -= read_size;
163 current_pos += read_size;
164
165 if (rest_size < TcpClient.internalPacketHeaderSize) {
166
167 byte[] rest_bytes = new byte[rest_size];
168 state.ms_ptr.Position = read_size;
169 state.ms_ptr.Read(rest_bytes, 0, (int)rest_size);
170 state.ms_ptr.Close();
171 state.ms_ptr = new MemoryStream();
172 state.ms_ptr.Write(rest_bytes, 0, (int)rest_size);
173 break;
174 }
175 }
176
177 } // while (true)
178
179 }
180 catch (Exception e)
181 {
182 //MainLog.Instance.Verbose("TCPSERVER", e.ToString());
183 //MainLog.Instance.Verbose("TCPSERVER", e.StackTrace);
184 }
185
186 handler.BeginReceive( state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state);
187 }
188
189 private static void Send(Socket handler, String data) {
190 byte[] byteData = Encoding.ASCII.GetBytes(data);
191 handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
192 }
193
194 private static void SendCallback(IAsyncResult ar) {
195 try {
196 Socket handler = (Socket) ar.AsyncState;
197 int bytesSent = handler.EndSend(ar);
198 //Console.WriteLine("Sent {0} bytes to client.", bytesSent);
199 handler.Shutdown(SocketShutdown.Both);
200 handler.Close();
201 } catch (Exception e) {
202 Console.WriteLine(e.ToString());
203 }
204 }
205 }
206
207 public class TcpServer {
208 private int mPort = 11000;
209 public TcpServer() {
210 }
211 public TcpServer(int port) {
212 mPort = port;
213 }
214 public void start() {
215 AsynchronousSocketListener.StartListening(mPort);
216 }
217 }
218}
219
diff --git a/ThirdParty/3Di/README.txt b/ThirdParty/3Di/README.txt
new file mode 100644
index 0000000..fd7980b
--- /dev/null
+++ b/ThirdParty/3Di/README.txt
@@ -0,0 +1,82 @@
1INTRODUCTION
2
3This folder contains code that implement:
4
51. Dynamic load balancing
6
7OpenSim is allowing many regions to share a region server, but the optimal
8number of regions on each server depends on the load of each region, something
9which may change as time goes on. 3Di is working on a load balancer that
10allows the current load to be monitored and regions to be reassigned without
11requiring the servers to be restarted. To move a region, its state is
12serialized, and a new clone is created on the target server using this
13stream. The old region is then destroyed and the client viewer updated to use
14the new region address.
15
162. Region splitting
17
18Currently each region can hold only a small number of avatars. To allow more
19avatars in each region, 3Di has implemented region splitting, in which several
20copies of a given region can be distributed across the region servers. Each
21sub-region updates a fraction of the avatars, and sends state updates to the
22other sub-regions.
23
24IMPLEMENTATION
25
26The code is organised as follows:
27
28* LoadBalancer: communicates with other region servers and creates/destroys
29regions on command
30* RegionMonitor/MonitorGUI: provides a browser GUI, showing the state of the
31grid, and provides buttons for controlling region movement, splitting, and
32merging.
33* RegionMonitor/ServerPlugin: this is a region server plugin which
34communicates with the load balancer GUI to provide information
35on the identity and status of the regions on the grid
36* RegionProxy: maps messages from a clients to the true location of a region.
37
38USAGE
39
40In order to use these additions the following lines have to be added to
41OpenSim.ini:
42
43proxy_offset = -1000
44proxy_url = http://10.8.1.50:9001
45serialize_dir = /mnt/temp/
46
47If defined, proxy_offset defines how to calculate the true region port, e.g.
48if the XML defines the port as 9000 the actual port is 8000 if proxy_offset
49is -1000. The RegionProxy module will open a port at 9000 which the clients
50can connect to, and route all traffic from there to port 8000. This allows
51the region proxy to run on region server together with regions without
52blocking them by using the same port number.
53
54The proxy location is defined in proxy_url. When splitting, the region state
55is stored on a file in the folder specified in serialize_dir. This has to be
56a shared folder which both region servers involved in the split have access to.
57
583. Monitor GUI
59
60RegionMonitor/MonitorGUI is used to view status of all the managed Region
61servers, and send "Move", "Split", "Merge" commands to a specified Regions
62server.
63
64MonitorGUI is a web-based application. You can access it through a web browser.
65Its back-end is written in perl. (CGI script)
66
67Pre-requierments (CentOS, Fedora)
68
69RPM package "perl-XML-RPC" and relevant packages.
70
71Installation
72
731. Install Apache
742. copy all the files undef "ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs" to
75"$APACHE_ROOT/htdocs"
763. Configuration in "monitor.cgi"
77 * 10th line, set the value to your "monitor.cgi"'s location.
78 * 11th line, set the value to your Grid server.
79 * 12th line, set your region proxy port number here.
80 (ref. OpenSim.ini::NetWork::http_listener_port)
81* The code also works fine with mod_perl.
82
diff --git a/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MonitorGUI/View.pm b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MonitorGUI/View.pm
new file mode 100644
index 0000000..bab462f
--- /dev/null
+++ b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MonitorGUI/View.pm
@@ -0,0 +1,214 @@
1package MonitorGUI::View;
2
3use strict;
4
5my @server_list;
6my $max_port;
7my $regions;
8
9sub screen_header {
10 return << "HEADER";
11<HTML>
12<HEAD>
13<STYLE TYPE="text/css">
14<!--
15a:link {font-size: 12pt; text-decoration:none; color:#0000ff ;}
16a:visited {font-size: 12pt; text-decoration:none; color:#ff0000 ;}
17a:active {font-size: 12pt; text-decoration:none; color:#00ff00 ;}
18a:hover {font-size: 12pt; text-decoration:underline; color:#ff00ff ;}
19td {font-size: 12pt;border:0;}
20th {background-color:#000000; font-size: 12pt;border:0; color:#FFFFFF; }
21tr {background-color:#FFFFFF; }
22b {font-size: 12pt;}
23//table {background-color:#000000; }
24-->
25</STYLE>
26<META http-equiv="content-type" content="text/html;charset=UTF-8" />
27<META name="refresh" content="300" />
28<TITLE>Region Monitor GUI, 3Di</TITLE>
29</HEAD>
30<BODY>
31HEADER
32}
33
34sub screen_footer {
35 return << "FOOTER";
36</BODY>
37</HTML>
38FOOTER
39}
40
41sub html {
42 my $grid_info = shift;
43 my $regions_list = $grid_info->{"sim-profiles"};
44 $regions = undef;
45 foreach(@$regions_list) {
46 my $ip = $_->{sim_ip} || "UNKNOWN";
47 my $port = $_->{sim_port} || "UNKNOWN";
48 $regions->{$ip}->{$port} = $_;
49 if (!$regions->{max_port} || $regions->{max_port} < $port) {
50 $regions->{max_port} = $port;
51 }
52 }
53 @server_list = keys %$regions;
54 $max_port = $regions->{max_port};
55 my $html = "";
56 foreach my $machine (@server_list) {
57 next if ($machine eq "max_port");
58 $html .= &_machine_view($machine, $regions->{$machine});
59 }
60 return $html;
61}
62
63sub _machine_view {
64 my ($ip, $info) = @_;
65 my $region_html = "";
66 foreach my $region (keys %$info) {
67 $region_html .= &_region_html($info->{$region});
68 }
69 my $html =<< "MACHINE_HTML";
70<h3>$ip</h3>
71$region_html
72<hr size=0 noshade />
73MACHINE_HTML
74}
75
76sub _region_html {
77 my $region_info = shift;
78 my $name = $region_info->{name} || "UNKNOWN";
79 my $x = $region_info->{x} || -1;
80 my $y = $region_info->{y} || -1;
81 my $ip = $region_info->{sim_ip} || "UNKNOWN";
82 my $port = $region_info->{sim_port} || "UNKNOWN";
83 my $get_scene_presence_filter = $region_info->{get_scene_presence_filter};
84 my $get_scene_presence = $region_info->{get_scene_presence};
85 my $get_avatar_filter = $region_info->{get_avatar_filter};
86 my $get_avatar = $region_info->{get_avatar};
87 my $avatar_names = $region_info->{avatar_names};
88 my $action_forms = &_action_forms($region_info);
89 my $html = <<"REGION_HTML";
90<strong>$name</strong><br/>
91$ip:$port | X: $x Y: $y<br/>
92<table border="0">
93<tr>
94<td>get_avatar</td>
95<td>$get_avatar</td>
96<td></td>
97</tr>
98<tr>
99<td>get_avatar_filter</td>
100<td>$get_avatar_filter</td>
101<td>$avatar_names</td>
102</tr>
103<tr>
104<td>get_scene_presence</td>
105<td>$get_scene_presence</td>
106<td></td>
107</tr>
108<tr>
109<td>get_scene_presence_filter</td>
110<td>$get_scene_presence_filter</td>
111<td></td>
112</tr>
113</table>
114$action_forms
115REGION_HTML
116 return $html;
117}
118
119sub _action_forms {
120 my $region_info = shift;
121 my $ip = $region_info->{sim_ip};
122 my $port = $region_info->{sim_port};
123 my $default_input_port = $max_port + 1;
124 my $move_to_options = "";
125 my $split_to_options = "";
126 my $merge_ip_options = "";
127 foreach(@server_list) {
128 next if ($_ eq "max_port");
129 $merge_ip_options .= "<option value=\"$_\">$_\n";
130 $split_to_options .= "<option value=\"$_\">$_\n";
131 #next if ($_ eq $ip);
132 $move_to_options .= "<option value=\"$_\">$_\n";
133 }
134 my $merge_port_options = "";
135 my $merge_disabled = "disabled";
136
137 foreach(keys %{$regions->{$ip}}) {
138 next if ($_ eq $port);
139 $merge_disabled = "";
140 }
141# for(9000..$max_port) { # TODO :
142# next if ($_ eq $port);
143# $merge_port_options .= "<option value=\"$_\">$_\n";
144# }
145 my %port = ();
146 foreach my $ip (keys %$regions) {
147 next if ($ip eq "max_port");
148 print STDERR "--" . $ip . "\n";
149 foreach my $region_port (keys %{$regions->{$ip}}) {
150 print STDERR "---" . $region_port . "\n";
151 $port{$region_port} = 1;
152 }
153 }
154 foreach (keys %port) {
155 $merge_port_options .= "<option value=\"$_\">$_\n";
156 $merge_disabled = "";
157 }
158 return << "ACTION_FORMS";
159<table>
160<tr>
161<form method="POST">
162<td>
163<input type="hidden" name="A" value="move" />
164<input type="hidden" name="from_ip" value="$ip" />
165<input type="hidden" name="from_port" value="$port" />
166<input type="submit" value="Move to" />
167<select name="to_ip">
168$move_to_options
169</select>:
170<input type="text" name="to_port" size="5" value="$default_input_port"/>
171</td>
172</form>
173
174<td>
175&nbsp;&nbsp;|&nbsp;&nbsp;
176</td>
177
178<form method="POST">
179<td>
180<input type="hidden" name="A" value="split" />
181<input type="hidden" name="from_ip" value="$ip" />
182<input type="hidden" name="from_port" value="$port" />
183<input type="submit" value="Split to" />
184<select name="to_ip">
185$split_to_options
186</select>:
187<input type="text" name="to_port" size="5" value="$default_input_port"/>
188</td>
189</form>
190
191<td>
192&nbsp;&nbsp;|&nbsp;&nbsp;
193</td>
194
195<form method="POST">
196<td>
197<input type="hidden" name="A" value="merge" />
198<input type="hidden" name="from_ip" value="$ip" />
199<input type="hidden" name="master_port" value="$port" />
200<input type="submit" value="Merge" $merge_disabled />
201<select name="slave_ip" $merge_disabled>
202$merge_ip_options
203</select>
204<select name="slave_port" $merge_disabled>
205$merge_port_options
206</select>
207</td>
208</form>
209</tr>
210</table>
211ACTION_FORMS
212}
213
2141;
diff --git a/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MyCGI.pm b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MyCGI.pm
new file mode 100644
index 0000000..1f232aa
--- /dev/null
+++ b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MyCGI.pm
@@ -0,0 +1,91 @@
1package MyCGI;
2
3use strict;
4use CGI;
5
6sub getParam {
7 my $cgi;
8 if ($ARGV[0]) {
9 $cgi = new CGI($ARGV[0]);
10 } else {
11 $cgi = new CGI;
12 }
13 my @param_names = $cgi->param();
14 my %param = ();
15 foreach (@param_names) {
16 $param{$_} = $cgi->param($_);
17 }
18 return \%param;
19}
20
21sub getCookie {
22 my $name = shift;
23 my $cookie_value = &CGI::cookie($name);
24 return &_parse($cookie_value);
25}
26
27sub outputHtml {
28 my ($charset, $html) = @_;
29 print &CGI::header(-charset => $charset);
30 print $html;
31}
32
33sub outputXml {
34 my ($charset, $xml) = @_;
35 print &CGI::header( -type => 'text/xml', -charset => $charset );
36 print $xml;
37}
38
39sub makeCookieValue {
40 my $param = shift;
41 my @data = ();
42 foreach(keys %$param) {
43 push(@data, $_ . "=" . $param->{$_});
44 }
45 return join("&", @data);
46}
47
48sub setCookie {
49 my $param = shift;
50 my $cookie = &CGI::cookie(
51 -name => $param->{name} || return,
52 -value => $param->{value},
53 -domain => $param->{domain},
54 -path => $param->{path},
55 -expires => $param->{expires},
56 );
57 return &CGI::header(-cookie => $cookie);
58}
59
60sub redirect {
61 my $dest = shift;
62 &CGI::redirect($dest);
63}
64
65sub urlEncode {
66 my $str = shift;
67 $str =~ s/([^\w ])/'%'.unpack('H2', $1)/eg;
68 $str =~ tr/ /+/;
69 return $str;
70}
71
72sub urlDecode {
73 my $str = shift;
74 $str =~ tr/+/ /;
75 $str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
76 return $str;
77}
78
79sub _parse {
80 my $value = shift;
81 my @pair = split(/&/, $value);
82 my %data = ();
83 foreach(@pair) {
84 my ($name, $value) = split(/=/, $_);
85 $data{$name} = $value;
86 }
87 return \%data;
88}
89
901;
91
diff --git a/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/monitor.cgi b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/monitor.cgi
new file mode 100644
index 0000000..a5f6445
--- /dev/null
+++ b/ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/monitor.cgi
@@ -0,0 +1,202 @@
1#!/usr/bin/perl -w
2
3use strict;
4use Carp;
5use MyCGI;
6use XML::RPC;
7use MonitorGUI::View;
8
9use vars qw ($THIS_URL $GRID_SERVER_URL $DEFAULT_PROXY_PORT);
10$THIS_URL = "http://10.8.1.165/monitorgui/monitor.cgi";
11$GRID_SERVER_URL = "http://10.8.1.165/opensim/grid.cgi";
12$DEFAULT_PROXY_PORT = 9000;
13
14my %ACTIONS = (
15 # Region commands
16 move => \&move_command,
17 split => \&split_command,
18 merge => \&merge_command,
19 # display commands
20 default => \&main_screen,
21 refresh => \&refresh,
22);
23
24# ##################
25# main
26my $param = &MyCGI::getParam;
27my $act = $param->{A} || "default";
28my $contents = "";
29if (!$ACTIONS{$act}) {
30 &gui_error("404 NOT FOUND");
31} else {
32 eval {
33 $ACTIONS{$act}->($param);
34 };
35 if ($@) {
36 &gui_error($@);
37 }
38}
39
40# #################
41# Region Commands
42sub move_command {
43 my $param = shift;
44 # from
45 my $from_ip = $param->{from_ip} || Carp::croak("not enough params (from_ip)");
46 my $from_port = $param->{from_port} || Carp::croak("not enough params (from_port)");
47 my $from_url = "http://" . $param->{from_ip} . ":" . $DEFAULT_PROXY_PORT;
48 # to
49 my $to_ip = $param->{to_ip} || Carp::croak("not enough params (to_ip)");
50 my $to_port = $param->{to_port} || Carp::croak("not enough params (to_port)");
51 my $to_url = "http://" . $param->{to_ip} . ":" . $DEFAULT_PROXY_PORT;
52 # commands
53 eval {
54 &OpenSim::Utility::XMLRPCCall_array($from_url, "SerializeRegion", [$from_ip, $from_port]);
55 &OpenSim::Utility::XMLRPCCall_array($to_url, "DeserializeRegion_Move", [$from_ip, $from_port, $to_ip, $to_port]);
56 &OpenSim::Utility::XMLRPCCall_array($from_url, "TerminateRegion", [$from_port]);
57 };
58 if ($@) {
59 print STDERR "Get Status Error: $@\n";
60 }
61
62 # client refresh
63 &redirect_refresh({wait=>5, force=>"$from_url|$to_url", msg=>"Move region $from_ip:$from_port from $from_url to $to_url"});
64}
65
66sub split_command {
67 my $param = shift;
68 # from
69 my $from_ip = $param->{from_ip} || Carp::croak("not enough params (from_ip)");
70 my $from_port = $param->{from_port} || Carp::croak("not enough params (from_port)");
71 my $from_url = "http://" . $param->{from_ip} . ":" . $DEFAULT_PROXY_PORT;
72 # to
73 my $to_ip = $param->{to_ip} || Carp::croak("not enough params (to_ip)");
74 my $to_port = $param->{to_port} || Carp::croak("not enough params (to_port)");
75 my $to_url = "http://" . $param->{to_ip} . ":" . $DEFAULT_PROXY_PORT;
76 # commands
77 eval {
78 &OpenSim::Utility::XMLRPCCall_array($from_url, "SerializeRegion", [$from_ip, $from_port]);
79 &OpenSim::Utility::XMLRPCCall_array($to_url, "DeserializeRegion_Clone", [$from_ip, $from_port, $to_ip, $to_port]);
80 };
81 if ($@) {
82 print STDERR "Get Status Error: $@\n";
83 }
84
85 &redirect_refresh({wait=>5, force=>"$from_url", msg=>"Split region $from_ip:$from_port"});
86}
87
88sub merge_command {
89 my $param = shift;
90 # from
91 my $from_ip = $param->{from_ip} || Carp::croak("not enough params (from_ip)");
92 my $url = "http://" . $param->{from_ip} . ":" . $DEFAULT_PROXY_PORT;
93 # ports
94 my $master_port = $param->{master_port} || Carp::croak("not enough params (master_port)");
95 my $slave_ip = $param->{slave_ip} || Carp::croak("not enough params (slave_ip)");
96 my $slave_port = $param->{slave_port} || Carp::croak("not enough params (slave_port)");
97 my $slave_url = "http://" . $param->{slave_ip} . ":" . $DEFAULT_PROXY_PORT;
98 # commands
99 eval {
100 &XMLRPCCall_array($url, "MergeRegions", [$from_ip, $master_port]);
101 &XMLRPCCall_array($slave_url, "TerminateRegion", [$slave_port]);
102 };
103 if ($@) {
104 print STDERR "Get Status Error: $@\n";
105 }
106 &redirect_refresh({wait=>5, force=>"$url", msg=>"Merge region $from_ip:$master_port, $slave_port"});
107}
108
109# #################
110# Display
111sub main_screen {
112 my %xml_rpc_param = (
113 # TODO: should be 0 - 65535 ?
114 xmin => 999, ymin => 999, xmax => 1010, ymax => 1010,
115 );
116 my $res_obj = undef;
117 eval {
118 $res_obj = &XMLRPCCall($GRID_SERVER_URL, "map_block", \%xml_rpc_param);
119 };
120 if ($@) {
121 &gui_error("map_block Error: " . $@);
122 }
123 my %copy_obj = %$res_obj;
124 my $getstatus_failed = "<font color=\"red\">GetStatus Failed</font>";
125 my $regions_list = $res_obj->{"sim-profiles"};
126 foreach(@$regions_list) {
127 if ($_->{sim_ip} && $_->{sim_port}) {
128 my $url = "http://" . $_->{sim_ip} . ":" . $DEFAULT_PROXY_PORT;
129 my $port = $_->{sim_port};
130 my $res = undef;
131 eval {
132 $res = &XMLRPCCall_array($url, "GetStatus", [$port]);
133 };
134 if ($@) {
135 print STDERR "Get Status Error: $@\n";
136 }
137 $_->{get_scene_presence_filter} = $res ? $res->{get_scene_presence_filter} : $getstatus_failed;
138 $_->{get_scene_presence} = $res ? $res->{get_scene_presence} : $getstatus_failed;
139 $_->{get_avatar_filter} = $res ? $res->{get_avatar_filter} : $getstatus_failed;
140 $_->{get_avatar} = $res ? $res->{get_avatar} : $getstatus_failed;
141 $_->{avatar_names} = $res ? $res->{avatar_names} : "NO USER";
142 }
143 }
144 my $html = &MonitorGUI::View::html(\%copy_obj);
145 &MyCGI::outputHtml("UTF-8", &MonitorGUI::View::screen_header . $html . &MonitorGUI::View::screen_footer);
146}
147
148sub gui_error {
149 my $msg = shift;
150 &MyCGI::outputHtml("UTF-8", "<h1>ERROR</h1><hr />$msg");
151}
152
153sub redirect_refresh {
154 my $args = shift;
155 my $wait = $args->{wait};
156 my $force = $args->{force} || "";
157 my $msg = $args->{msg} || "";
158 my $param = "A=refresh&wait=$wait&ip=$force&msg=$msg";
159 my $dist_url = $THIS_URL . "?" . $param;
160 &MyCGI::redirect($dist_url);
161}
162
163sub refresh {
164 my $param = shift;
165 my $msg = $param->{msg} || "";
166 my $wait = $param->{wait} || 0;
167 my $force = $param->{ip} || "";
168 #my $jump_url = $force ? "$THIS_URL?A=force&ip=$force" : $THIS_URL;
169 my $jump_url = $THIS_URL;
170 my $html =<< "HTML";
171<html>
172<head>
173<meta http-equiv="Refresh" content="$wait;URL=$jump_url" />
174<title>Region Monitor GUI REFRESH</title>
175</head>
176<body>
177<h3>$msg</h3>
178<br>
179wait <font color="red"><b>$wait</b></font> sec for server to take effect ... <br>
180(* The page will jump to "Monitor Screen" automatically)
181</body>
182</html>
183HTML
184 &MyCGI::outputHtml("UTF-8", $html);
185}
186
187# ##################
188# Utility
189sub XMLRPCCall {
190 my ($url, $methodname, $param) = @_;
191 my $xmlrpc = new XML::RPC($url);
192 my $result = $xmlrpc->call($methodname, $param);
193 return $result;
194}
195
196sub XMLRPCCall_array {
197 my ($url, $methodname, $param) = @_;
198 my $xmlrpc = new XML::RPC($url);
199 my $result = $xmlrpc->call($methodname, @$param);
200 return $result;
201}
202
diff --git a/ThirdParty/3Di/RegionMonitor/ServerPlugin/RegionMonitorPlugin.cs b/ThirdParty/3Di/RegionMonitor/ServerPlugin/RegionMonitorPlugin.cs
new file mode 100644
index 0000000..5d2df3a
--- /dev/null
+++ b/ThirdParty/3Di/RegionMonitor/ServerPlugin/RegionMonitorPlugin.cs
@@ -0,0 +1,129 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28using System;
29using System.Runtime.Remoting;
30using System.Threading;
31using Mono.Addins;
32using OpenSim;
33using OpenSim.Framework.Console;
34using MonitorLib;
35
36[assembly:Addin]
37[assembly:AddinDependency ("OpenSim", "0.5")]
38
39namespace OpenSim.ApplicationPlugins.RegionMonitor
40{
41 [Extension("/OpenSim/Startup")]
42 public class RegionMonitorPlugin : MonitorLibBase, IApplicationPlugin
43 {
44 protected Thread m_mointorThread;
45 protected static OpenSimMain m_openSimMain;
46
47 public void Initialise(OpenSimMain opensim)
48 {
49 m_openSimMain = opensim;
50 Start();
51 MainLog.Instance.Verbose("Monitor", "Region monitor is runing ...");
52 }
53
54 public void Close()
55 {
56 }
57
58 public void Start()
59 {
60 // start monitor thread (remoting module)
61 m_mointorThread = new Thread(new ThreadStart(StartMonitor));
62 m_mointorThread.IsBackground = true;
63 m_mointorThread.Start();
64 }
65
66 private void StartMonitor()
67 {
68 try
69 {
70 Object lockObj = new Object();
71
72 RemotingConfiguration.Configure("monitorS.config", false);
73
74 lock (lockObj)
75 {
76 System.Threading.Monitor.Wait(lockObj);
77 }
78 }
79 catch (Exception e)
80 {
81 MainLog.Instance.Warn("MONITOR", "Error - " + e.Message);
82 }
83 }
84
85 public override bool FetchInfo(out string outstr)
86 {
87 MainLog.Instance.Verbose("MONITOR", "Fetch Information from Region server");
88 bool status = true;
89 string startTime = "";
90 string upTime = "";
91 int userNumber = 0;
92 int regionNumber = 0;
93 m_openSimMain.GetRunTime(out startTime, out upTime);
94 m_openSimMain.GetAvatarNumber(out userNumber);
95 m_openSimMain.GetRegionNumber(out regionNumber);
96 outstr = startTime
97 + "," + upTime
98 + "," + regionNumber
99 + "," + userNumber;
100 return status;
101 }
102
103
104 public override bool MoveRegion()
105 {
106 MainLog.Instance.Verbose("MONITOR", "Move Region");
107 bool status = true;
108
109 return status;
110 }
111
112 public override bool SplitRegion()
113 {
114 MainLog.Instance.Verbose("MONITOR", "Split Region");
115 bool status = true;
116
117 return status;
118 }
119
120 public override bool MergeScenes()
121 {
122 MainLog.Instance.Verbose("MONITOR", "Merge Scenes");
123 bool status = true;
124
125 return status;
126 }
127
128 }
129}
diff --git a/ThirdParty/3Di/RegionProxy/RegionProxyPlugin.cs b/ThirdParty/3Di/RegionProxy/RegionProxyPlugin.cs
new file mode 100644
index 0000000..13aec48
--- /dev/null
+++ b/ThirdParty/3Di/RegionProxy/RegionProxyPlugin.cs
@@ -0,0 +1,513 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.IO;
31using System.Net;
32using System.Xml;
33using System.Text;
34using System.Xml.Serialization;
35using System.Net.Sockets;
36using System.Collections;
37using System.Collections.Generic;
38using System.Diagnostics;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Servers;
42using OpenSim.Framework.Console;
43using Nwc.XmlRpc;
44
45using Mono.Addins;
46
47[assembly:Addin]
48[assembly:AddinDependency ("OpenSim", "0.5")]
49
50namespace OpenSim.ApplicationPlugins.RegionProxy
51{
52 /* This module has an interface to OpenSim clients that is constant, and is responsible for relaying
53 * messages to and from clients to the region objects. Since the region objects can be duplicated and
54 * moved dynamically, the proxy provides methods for changing and adding regions. If more than one region
55 * is associated with a client port, then the message will be broadcasted to all those regions.
56 *
57 * The client interface port may be blocked. While being blocked, all messages from the clients will be
58 * stored in the proxy. Once the interface port is unblocked again, all stored messages will be resent
59 * to the regions. This functionality is used when moving or cloning an region to make sure that no messages
60 * are sent to the region while it is being reconfigured.
61 *
62 * The proxy opens a XmlRpc interface with these public methods:
63 * - AddPort
64 * - AddRegion
65 * - ChangeRegion
66 * - BlockClientMessages
67 * - UnblockClientMessages
68 */
69
70 [Extension("/OpenSim/Startup")]
71 public class RegionProxyPlugin : IApplicationPlugin
72 {
73 private ProxyServer proxy;
74 private BaseHttpServer command_server;
75 private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
76
77 public void Initialise(OpenSimMain openSim)
78 {
79 Console.WriteLine("Starting proxy");
80 string proxyURL = openSim.ConfigSource.Configs["Network"].GetString("proxy_url", "");
81 if(proxyURL.Length==0) return;
82
83 uint port = (uint) Int32.Parse(proxyURL.Split(new char[] { ':' })[2]);
84 command_server = new BaseHttpServer(port);
85 command_server.Start();
86 command_server.AddXmlRPCHandler("AddPort", AddPort);
87 command_server.AddXmlRPCHandler("AddRegion", AddRegion);
88 command_server.AddXmlRPCHandler("DeleteRegion", DeleteRegion);
89 command_server.AddXmlRPCHandler("ChangeRegion", ChangeRegion);
90 command_server.AddXmlRPCHandler("BlockClientMessages", BlockClientMessages);
91 command_server.AddXmlRPCHandler("UnblockClientMessages", UnblockClientMessages);
92 command_server.AddXmlRPCHandler("Stop", Stop);
93
94 proxy=new ProxyServer(m_log);
95 }
96
97 public void Close()
98 {
99 }
100
101 private XmlRpcResponse Stop(XmlRpcRequest request)
102 {
103 try
104 {
105 proxy.Stop();
106 }
107 catch (Exception e)
108 {
109 m_log.Error("[PROXY]" + e.Message);
110 m_log.Error("[PROXY]" + e.StackTrace);
111 }
112 return new XmlRpcResponse();
113 }
114
115 private XmlRpcResponse AddPort(XmlRpcRequest request)
116 {
117 try {
118 int clientPort = (int) request.Params[0];
119 int regionPort = (int) request.Params[1];
120 string regionUrl = (string) request.Params[2];
121 proxy.AddPort(clientPort, regionPort, regionUrl);
122 } catch(Exception e) {
123 m_log.Error("[PROXY]"+e.Message);
124 m_log.Error("[PROXY]"+e.StackTrace);
125 }
126 return new XmlRpcResponse();
127 }
128
129 private XmlRpcResponse AddRegion(XmlRpcRequest request)
130 {
131 try {
132 int currentRegionPort = (int) request.Params[0];
133 string currentRegionUrl = (string) request.Params[1];
134 int newRegionPort = (int) request.Params[2];
135 string newRegionUrl = (string) request.Params[3];
136 proxy.AddRegion(currentRegionPort, currentRegionUrl, newRegionPort, newRegionUrl);
137 } catch(Exception e) {
138 m_log.Error("[PROXY]"+e.Message);
139 m_log.Error("[PROXY]"+e.StackTrace);
140 }
141 return new XmlRpcResponse();
142 }
143
144 private XmlRpcResponse ChangeRegion(XmlRpcRequest request)
145 {
146 try {
147 int currentRegionPort = (int) request.Params[0];
148 string currentRegionUrl = (string) request.Params[1];
149 int newRegionPort = (int) request.Params[2];
150 string newRegionUrl = (string) request.Params[3];
151 proxy.ChangeRegion(currentRegionPort, currentRegionUrl, newRegionPort, newRegionUrl);
152 } catch(Exception e) {
153 m_log.Error("[PROXY]"+e.Message);
154 m_log.Error("[PROXY]"+e.StackTrace);
155 }
156 return new XmlRpcResponse();
157 }
158
159 private XmlRpcResponse DeleteRegion(XmlRpcRequest request)
160 {
161 try {
162 int currentRegionPort = (int) request.Params[0];
163 string currentRegionUrl = (string) request.Params[1];
164 proxy.DeleteRegion(currentRegionPort, currentRegionUrl);
165 } catch(Exception e) {
166 m_log.Error("[PROXY]"+e.Message);
167 m_log.Error("[PROXY]"+e.StackTrace);
168 }
169 return new XmlRpcResponse();
170 }
171
172 private XmlRpcResponse BlockClientMessages(XmlRpcRequest request)
173 {
174 try {
175 string regionUrl = (string) request.Params[0];
176 int regionPort = (int) request.Params[1];
177 proxy.BlockClientMessages(regionUrl, regionPort);
178 } catch(Exception e) {
179 m_log.Error("[PROXY]"+e.Message);
180 m_log.Error("[PROXY]"+e.StackTrace);
181 }
182 return new XmlRpcResponse();
183 }
184
185 private XmlRpcResponse UnblockClientMessages(XmlRpcRequest request)
186 {
187 try {
188 string regionUrl = (string) request.Params[0];
189 int regionPort = (int) request.Params[1];
190 proxy.UnblockClientMessages(regionUrl, regionPort);
191 } catch(Exception e) {
192 m_log.Error("[PROXY]"+e.Message);
193 m_log.Error("[PROXY]"+e.StackTrace);
194 }
195 return new XmlRpcResponse();
196 }
197 }
198
199
200 public class ProxyServer {
201 protected AsyncCallback receivedData;
202 protected ProxyMap proxy_map = new ProxyMap();
203 protected readonly log4net.ILog m_log;
204 protected bool running;
205
206 protected class ProxyMap
207 {
208 public class RegionData
209 {
210 public bool isBlocked = false;
211 public Queue storedMessages = new Queue();
212 public List<EndPoint> regions = new List<EndPoint>();
213 }
214
215 private Dictionary<EndPoint, RegionData> map;
216
217 public ProxyMap() {
218 map = new Dictionary<EndPoint, RegionData>();
219 }
220
221 public void Add(EndPoint client, EndPoint region)
222 {
223 if(map.ContainsKey(client))
224 {
225 map[client].regions.Add(region);
226 }
227 else
228 {
229 RegionData regions = new RegionData();
230 map.Add(client, regions);
231 regions.regions.Add(region);
232 }
233 }
234
235 public RegionData GetRegionData(EndPoint client)
236 {
237 return map[client];
238 }
239
240 public EndPoint GetClient(EndPoint region)
241 {
242 foreach (KeyValuePair<EndPoint, RegionData> pair in map)
243 {
244 if(pair.Value.regions.Contains(region))
245 {
246 return pair.Key;
247 }
248 }
249 return null;
250 }
251 }
252
253 protected class ServerData {
254 public Socket server;
255 public EndPoint clientEP;
256 public EndPoint senderEP;
257 public IPEndPoint serverIP;
258 public byte[] recvBuffer = new byte[4096];
259
260 public ServerData()
261 {
262 server = null;
263 }
264 }
265
266 protected class StoredMessage
267 {
268 public byte[] buffer;
269 public int length;
270 public EndPoint senderEP;
271 public ServerData sd;
272
273 public StoredMessage(byte[] buffer, int length, int maxLength, EndPoint senderEP, ServerData sd)
274 {
275 this.buffer = new byte[maxLength];
276 this.length = length;
277 for(int i=0; i<length; i++) this.buffer[i]=buffer[i];
278 this.senderEP = senderEP;
279 this.sd = sd;
280 }
281 }
282
283 public ProxyServer(log4net.ILog log)
284 {
285 m_log = log;
286 running=false;
287 receivedData = new AsyncCallback(OnReceivedData);
288 }
289
290 public void BlockClientMessages(string regionUrl, int regionPort)
291 {
292 EndPoint client = proxy_map.GetClient(new IPEndPoint(IPAddress.Parse(regionUrl), regionPort));
293 ProxyMap.RegionData rd = proxy_map.GetRegionData(client);
294 rd.isBlocked = true;
295 }
296
297 public void UnblockClientMessages(string regionUrl, int regionPort)
298 {
299 EndPoint client = proxy_map.GetClient(new IPEndPoint(IPAddress.Parse(regionUrl), regionPort));
300 ProxyMap.RegionData rd = proxy_map.GetRegionData(client);
301
302 rd.isBlocked = false;
303 while(rd.storedMessages.Count > 0) {
304 StoredMessage msg = (StoredMessage) rd.storedMessages.Dequeue();
305 //m_log.Verbose("[PROXY]"+"Resending blocked message from {0}", msg.senderEP);
306 SendMessage(msg.buffer, msg.length, msg.senderEP, msg.sd);
307 }
308 }
309
310 public void AddRegion(int oldRegionPort, string oldRegionUrl, int newRegionPort, string newRegionUrl)
311 {
312 //m_log.Verbose("[PROXY]"+"AddRegion {0} {1}", oldRegionPort, newRegionPort);
313 EndPoint client = proxy_map.GetClient(new IPEndPoint(IPAddress.Parse(oldRegionUrl), oldRegionPort));
314 ProxyMap.RegionData data = proxy_map.GetRegionData(client);
315 data.regions.Add(new IPEndPoint(IPAddress.Parse(newRegionUrl), newRegionPort));
316 }
317
318 public void ChangeRegion(int oldRegionPort, string oldRegionUrl, int newRegionPort, string newRegionUrl)
319 {
320 //m_log.Verbose("[PROXY]"+"ChangeRegion {0} {1}", oldRegionPort, newRegionPort);
321 EndPoint client = proxy_map.GetClient(new IPEndPoint(IPAddress.Parse(oldRegionUrl), oldRegionPort));
322 ProxyMap.RegionData data = proxy_map.GetRegionData(client);
323 data.regions.Clear();
324 data.regions.Add(new IPEndPoint(IPAddress.Parse(newRegionUrl), newRegionPort));
325 }
326
327 public void DeleteRegion(int oldRegionPort, string oldRegionUrl)
328 {
329 m_log.InfoFormat("[PROXY]"+"DeleteRegion {0} {1}", oldRegionPort, oldRegionUrl);
330 EndPoint regionEP = new IPEndPoint(IPAddress.Parse(oldRegionUrl), oldRegionPort);
331 EndPoint client = proxy_map.GetClient(regionEP);
332 ProxyMap.RegionData data = proxy_map.GetRegionData(client);
333 data.regions.Remove(regionEP);
334 }
335
336 public void AddPort(int clientPort, int regionPort, string regionUrl)
337 {
338 running = true;
339
340 //m_log.Verbose("[PROXY]"+"AddPort {0} {1}", clientPort, regionPort);
341 IPEndPoint clientEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), clientPort);
342 proxy_map.Add(clientEP, new IPEndPoint(IPAddress.Parse(regionUrl), regionPort));
343
344 ServerData sd = new ServerData();
345 sd.clientEP = new IPEndPoint(clientEP.Address, clientEP.Port);
346
347 OpenPort(sd);
348 }
349
350 protected void OpenPort(ServerData sd)
351 {
352 // sd.clientEP must be set before calling this function
353
354 ClosePort(sd);
355
356 try
357 {
358
359 m_log.InfoFormat("[PROXY] Opening UDP socket on {0}", sd.clientEP);
360 sd.serverIP = new IPEndPoint(IPAddress.Parse("0.0.0.0"), ((IPEndPoint)sd.clientEP).Port);
361 sd.server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
362 sd.server.Bind(sd.serverIP);
363
364 sd.senderEP = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0);
365 //receivedData = new AsyncCallback(OnReceivedData);
366 sd.server.BeginReceiveFrom(sd.recvBuffer, 0, sd.recvBuffer.Length, SocketFlags.None, ref sd.senderEP, receivedData, sd);
367 }
368 catch (Exception e)
369 {
370 m_log.ErrorFormat("[PROXY] Failed to (re)open socket {0}", sd.clientEP);
371 m_log.Error("[PROXY]" + e.Message);
372 m_log.Error("[PROXY]" + e.StackTrace);
373 }
374 }
375
376 protected void ClosePort(ServerData sd)
377 {
378 // Close the port if it exists and is open
379 if (sd.server == null) return;
380
381 try
382 {
383 sd.server.Shutdown(SocketShutdown.Both);
384 sd.server.Close();
385 }
386 catch (Exception)
387 {
388 }
389 }
390
391 public void Stop()
392 {
393 running = false;
394 m_log.InfoFormat("[PROXY] Stopping the proxy server");
395
396 }
397
398
399 protected virtual void OnReceivedData(IAsyncResult result)
400 {
401 if(!running) return;
402
403 ServerData sd = (ServerData)result.AsyncState;
404 sd.senderEP = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0);
405
406 try
407 {
408 int numBytes = sd.server.EndReceiveFrom(result, ref sd.senderEP);
409 if (numBytes > 0)
410 {
411 SendMessage(sd.recvBuffer, numBytes, sd.senderEP, sd);
412 }
413 }
414 catch (Exception e)
415 {
416// OpenPort(sd); // reopen the port just in case
417 m_log.ErrorFormat("[PROXY] EndReceiveFrom failed in {0}", sd.clientEP);
418 m_log.Error("[PROXY]" + e.Message);
419 m_log.Error("[PROXY]" + e.StackTrace);
420 }
421
422 WaitForNextMessage(sd);
423 }
424
425 protected void WaitForNextMessage(ServerData sd)
426 {
427 bool error = true;
428 while (error)
429 {
430 error = false;
431 try
432 {
433 sd.server.BeginReceiveFrom(sd.recvBuffer, 0, sd.recvBuffer.Length, SocketFlags.None, ref sd.senderEP, receivedData, sd);
434 }
435 catch (Exception e)
436 {
437 error = true;
438 m_log.ErrorFormat("[PROXY] BeginReceiveFrom failed, retrying... {0}", sd.clientEP);
439 m_log.Error("[PROXY]" + e.Message);
440 m_log.Error("[PROXY]" + e.StackTrace);
441 OpenPort(sd);
442 }
443 }
444 }
445
446 protected void SendMessage(byte[] buffer, int length, EndPoint senderEP, ServerData sd)
447 {
448 int numBytes = length;
449
450 //m_log.ErrorFormat("[PROXY] Got message from {0} in thread {1}, size {2}", senderEP, sd.clientEP, numBytes);
451 EndPoint client = proxy_map.GetClient(senderEP);
452
453 if (client != null)
454 {
455 try
456 {
457 client = PacketPool.DecodeProxyMessage(buffer, ref numBytes);
458 try
459 {
460 // This message comes from a region object, forward it to the its client
461 sd.server.SendTo(buffer, numBytes, SocketFlags.None, client);
462 //m_log.InfoFormat("[PROXY] Sending region message from {0} to {1}, size {2}", senderEP, client, numBytes);
463 }
464 catch (Exception e)
465 {
466 OpenPort(sd); // reopen the port just in case
467 m_log.ErrorFormat("[PROXY] Failed sending region message from {0} to {1}", senderEP, client);
468 m_log.Error("[PROXY]" + e.Message);
469 m_log.Error("[PROXY]" + e.StackTrace);
470 return;
471 }
472 }
473 catch (Exception e)
474 {
475 OpenPort(sd); // reopen the port just in case
476 m_log.ErrorFormat("[PROXY] Failed decoding region message from {0}", senderEP);
477 m_log.Error("[PROXY]" + e.Message);
478 m_log.Error("[PROXY]" + e.StackTrace);
479 return;
480 }
481
482 }
483 else
484 {
485 // This message comes from a client object, forward it to the the region(s)
486 PacketPool.EncodeProxyMessage(buffer, ref numBytes, senderEP);
487 ProxyMap.RegionData rd = proxy_map.GetRegionData(sd.clientEP);
488 foreach (EndPoint region in rd.regions)
489 {
490 if(rd.isBlocked) {
491 rd.storedMessages.Enqueue(new StoredMessage(buffer, length, numBytes, senderEP, sd));
492 }
493 else
494 {
495 try
496 {
497 sd.server.SendTo(buffer, numBytes, SocketFlags.None, region);
498 //m_log.InfoFormat("[PROXY] Sending client message from {0} to {1}", senderEP, region);
499 }
500 catch (Exception e)
501 {
502 OpenPort(sd); // reopen the port just in case
503 m_log.ErrorFormat("[PROXY] Failed sending client message from {0} to {1}", senderEP, region);
504 m_log.Error("[PROXY]" + e.Message);
505 m_log.Error("[PROXY]" + e.StackTrace);
506 return;
507 }
508 }
509 }
510 }
511 }
512 }
513}