diff options
author | Johan Berntsson | 2008-03-04 05:31:54 +0000 |
---|---|---|
committer | Johan Berntsson | 2008-03-04 05:31:54 +0000 |
commit | 279e0061c515ee0a03036bef68eea9738273d785 (patch) | |
tree | 4502228eb7b87a760e0b0e67aded9d1d870d0bed /ThirdParty | |
parent | Added copyright heaaders. Minor cleanup. (diff) | |
download | opensim-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 '')
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs | 1101 | ||||
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/TcpClient.cs | 240 | ||||
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/TcpServer.cs | 219 | ||||
-rw-r--r-- | ThirdParty/3Di/README.txt | 82 | ||||
-rw-r--r-- | ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MonitorGUI/View.pm | 214 | ||||
-rw-r--r-- | ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/MyCGI.pm | 91 | ||||
-rw-r--r-- | ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs/monitor.cgi | 202 | ||||
-rw-r--r-- | ThirdParty/3Di/RegionMonitor/ServerPlugin/RegionMonitorPlugin.cs | 129 | ||||
-rw-r--r-- | ThirdParty/3Di/RegionProxy/RegionProxyPlugin.cs | 513 |
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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Xml; | ||
33 | using System.Text; | ||
34 | using System.Xml.Serialization; | ||
35 | using System.Net.Sockets; | ||
36 | using System.Collections; | ||
37 | using System.Collections.Generic; | ||
38 | using System.Diagnostics; | ||
39 | using System.Threading; | ||
40 | |||
41 | using OpenSim.Framework; | ||
42 | using OpenSim.Framework.Console; | ||
43 | using OpenSim.Framework.Servers; | ||
44 | using OpenSim.Region.Environment; | ||
45 | using OpenSim.Region.Environment.Scenes; | ||
46 | using OpenSim.Region.ClientStack; | ||
47 | |||
48 | using Nwc.XmlRpc; | ||
49 | using Nini.Config; | ||
50 | |||
51 | using Mono.Addins; | ||
52 | |||
53 | using libsecondlife; | ||
54 | using libsecondlife.Packets; | ||
55 | |||
56 | [assembly:Addin] | ||
57 | [assembly:AddinDependency ("OpenSim", "0.5")] | ||
58 | |||
59 | namespace 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(); | ||
751 | presences.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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Net.Sockets; | ||
33 | using System.Threading; | ||
34 | using System.Text; | ||
35 | using System.Runtime.Serialization.Formatters.Binary; | ||
36 | |||
37 | namespace 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 | |||
126 | public 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 | /* | ||
230 | for (int i = 0; i < 10; i++) | ||
231 | { | ||
232 | Console.Write(packet[i] + " "); | ||
233 | } | ||
234 | Console.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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Net.Sockets; | ||
33 | using System.Text; | ||
34 | using System.Threading; | ||
35 | using System.Runtime.Serialization.Formatters.Binary; | ||
36 | |||
37 | using OpenSim.Framework.Console; | ||
38 | |||
39 | namespace 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 @@ | |||
1 | INTRODUCTION | ||
2 | |||
3 | This folder contains code that implement: | ||
4 | |||
5 | 1. Dynamic load balancing | ||
6 | |||
7 | OpenSim is allowing many regions to share a region server, but the optimal | ||
8 | number of regions on each server depends on the load of each region, something | ||
9 | which may change as time goes on. 3Di is working on a load balancer that | ||
10 | allows the current load to be monitored and regions to be reassigned without | ||
11 | requiring the servers to be restarted. To move a region, its state is | ||
12 | serialized, and a new clone is created on the target server using this | ||
13 | stream. The old region is then destroyed and the client viewer updated to use | ||
14 | the new region address. | ||
15 | |||
16 | 2. Region splitting | ||
17 | |||
18 | Currently each region can hold only a small number of avatars. To allow more | ||
19 | avatars in each region, 3Di has implemented region splitting, in which several | ||
20 | copies of a given region can be distributed across the region servers. Each | ||
21 | sub-region updates a fraction of the avatars, and sends state updates to the | ||
22 | other sub-regions. | ||
23 | |||
24 | IMPLEMENTATION | ||
25 | |||
26 | The code is organised as follows: | ||
27 | |||
28 | * LoadBalancer: communicates with other region servers and creates/destroys | ||
29 | regions on command | ||
30 | * RegionMonitor/MonitorGUI: provides a browser GUI, showing the state of the | ||
31 | grid, and provides buttons for controlling region movement, splitting, and | ||
32 | merging. | ||
33 | * RegionMonitor/ServerPlugin: this is a region server plugin which | ||
34 | communicates with the load balancer GUI to provide information | ||
35 | on 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 | |||
38 | USAGE | ||
39 | |||
40 | In order to use these additions the following lines have to be added to | ||
41 | OpenSim.ini: | ||
42 | |||
43 | proxy_offset = -1000 | ||
44 | proxy_url = http://10.8.1.50:9001 | ||
45 | serialize_dir = /mnt/temp/ | ||
46 | |||
47 | If defined, proxy_offset defines how to calculate the true region port, e.g. | ||
48 | if the XML defines the port as 9000 the actual port is 8000 if proxy_offset | ||
49 | is -1000. The RegionProxy module will open a port at 9000 which the clients | ||
50 | can connect to, and route all traffic from there to port 8000. This allows | ||
51 | the region proxy to run on region server together with regions without | ||
52 | blocking them by using the same port number. | ||
53 | |||
54 | The proxy location is defined in proxy_url. When splitting, the region state | ||
55 | is stored on a file in the folder specified in serialize_dir. This has to be | ||
56 | a shared folder which both region servers involved in the split have access to. | ||
57 | |||
58 | 3. Monitor GUI | ||
59 | |||
60 | RegionMonitor/MonitorGUI is used to view status of all the managed Region | ||
61 | servers, and send "Move", "Split", "Merge" commands to a specified Regions | ||
62 | server. | ||
63 | |||
64 | MonitorGUI is a web-based application. You can access it through a web browser. | ||
65 | Its back-end is written in perl. (CGI script) | ||
66 | |||
67 | Pre-requierments (CentOS, Fedora) | ||
68 | |||
69 | RPM package "perl-XML-RPC" and relevant packages. | ||
70 | |||
71 | Installation | ||
72 | |||
73 | 1. Install Apache | ||
74 | 2. copy all the files undef "ThirdParty/3Di/RegionMonitor/MonitorGUI/htdocs" to | ||
75 | "$APACHE_ROOT/htdocs" | ||
76 | 3. 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 @@ | |||
1 | package MonitorGUI::View; | ||
2 | |||
3 | use strict; | ||
4 | |||
5 | my @server_list; | ||
6 | my $max_port; | ||
7 | my $regions; | ||
8 | |||
9 | sub screen_header { | ||
10 | return << "HEADER"; | ||
11 | <HTML> | ||
12 | <HEAD> | ||
13 | <STYLE TYPE="text/css"> | ||
14 | <!-- | ||
15 | a:link {font-size: 12pt; text-decoration:none; color:#0000ff ;} | ||
16 | a:visited {font-size: 12pt; text-decoration:none; color:#ff0000 ;} | ||
17 | a:active {font-size: 12pt; text-decoration:none; color:#00ff00 ;} | ||
18 | a:hover {font-size: 12pt; text-decoration:underline; color:#ff00ff ;} | ||
19 | td {font-size: 12pt;border:0;} | ||
20 | th {background-color:#000000; font-size: 12pt;border:0; color:#FFFFFF; } | ||
21 | tr {background-color:#FFFFFF; } | ||
22 | b {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> | ||
31 | HEADER | ||
32 | } | ||
33 | |||
34 | sub screen_footer { | ||
35 | return << "FOOTER"; | ||
36 | </BODY> | ||
37 | </HTML> | ||
38 | FOOTER | ||
39 | } | ||
40 | |||
41 | sub 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 | |||
63 | sub _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 /> | ||
73 | MACHINE_HTML | ||
74 | } | ||
75 | |||
76 | sub _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 | ||
115 | REGION_HTML | ||
116 | return $html; | ||
117 | } | ||
118 | |||
119 | sub _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 | | | ||
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 | | | ||
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> | ||
211 | ACTION_FORMS | ||
212 | } | ||
213 | |||
214 | 1; | ||
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 @@ | |||
1 | package MyCGI; | ||
2 | |||
3 | use strict; | ||
4 | use CGI; | ||
5 | |||
6 | sub 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 | |||
21 | sub getCookie { | ||
22 | my $name = shift; | ||
23 | my $cookie_value = &CGI::cookie($name); | ||
24 | return &_parse($cookie_value); | ||
25 | } | ||
26 | |||
27 | sub outputHtml { | ||
28 | my ($charset, $html) = @_; | ||
29 | print &CGI::header(-charset => $charset); | ||
30 | print $html; | ||
31 | } | ||
32 | |||
33 | sub outputXml { | ||
34 | my ($charset, $xml) = @_; | ||
35 | print &CGI::header( -type => 'text/xml', -charset => $charset ); | ||
36 | print $xml; | ||
37 | } | ||
38 | |||
39 | sub makeCookieValue { | ||
40 | my $param = shift; | ||
41 | my @data = (); | ||
42 | foreach(keys %$param) { | ||
43 | push(@data, $_ . "=" . $param->{$_}); | ||
44 | } | ||
45 | return join("&", @data); | ||
46 | } | ||
47 | |||
48 | sub 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 | |||
60 | sub redirect { | ||
61 | my $dest = shift; | ||
62 | &CGI::redirect($dest); | ||
63 | } | ||
64 | |||
65 | sub urlEncode { | ||
66 | my $str = shift; | ||
67 | $str =~ s/([^\w ])/'%'.unpack('H2', $1)/eg; | ||
68 | $str =~ tr/ /+/; | ||
69 | return $str; | ||
70 | } | ||
71 | |||
72 | sub 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 | |||
79 | sub _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 | |||
90 | 1; | ||
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 | |||
3 | use strict; | ||
4 | use Carp; | ||
5 | use MyCGI; | ||
6 | use XML::RPC; | ||
7 | use MonitorGUI::View; | ||
8 | |||
9 | use 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 | |||
14 | my %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 | ||
26 | my $param = &MyCGI::getParam; | ||
27 | my $act = $param->{A} || "default"; | ||
28 | my $contents = ""; | ||
29 | if (!$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 | ||
42 | sub 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 | |||
66 | sub 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 | |||
88 | sub 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 | ||
111 | sub 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 | |||
148 | sub gui_error { | ||
149 | my $msg = shift; | ||
150 | &MyCGI::outputHtml("UTF-8", "<h1>ERROR</h1><hr />$msg"); | ||
151 | } | ||
152 | |||
153 | sub 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 | |||
163 | sub 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> | ||
179 | wait <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> | ||
183 | HTML | ||
184 | &MyCGI::outputHtml("UTF-8", $html); | ||
185 | } | ||
186 | |||
187 | # ################## | ||
188 | # Utility | ||
189 | sub 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 | |||
196 | sub 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 | */ | ||
28 | using System; | ||
29 | using System.Runtime.Remoting; | ||
30 | using System.Threading; | ||
31 | using Mono.Addins; | ||
32 | using OpenSim; | ||
33 | using OpenSim.Framework.Console; | ||
34 | using MonitorLib; | ||
35 | |||
36 | [assembly:Addin] | ||
37 | [assembly:AddinDependency ("OpenSim", "0.5")] | ||
38 | |||
39 | namespace 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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Xml; | ||
33 | using System.Text; | ||
34 | using System.Xml.Serialization; | ||
35 | using System.Net.Sockets; | ||
36 | using System.Collections; | ||
37 | using System.Collections.Generic; | ||
38 | using System.Diagnostics; | ||
39 | |||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Framework.Servers; | ||
42 | using OpenSim.Framework.Console; | ||
43 | using Nwc.XmlRpc; | ||
44 | |||
45 | using Mono.Addins; | ||
46 | |||
47 | [assembly:Addin] | ||
48 | [assembly:AddinDependency ("OpenSim", "0.5")] | ||
49 | |||
50 | namespace 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 | } | ||