diff options
author | Mike Mazur | 2009-01-28 01:56:04 +0000 |
---|---|---|
committer | Mike Mazur | 2009-01-28 01:56:04 +0000 |
commit | fefe0ff3d9ceaaefccb494ef13a5a98cc0131070 (patch) | |
tree | 01c5057fb9f23c55f07ff102906f5f14d6453118 /ThirdParty/3Di/LoadBalancer | |
parent | Slight cleanup of docs, removing trailing whitespace. (diff) | |
download | opensim-SC_OLD-fefe0ff3d9ceaaefccb494ef13a5a98cc0131070.zip opensim-SC_OLD-fefe0ff3d9ceaaefccb494ef13a5a98cc0131070.tar.gz opensim-SC_OLD-fefe0ff3d9ceaaefccb494ef13a5a98cc0131070.tar.bz2 opensim-SC_OLD-fefe0ff3d9ceaaefccb494ef13a5a98cc0131070.tar.xz |
Removing contents of ThirdParty/3Di. The load balancer can now be found
at http://forge.opensimulator.org/gf/project/loadbalancer/
config key: svn.rmdir
Diffstat (limited to 'ThirdParty/3Di/LoadBalancer')
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs | 1094 | ||||
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/Resources/LoadBalancer.addin.xml | 12 | ||||
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/TcpClient.cs | 225 | ||||
-rw-r--r-- | ThirdParty/3Di/LoadBalancer/TcpServer.cs | 219 |
4 files changed, 0 insertions, 1550 deletions
diff --git a/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs b/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs deleted file mode 100644 index b3b66da..0000000 --- a/ThirdParty/3Di/LoadBalancer/LoadBalancerPlugin.cs +++ /dev/null | |||
@@ -1,1094 +0,0 @@ | |||
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.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Net; | ||
33 | using System.Reflection; | ||
34 | using System.Threading; | ||
35 | using OpenMetaverse; | ||
36 | using OpenMetaverse.Packets; | ||
37 | using log4net; | ||
38 | using Mono.Addins; | ||
39 | using Nwc.XmlRpc; | ||
40 | using OpenSim.Framework; | ||
41 | using OpenSim.Framework.Servers; | ||
42 | using OpenSim.Region.ClientStack; | ||
43 | using OpenSim.Region.ClientStack.LindenUDP; | ||
44 | using OpenSim.Region.Environment.Scenes; | ||
45 | |||
46 | // TODO: remove LindenUDP dependency | ||
47 | |||
48 | namespace OpenSim.ApplicationPlugins.LoadBalancer | ||
49 | { | ||
50 | public class LoadBalancerPlugin : IApplicationPlugin | ||
51 | { | ||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | |||
54 | private BaseHttpServer commandServer; | ||
55 | private bool[] isLocalNeighbour; | ||
56 | private bool isSplit; | ||
57 | private TcpServer mTcpServer; | ||
58 | private readonly object padlock = new object(); | ||
59 | |||
60 | private int proxyOffset; | ||
61 | private string proxyURL; | ||
62 | private List<RegionInfo> regionData; | ||
63 | private int[] regionPortList; | ||
64 | private SceneManager sceneManager; | ||
65 | private string[] sceneURL; | ||
66 | private string serializeDir; | ||
67 | private OpenSimBase simMain; | ||
68 | private TcpClient[] tcpClientList; | ||
69 | private List<IClientNetworkServer> m_clientServers; | ||
70 | |||
71 | #region IApplicationPlugin Members | ||
72 | // TODO: required by IPlugin, but likely not at all right | ||
73 | string m_name = "LoadBalancer"; | ||
74 | string m_version = "0.1"; | ||
75 | |||
76 | public string Version { get { return m_version; } } | ||
77 | public string Name { get { return m_name; } } | ||
78 | |||
79 | public void Initialise() | ||
80 | { | ||
81 | m_log.Info("[BALANCER]: " + Name + " cannot be default-initialized!"); | ||
82 | throw new PluginNotInitialisedException (Name); | ||
83 | } | ||
84 | |||
85 | public void Initialise(OpenSimBase openSim) | ||
86 | { | ||
87 | m_log.Info("[BALANCER]: " + "Entering Initialize()"); | ||
88 | |||
89 | proxyURL = openSim.ConfigSource.Source.Configs["Network"].GetString("proxy_url", ""); | ||
90 | if (proxyURL.Length == 0) return; | ||
91 | |||
92 | StartTcpServer(); | ||
93 | |||
94 | LLClientView.SynchronizeClient = SynchronizePackets; | ||
95 | AsynchronousSocketListener.PacketHandler = SynchronizePacketRecieve; | ||
96 | |||
97 | sceneManager = openSim.SceneManager; | ||
98 | m_clientServers = openSim.ClientServers; | ||
99 | regionData = openSim.RegionData; | ||
100 | simMain = openSim; | ||
101 | commandServer = openSim.HttpServer; | ||
102 | |||
103 | proxyOffset = Int32.Parse(openSim.ConfigSource.Source.Configs["Network"].GetString("proxy_offset", "0")); | ||
104 | serializeDir = openSim.ConfigSource.Source.Configs["Network"].GetString("serialize_dir", "/tmp/"); | ||
105 | |||
106 | commandServer.AddXmlRPCHandler("SerializeRegion", SerializeRegion); | ||
107 | commandServer.AddXmlRPCHandler("DeserializeRegion_Move", DeserializeRegion_Move); | ||
108 | commandServer.AddXmlRPCHandler("DeserializeRegion_Clone", DeserializeRegion_Clone); | ||
109 | commandServer.AddXmlRPCHandler("TerminateRegion", TerminateRegion); | ||
110 | |||
111 | commandServer.AddXmlRPCHandler("SplitRegion", SplitRegion); | ||
112 | commandServer.AddXmlRPCHandler("MergeRegions", MergeRegions); | ||
113 | commandServer.AddXmlRPCHandler("UpdatePhysics", UpdatePhysics); | ||
114 | commandServer.AddXmlRPCHandler("GetStatus", GetStatus); | ||
115 | |||
116 | m_log.Info("[BALANCER] " + "Exiting Initialize()"); | ||
117 | } | ||
118 | |||
119 | public void Dispose() | ||
120 | { | ||
121 | } | ||
122 | |||
123 | #endregion | ||
124 | |||
125 | private void StartTcpServer() | ||
126 | { | ||
127 | Thread server_thread = new Thread(new ThreadStart( | ||
128 | delegate | ||
129 | { | ||
130 | mTcpServer = new TcpServer(10001); | ||
131 | mTcpServer.start(); | ||
132 | })); | ||
133 | server_thread.Start(); | ||
134 | } | ||
135 | |||
136 | private XmlRpcResponse GetStatus(XmlRpcRequest request) | ||
137 | { | ||
138 | XmlRpcResponse response = new XmlRpcResponse(); | ||
139 | try | ||
140 | { | ||
141 | m_log.Info("[BALANCER] " + "Entering RegionStatus()"); | ||
142 | |||
143 | int src_port = Convert.ToInt32(request.Params[0]); | ||
144 | Scene scene; | ||
145 | // try to get the scene object | ||
146 | RegionInfo src_region = SearchRegionFromPortNum(src_port); | ||
147 | if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false) | ||
148 | { | ||
149 | m_log.Error("[BALANCER] " + "The Scene is not found"); | ||
150 | return response; | ||
151 | } | ||
152 | // serialization of client's informations | ||
153 | List<ScenePresence> presences = scene.GetScenePresences(); | ||
154 | int get_scene_presence = presences.Count; | ||
155 | int get_scene_presence_filter = 0; | ||
156 | foreach (ScenePresence pre in presences) | ||
157 | { | ||
158 | IClientAPI client = pre.ControllingClient; | ||
159 | //if (pre.MovementFlag!=0 && client.PacketProcessingEnabled==true) | ||
160 | if (client.IsActive) | ||
161 | { | ||
162 | get_scene_presence_filter++; | ||
163 | } | ||
164 | } | ||
165 | List<ScenePresence> avatars = scene.GetAvatars(); | ||
166 | int get_avatar = avatars.Count; | ||
167 | int get_avatar_filter = 0; | ||
168 | string avatar_names = ""; | ||
169 | foreach (ScenePresence pre in avatars) | ||
170 | { | ||
171 | IClientAPI client = pre.ControllingClient; | ||
172 | //if (pre.MovementFlag!=0 && client.PacketProcessingEnabled==true) | ||
173 | if (client.IsActive) | ||
174 | { | ||
175 | get_avatar_filter++; | ||
176 | avatar_names += pre.Firstname + " " + pre.Lastname + "; "; | ||
177 | } | ||
178 | } | ||
179 | |||
180 | Hashtable responseData = new Hashtable(); | ||
181 | responseData["get_scene_presence_filter"] = get_scene_presence_filter; | ||
182 | responseData["get_scene_presence"] = get_scene_presence; | ||
183 | responseData["get_avatar_filter"] = get_avatar_filter; | ||
184 | responseData["get_avatar"] = get_avatar; | ||
185 | responseData["avatar_names"] = avatar_names; | ||
186 | response.Value = responseData; | ||
187 | |||
188 | m_log.Info("[BALANCER] " + "Exiting RegionStatus()"); | ||
189 | } | ||
190 | catch (Exception e) | ||
191 | { | ||
192 | m_log.Error("[BALANCER] " + e); | ||
193 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
194 | } | ||
195 | return response; | ||
196 | } | ||
197 | |||
198 | private XmlRpcResponse SerializeRegion(XmlRpcRequest request) | ||
199 | { | ||
200 | try | ||
201 | { | ||
202 | m_log.Info("[BALANCER] " + "Entering SerializeRegion()"); | ||
203 | |||
204 | string src_url = (string) request.Params[0]; | ||
205 | int src_port = (int) request.Params[1]; | ||
206 | |||
207 | SerializeRegion(src_url, src_port); | ||
208 | |||
209 | m_log.Info("[BALANCER] " + "Exiting SerializeRegion()"); | ||
210 | } | ||
211 | catch (Exception e) | ||
212 | { | ||
213 | m_log.Error("[BALANCER] " + e); | ||
214 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
215 | } | ||
216 | |||
217 | return new XmlRpcResponse(); | ||
218 | } | ||
219 | |||
220 | private XmlRpcResponse DeserializeRegion_Move(XmlRpcRequest request) | ||
221 | { | ||
222 | try | ||
223 | { | ||
224 | m_log.Info("[BALANCER] " + "Entering DeserializeRegion_Move()"); | ||
225 | |||
226 | string src_url = (string) request.Params[0]; | ||
227 | int src_port = (int) request.Params[1]; | ||
228 | string dst_url = (string) request.Params[2]; | ||
229 | int dst_port = (int) request.Params[3]; | ||
230 | |||
231 | DeserializeRegion_Move(src_port, dst_port, src_url, dst_url); | ||
232 | |||
233 | m_log.Info("[BALANCER] " + "Exiting DeserializeRegion_Move()"); | ||
234 | } | ||
235 | catch (Exception e) | ||
236 | { | ||
237 | m_log.Error("[BALANCER] " + e); | ||
238 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
239 | } | ||
240 | |||
241 | return new XmlRpcResponse(); | ||
242 | } | ||
243 | |||
244 | private XmlRpcResponse DeserializeRegion_Clone(XmlRpcRequest request) | ||
245 | { | ||
246 | try | ||
247 | { | ||
248 | m_log.Info("[BALANCER] " + "Entering DeserializeRegion_Clone()"); | ||
249 | |||
250 | string src_url = (string) request.Params[0]; | ||
251 | int src_port = (int) request.Params[1]; | ||
252 | string dst_url = (string) request.Params[2]; | ||
253 | int dst_port = (int) request.Params[3]; | ||
254 | |||
255 | DeserializeRegion_Clone(src_port, dst_port, src_url, dst_url); | ||
256 | |||
257 | m_log.Info("[BALANCER] " + "Exiting DeserializeRegion_Clone()"); | ||
258 | } | ||
259 | catch (Exception e) | ||
260 | { | ||
261 | m_log.Error("[BALANCER] " + e); | ||
262 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
263 | throw; | ||
264 | } | ||
265 | |||
266 | return new XmlRpcResponse(); | ||
267 | } | ||
268 | |||
269 | private XmlRpcResponse TerminateRegion(XmlRpcRequest request) | ||
270 | { | ||
271 | try | ||
272 | { | ||
273 | m_log.Info("[BALANCER] " + "Entering TerminateRegion()"); | ||
274 | |||
275 | int src_port = (int) request.Params[0]; | ||
276 | |||
277 | // backgroud | ||
278 | WaitCallback callback = TerminateRegion; | ||
279 | ThreadPool.QueueUserWorkItem(callback, src_port); | ||
280 | |||
281 | m_log.Info("[BALANCER] " + "Exiting TerminateRegion()"); | ||
282 | } | ||
283 | catch (Exception e) | ||
284 | { | ||
285 | m_log.Error("[BALANCER] " + e); | ||
286 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
287 | } | ||
288 | |||
289 | return new XmlRpcResponse(); | ||
290 | } | ||
291 | |||
292 | // internal functions | ||
293 | |||
294 | private void SerializeRegion(string src_url, int src_port) | ||
295 | { | ||
296 | //------------------------------------------ | ||
297 | // Processing of origin region | ||
298 | //------------------------------------------ | ||
299 | |||
300 | // search origin region | ||
301 | RegionInfo src_region = SearchRegionFromPortNum(src_port); | ||
302 | |||
303 | if (src_region == null) | ||
304 | { | ||
305 | m_log.Error("[BALANCER] " + "Region not found"); | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | Util.XmlRpcCommand(src_region.proxyUrl, "BlockClientMessages", src_url, src_port + proxyOffset); | ||
310 | |||
311 | // serialization of origin region's data | ||
312 | SerializeRegion(src_region, serializeDir); | ||
313 | } | ||
314 | |||
315 | private void DeserializeRegion_Move(int src_port, int dst_port, string src_url, string dst_url) | ||
316 | { | ||
317 | //------------------------------------------ | ||
318 | // Processing of destination region | ||
319 | //------------------------------------------ | ||
320 | |||
321 | // import the source region's data | ||
322 | RegionInfo dst_region = DeserializeRegion(dst_port, serializeDir); | ||
323 | |||
324 | Util.XmlRpcCommand(dst_region.proxyUrl, "ChangeRegion", src_port + proxyOffset, src_url, dst_port + proxyOffset, dst_url); | ||
325 | Util.XmlRpcCommand(dst_region.proxyUrl, "UnblockClientMessages", dst_url, dst_port + proxyOffset); | ||
326 | } | ||
327 | |||
328 | private void DeserializeRegion_Clone(int src_port, int dst_port, string src_url, string dst_url) | ||
329 | { | ||
330 | //------------------------------------------ | ||
331 | // Processing of destination region | ||
332 | //------------------------------------------ | ||
333 | |||
334 | // import the source region's data | ||
335 | RegionInfo dst_region = DeserializeRegion(dst_port, serializeDir); | ||
336 | |||
337 | // Decide who is in charge for each section | ||
338 | int[] port = new int[] {src_port, dst_port}; | ||
339 | string[] url = new string[] {"http://" + src_url + ":" + commandServer.Port, "http://" + dst_url + ":" + commandServer.Port}; | ||
340 | for (int i = 0; i < 2; i++) Util.XmlRpcCommand(url[i], "SplitRegion", i, 2, port[0], port[1], url[0], url[1]); | ||
341 | |||
342 | // Enable the proxy | ||
343 | Util.XmlRpcCommand(dst_region.proxyUrl, "AddRegion", src_port + proxyOffset, src_url, dst_port + proxyOffset, dst_url); | ||
344 | Util.XmlRpcCommand(dst_region.proxyUrl, "UnblockClientMessages", dst_url, dst_port + proxyOffset); | ||
345 | } | ||
346 | |||
347 | private void TerminateRegion(object param) | ||
348 | { | ||
349 | int src_port = (int) param; | ||
350 | |||
351 | //------------------------------------------ | ||
352 | // Processing of remove region | ||
353 | //------------------------------------------ | ||
354 | |||
355 | // search origin region | ||
356 | RegionInfo src_region = SearchRegionFromPortNum(src_port); | ||
357 | |||
358 | if (src_region == null) | ||
359 | { | ||
360 | m_log.Error("[BALANCER] " + "Region not found"); | ||
361 | return; | ||
362 | } | ||
363 | |||
364 | isSplit = false; | ||
365 | |||
366 | // remove client resources | ||
367 | RemoveAllClientResource(src_region); | ||
368 | // remove old region | ||
369 | RemoveRegion(src_region.RegionID, src_region.InternalEndPoint.Port); | ||
370 | |||
371 | m_log.Info("[BALANCER] " + "Region terminated"); | ||
372 | } | ||
373 | |||
374 | private RegionInfo SearchRegionFromPortNum(int portnum) | ||
375 | { | ||
376 | RegionInfo result = null; | ||
377 | |||
378 | foreach (RegionInfo rinfo in regionData) | ||
379 | { | ||
380 | if (rinfo.InternalEndPoint.Port == portnum) | ||
381 | { | ||
382 | // m_log.Info("BALANCER", | ||
383 | // "Region found. Internal Port = {0}, Handle={1}", | ||
384 | // rinfo.InternalEndPoint.Port, rinfo.RegionHandle); | ||
385 | result = rinfo; | ||
386 | break; | ||
387 | } | ||
388 | } | ||
389 | |||
390 | return result; | ||
391 | } | ||
392 | |||
393 | private IClientNetworkServer SearchClientServerFromPortNum(int portnum) | ||
394 | { | ||
395 | return m_clientServers.Find( | ||
396 | delegate(IClientNetworkServer server) | ||
397 | { | ||
398 | // ReSharper disable PossibleNullReferenceException | ||
399 | return (portnum + proxyOffset == ((IPEndPoint) server.Server.LocalEndPoint).Port); | ||
400 | // ReSharper restore PossibleNullReferenceException | ||
401 | } | ||
402 | ); | ||
403 | } | ||
404 | |||
405 | private void SerializeRegion(RegionInfo src_region, string export_dir) | ||
406 | { | ||
407 | Scene scene; | ||
408 | int i = 0; | ||
409 | |||
410 | // try to get the scene object | ||
411 | if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false) | ||
412 | { | ||
413 | m_log.Error("[BALANCER] " + "The Scene is not found"); | ||
414 | return; | ||
415 | } | ||
416 | |||
417 | // create export directory | ||
418 | DirectoryInfo dirinfo = new DirectoryInfo(export_dir); | ||
419 | if (!dirinfo.Exists) | ||
420 | { | ||
421 | dirinfo.Create(); | ||
422 | } | ||
423 | |||
424 | // serialization of client's informations | ||
425 | List<ScenePresence> presences = scene.GetScenePresences(); | ||
426 | |||
427 | foreach (ScenePresence pre in presences) | ||
428 | { | ||
429 | SerializeClient(i, scene, pre, export_dir); | ||
430 | i++; | ||
431 | } | ||
432 | |||
433 | // serialization of region data | ||
434 | SerializableRegionInfo dst_region = new SerializableRegionInfo(src_region); | ||
435 | |||
436 | string filename = export_dir + "RegionInfo_" + src_region.RegionID + ".bin"; | ||
437 | Util.SerializeToFile(filename, dst_region); | ||
438 | |||
439 | // backup current scene's entities | ||
440 | //scene.Backup(); | ||
441 | |||
442 | m_log.InfoFormat("[BALANCER] " + "region serialization completed [{0}]", | ||
443 | src_region.RegionID.ToString()); | ||
444 | } | ||
445 | |||
446 | private static void SerializeClient(int idx, IScene scene, EntityBase pre, string export_dir) | ||
447 | { | ||
448 | string filename; | ||
449 | IClientAPI controller; | ||
450 | |||
451 | m_log.InfoFormat("[BALANCER] " + "agent id : {0}", pre.UUID); | ||
452 | |||
453 | uint[] circuits = scene.ClientManager.GetAllCircuits(pre.UUID); | ||
454 | |||
455 | foreach (uint code in circuits) | ||
456 | { | ||
457 | m_log.InfoFormat("[BALANCER] " + "circuit code : {0}", code); | ||
458 | |||
459 | if (scene.ClientManager.TryGetClient(code, out controller)) | ||
460 | { | ||
461 | ClientInfo info = controller.GetClientInfo(); | ||
462 | |||
463 | filename = export_dir + "ClientInfo-" + String.Format("{0:0000}", idx) + "_" + controller.CircuitCode + ".bin"; | ||
464 | |||
465 | Util.SerializeToFile(filename, info); | ||
466 | |||
467 | m_log.InfoFormat("[BALANCER] " + "client info serialized [filename={0}]", filename); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | //filename = export_dir + "Presence_" + controller.AgentId.ToString() + ".bin"; | ||
472 | filename = export_dir + "Presence_" + String.Format("{0:0000}", idx) + ".bin"; | ||
473 | |||
474 | Util.SerializeToFile(filename, pre); | ||
475 | |||
476 | m_log.InfoFormat("[BALANCER] " + "scene presence serialized [filename={0}]", filename); | ||
477 | } | ||
478 | |||
479 | private RegionInfo DeserializeRegion(int dst_port, string import_dir) | ||
480 | { | ||
481 | RegionInfo dst_region = null; | ||
482 | |||
483 | try | ||
484 | { | ||
485 | // deserialization of region data | ||
486 | string[] files = Directory.GetFiles(import_dir, "RegionInfo_*.bin"); | ||
487 | |||
488 | foreach (string filename in files) | ||
489 | { | ||
490 | m_log.InfoFormat("[BALANCER] RegionInfo filename = [{0}]", filename); | ||
491 | |||
492 | dst_region = new RegionInfo((SerializableRegionInfo) Util.DeserializeFromFile(filename)); | ||
493 | |||
494 | m_log.InfoFormat("[BALANCER] " + "RegionID = [{0}]", dst_region.RegionID.ToString()); | ||
495 | m_log.InfoFormat("[BALANCER] " + "RegionHandle = [{0}]", dst_region.RegionHandle); | ||
496 | m_log.InfoFormat("[BALANCER] " + "ProxyUrl = [{0}]", dst_region.proxyUrl); | ||
497 | m_log.InfoFormat("[BALANCER] " + "OriginRegionID = [{0}]", dst_region.originRegionID.ToString()); | ||
498 | |||
499 | CreateCloneRegion(dst_region, dst_port, true); | ||
500 | |||
501 | File.Delete(filename); | ||
502 | |||
503 | m_log.InfoFormat("[BALANCER] " + "region deserialized [{0}]", dst_region.RegionID); | ||
504 | } | ||
505 | |||
506 | if (dst_region != null) | ||
507 | { | ||
508 | // deserialization of client data | ||
509 | DeserializeClient(dst_region, import_dir); | ||
510 | |||
511 | m_log.InfoFormat("[BALANCER] " + "region deserialization completed [{0}]", | ||
512 | dst_region.ToString()); | ||
513 | } | ||
514 | } | ||
515 | catch (Exception e) | ||
516 | { | ||
517 | m_log.Error("[BALANCER] " + e); | ||
518 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
519 | throw; | ||
520 | } | ||
521 | |||
522 | return dst_region; | ||
523 | } | ||
524 | |||
525 | private void DeserializeClient(SimpleRegionInfo dst_region, string import_dir) | ||
526 | { | ||
527 | ScenePresence sp; | ||
528 | ClientInfo data; | ||
529 | Scene scene; | ||
530 | IClientAPI controller; | ||
531 | |||
532 | if (sceneManager.TryGetScene(dst_region.RegionID, out scene)) | ||
533 | { | ||
534 | IClientNetworkServer clientserv = SearchClientServerFromPortNum(scene.RegionInfo.InternalEndPoint.Port); | ||
535 | |||
536 | // restore the scene presence | ||
537 | for (int i = 0;; i++) | ||
538 | { | ||
539 | string filename = import_dir + "Presence_" + String.Format("{0:0000}", i) + ".bin"; | ||
540 | |||
541 | if (!File.Exists(filename)) | ||
542 | { | ||
543 | break; | ||
544 | } | ||
545 | |||
546 | sp = (ScenePresence) Util.DeserializeFromFile(filename); | ||
547 | Console.WriteLine("agent id = {0}", sp.UUID); | ||
548 | |||
549 | scene.m_restorePresences.Add(sp.UUID, sp); | ||
550 | File.Delete(filename); | ||
551 | |||
552 | m_log.InfoFormat("[BALANCER] " + "scene presence deserialized [{0}]", sp.UUID); | ||
553 | |||
554 | // restore the ClientView | ||
555 | |||
556 | string[] files = Directory.GetFiles(import_dir, "ClientInfo-" + String.Format("{0:0000}", i) + "_*.bin"); | ||
557 | |||
558 | foreach (string fname in files) | ||
559 | { | ||
560 | int start = fname.IndexOf('_'); | ||
561 | int end = fname.LastIndexOf('.'); | ||
562 | uint circuit_code = uint.Parse(fname.Substring(start + 1, end - start - 1)); | ||
563 | m_log.InfoFormat("[BALANCER] " + "client circuit code = {0}", circuit_code); | ||
564 | |||
565 | data = (ClientInfo) Util.DeserializeFromFile(fname); | ||
566 | |||
567 | AgentCircuitData agentdata = new AgentCircuitData(data.agentcircuit); | ||
568 | scene.AuthenticateHandler.AddNewCircuit(circuit_code, agentdata); | ||
569 | |||
570 | // TODO: This needs to be abstracted and converted into IClientNetworkServer | ||
571 | if (clientserv is LLUDPServer) | ||
572 | { | ||
573 | ((LLUDPServer) clientserv).RestoreClient(agentdata, data.userEP, data.proxyEP); | ||
574 | } | ||
575 | |||
576 | // waiting for the scene-presense restored | ||
577 | lock (scene.m_restorePresences) | ||
578 | { | ||
579 | Monitor.Wait(scene.m_restorePresences, 3000); | ||
580 | } | ||
581 | |||
582 | if (scene.ClientManager.TryGetClient(circuit_code, out controller)) | ||
583 | { | ||
584 | m_log.InfoFormat("[BALANCER] " + "get client [{0}]", circuit_code); | ||
585 | controller.SetClientInfo(data); | ||
586 | } | ||
587 | |||
588 | File.Delete(fname); | ||
589 | |||
590 | m_log.InfoFormat("[BALANCER] " + "client info deserialized [{0}]", circuit_code); | ||
591 | } | ||
592 | |||
593 | // backup new scene's entities | ||
594 | //scene.Backup(); | ||
595 | } | ||
596 | } | ||
597 | } | ||
598 | |||
599 | private void CreateCloneRegion(RegionInfo dst_region, int dst_port, bool createID_flag) | ||
600 | { | ||
601 | if (createID_flag) | ||
602 | { | ||
603 | dst_region.RegionID = UUID.Random(); | ||
604 | } | ||
605 | |||
606 | // change RegionInfo (memory only) | ||
607 | dst_region.InternalEndPoint.Port = dst_port; | ||
608 | dst_region.ExternalHostName = proxyURL.Split(new char[] {'/', ':'})[3]; | ||
609 | |||
610 | // Create new region | ||
611 | simMain.CreateRegion(dst_region, false); | ||
612 | } | ||
613 | |||
614 | private void RemoveRegion(UUID regionID, int port) | ||
615 | { | ||
616 | Scene killScene; | ||
617 | if (sceneManager.TryGetScene(regionID, out killScene)) | ||
618 | { | ||
619 | Console.WriteLine("scene found."); | ||
620 | |||
621 | if ((sceneManager.CurrentScene != null) | ||
622 | && (sceneManager.CurrentScene.RegionInfo.RegionID == killScene.RegionInfo.RegionID)) | ||
623 | { | ||
624 | sceneManager.TrySetCurrentScene(".."); | ||
625 | } | ||
626 | |||
627 | m_log.Info("Removing region : " + killScene.RegionInfo.RegionName); | ||
628 | regionData.Remove(killScene.RegionInfo); | ||
629 | sceneManager.CloseScene(killScene); | ||
630 | } | ||
631 | |||
632 | // Shutting down the UDP server | ||
633 | IClientNetworkServer clientsvr = SearchClientServerFromPortNum(port); | ||
634 | |||
635 | if (clientsvr != null) | ||
636 | { | ||
637 | clientsvr.Server.Close(); | ||
638 | m_clientServers.Remove(clientsvr); | ||
639 | } | ||
640 | } | ||
641 | |||
642 | private void RemoveAllClientResource(SimpleRegionInfo src_region) | ||
643 | { | ||
644 | Scene scene; | ||
645 | IClientAPI controller; | ||
646 | |||
647 | // try to get the scene object | ||
648 | if (sceneManager.TryGetScene(src_region.RegionID, out scene) == false) | ||
649 | { | ||
650 | m_log.Error("[BALANCER] " + "The Scene is not found"); | ||
651 | return; | ||
652 | } | ||
653 | |||
654 | // serialization of client's informations | ||
655 | List<ScenePresence> presences = scene.GetScenePresences(); | ||
656 | |||
657 | // remove all scene presences | ||
658 | foreach (ScenePresence pre in presences) | ||
659 | { | ||
660 | uint[] circuits = scene.ClientManager.GetAllCircuits(pre.UUID); | ||
661 | |||
662 | foreach (uint code in circuits) | ||
663 | { | ||
664 | m_log.InfoFormat("[BALANCER] " + "circuit code : {0}", code); | ||
665 | |||
666 | if (scene.ClientManager.TryGetClient(code, out controller)) | ||
667 | { | ||
668 | // stopping clientview thread | ||
669 | if ((controller).IsActive) | ||
670 | { | ||
671 | controller.Stop(); | ||
672 | (controller).IsActive = false; | ||
673 | } | ||
674 | // teminateing clientview thread | ||
675 | controller.Terminate(); | ||
676 | m_log.Info("[BALANCER] " + "client thread stopped"); | ||
677 | } | ||
678 | } | ||
679 | |||
680 | // remove scene presence | ||
681 | scene.RemoveClient(pre.UUID); | ||
682 | } | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * This section implements scene splitting and synchronization | ||
687 | */ | ||
688 | |||
689 | private XmlRpcResponse SplitRegion(XmlRpcRequest request) | ||
690 | { | ||
691 | try | ||
692 | { | ||
693 | int myID = (int) request.Params[0]; | ||
694 | int numRegions = (int) request.Params[1]; | ||
695 | regionPortList = new int[numRegions]; | ||
696 | sceneURL = new string[numRegions]; | ||
697 | tcpClientList = new TcpClient[numRegions]; | ||
698 | |||
699 | for (int i = 0; i < numRegions; i++) | ||
700 | { | ||
701 | regionPortList[i] = (int) request.Params[i + 2]; | ||
702 | sceneURL[i] = (string) request.Params[i + 2 + numRegions]; | ||
703 | } | ||
704 | |||
705 | for (int i = 0; i < numRegions; i++) | ||
706 | { | ||
707 | string hostname = sceneURL[i].Split(new char[] {'/', ':'})[3]; | ||
708 | m_log.InfoFormat("[SPLITSCENE] " + "creating tcp client host:{0}", hostname); | ||
709 | tcpClientList[i] = new TcpClient(hostname, 10001); | ||
710 | } | ||
711 | |||
712 | bool isMaster = (myID == 0); | ||
713 | |||
714 | isLocalNeighbour = new bool[numRegions]; | ||
715 | for (int i = 0; i < numRegions; i++) isLocalNeighbour[i] = (sceneURL[i] == sceneURL[myID]); | ||
716 | |||
717 | RegionInfo region = SearchRegionFromPortNum(regionPortList[myID]); | ||
718 | |||
719 | //Console.WriteLine("\n === SplitRegion {0}\n", region.RegionID); | ||
720 | |||
721 | Scene scene; | ||
722 | if (sceneManager.TryGetScene(region.RegionID, out scene)) | ||
723 | { | ||
724 | // Disable event updates, backups etc in the slave(s) | ||
725 | scene.Region_Status = isMaster ? RegionStatus.Up : RegionStatus.SlaveScene; | ||
726 | |||
727 | //Console.WriteLine("=== SplitRegion {0}: Scene found, status {1}", region.RegionID, scene.Region_Status); | ||
728 | |||
729 | // Disabling half of the avatars in master, and the other half in slave | ||
730 | |||
731 | int i = 0; | ||
732 | |||
733 | List<uint> circuits = scene.ClientManager.GetAllCircuitCodes(); | ||
734 | circuits.Sort(); | ||
735 | |||
736 | foreach (uint code in circuits) | ||
737 | { | ||
738 | m_log.InfoFormat("[BALANCER] " + "circuit code : {0}", code); | ||
739 | |||
740 | IClientAPI controller; | ||
741 | if (scene.ClientManager.TryGetClient(code, out controller)) | ||
742 | { | ||
743 | // Divide the presences evenly over the set of subscenes | ||
744 | LLClientView client = (LLClientView) controller; | ||
745 | client.IsActive = (((i + myID) % sceneURL.Length) == 0); | ||
746 | |||
747 | m_log.InfoFormat("[SPLITSCENE] === SplitRegion {0}: SP.PacketEnabled {1}", region.RegionID, client.IsActive); | ||
748 | |||
749 | if (!client.IsActive) | ||
750 | { | ||
751 | // stopping clientview thread | ||
752 | client.Stop(); | ||
753 | } | ||
754 | |||
755 | ++i; | ||
756 | } | ||
757 | } | ||
758 | |||
759 | scene.splitID = myID; | ||
760 | scene.SynchronizeScene = SynchronizeScenes; | ||
761 | isSplit = true; | ||
762 | } | ||
763 | else | ||
764 | { | ||
765 | m_log.Error("[SPLITSCENE] " + String.Format("Scene not found {0}", region.RegionID)); | ||
766 | } | ||
767 | } | ||
768 | catch (Exception e) | ||
769 | { | ||
770 | m_log.Error("[SPLITSCENE] " + e); | ||
771 | m_log.Error("[SPLITSCENE] " + e.StackTrace); | ||
772 | } | ||
773 | |||
774 | return new XmlRpcResponse(); | ||
775 | } | ||
776 | |||
777 | private XmlRpcResponse MergeRegions(XmlRpcRequest request) | ||
778 | { | ||
779 | // This should only be called for the master scene | ||
780 | try | ||
781 | { | ||
782 | m_log.Info("[BALANCER] " + "Entering MergeRegions()"); | ||
783 | |||
784 | string src_url = (string) request.Params[0]; | ||
785 | int src_port = (int) request.Params[1]; | ||
786 | |||
787 | RegionInfo region = SearchRegionFromPortNum(src_port); | ||
788 | |||
789 | Util.XmlRpcCommand(region.proxyUrl, "BlockClientMessages", src_url, src_port + proxyOffset); | ||
790 | |||
791 | Scene scene; | ||
792 | if (sceneManager.TryGetScene(region.RegionID, out scene)) | ||
793 | { | ||
794 | isSplit = false; | ||
795 | |||
796 | scene.SynchronizeScene = null; | ||
797 | scene.Region_Status = RegionStatus.Up; | ||
798 | |||
799 | List<ScenePresence> presences = scene.GetScenePresences(); | ||
800 | foreach (ScenePresence pre in presences) | ||
801 | { | ||
802 | LLClientView client = (LLClientView) pre.ControllingClient; | ||
803 | if (!client.IsActive) | ||
804 | { | ||
805 | client.Restart(); | ||
806 | client.IsActive = true; | ||
807 | } | ||
808 | } | ||
809 | } | ||
810 | |||
811 | // Delete the slave scenes | ||
812 | for (int i = 1; i < sceneURL.Length; i++) | ||
813 | { | ||
814 | string url = (sceneURL[i].Split('/')[2]).Split(':')[0]; // get URL part from EP | ||
815 | Util.XmlRpcCommand(region.proxyUrl, "DeleteRegion", regionPortList[i] + proxyOffset, url); | ||
816 | Thread.Sleep(1000); | ||
817 | Util.XmlRpcCommand(sceneURL[i], "TerminateRegion", regionPortList[i]); // TODO: need + proxyOffset? | ||
818 | } | ||
819 | |||
820 | Util.XmlRpcCommand(region.proxyUrl, "UnblockClientMessages", src_url, src_port + proxyOffset); | ||
821 | } | ||
822 | catch (Exception e) | ||
823 | { | ||
824 | m_log.Error("[BALANCER] " + e); | ||
825 | m_log.Error("[BALANCER] " + e.StackTrace); | ||
826 | throw; | ||
827 | } | ||
828 | |||
829 | return new XmlRpcResponse(); | ||
830 | } | ||
831 | |||
832 | private XmlRpcResponse UpdatePhysics(XmlRpcRequest request) | ||
833 | { | ||
834 | // this callback receives physic scene updates from the other sub-scenes (in split mode) | ||
835 | |||
836 | int regionPort = (int) request.Params[0]; | ||
837 | UUID scenePresenceID = new UUID((byte[]) request.Params[1], 0); | ||
838 | Vector3 position = new Vector3((byte[]) request.Params[2], 0); | ||
839 | Vector3 velocity = new Vector3((byte[]) request.Params[3], 0); | ||
840 | bool flying = (bool) request.Params[4]; | ||
841 | |||
842 | LocalUpdatePhysics(regionPort, scenePresenceID, position, velocity, flying); | ||
843 | |||
844 | return new XmlRpcResponse(); | ||
845 | } | ||
846 | |||
847 | private void LocalUpdatePhysics(int regionPort, UUID scenePresenceID, Vector3 position, Vector3 velocity, bool flying) | ||
848 | { | ||
849 | //m_log.Info("[SPLITSCENE] "+String.Format("UpdatePhysics called {0}", regionID)); | ||
850 | |||
851 | //m_log.Info("[SPLITSCENE] "+"LocalUpdatePhysics [region port:{0}, client:{1}, position:{2}, velocity:{3}, flying:{4}]", | ||
852 | // regionPort, scenePresenceID.ToString(), position.ToString(), | ||
853 | // velocity.ToString(), flying); | ||
854 | |||
855 | RegionInfo region = SearchRegionFromPortNum(regionPort); | ||
856 | |||
857 | // Find and update the scene precense | ||
858 | Scene scene; | ||
859 | if (sceneManager.TryGetScene(region.RegionID, out scene)) | ||
860 | { | ||
861 | ScenePresence pre = scene.GetScenePresences().Find(delegate(ScenePresence x) { return x.UUID == scenePresenceID; }); | ||
862 | |||
863 | if (pre == null) | ||
864 | { | ||
865 | m_log.ErrorFormat("[SPLITSCENE] [LocalUpdatePhysics] ScenePresence is missing... ({0})", scenePresenceID.ToString()); | ||
866 | return; | ||
867 | } | ||
868 | |||
869 | // m_log.Info("[SPLITSCENE] "+"LocalUpdatePhysics [region:{0}, client:{1}]", | ||
870 | // regionID.ToString(), pre.ToString()); | ||
871 | |||
872 | pre.AbsolutePosition = position; // will set PhysicsActor.Position | ||
873 | pre.Velocity = velocity; // will set PhysicsActor.Velocity | ||
874 | pre.PhysicsActor.Flying = flying; | ||
875 | } | ||
876 | } | ||
877 | |||
878 | private void SynchronizeScenes(Scene scene) | ||
879 | { | ||
880 | if (!isSplit) | ||
881 | { | ||
882 | return; | ||
883 | } | ||
884 | |||
885 | lock (padlock) | ||
886 | { | ||
887 | // Callback activated after a physics scene update | ||
888 | // int i = 0; | ||
889 | List<ScenePresence> presences = scene.GetScenePresences(); | ||
890 | foreach (ScenePresence pre in presences) | ||
891 | { | ||
892 | LLClientView client = (LLClientView) pre.ControllingClient; | ||
893 | |||
894 | // Because data changes by the physics simulation when the client doesn't move, | ||
895 | // if MovementFlag is false, It is necessary to synchronize. | ||
896 | //if (pre.MovementFlag!=0 && client.PacketProcessingEnabled==true) | ||
897 | if (client.IsActive) | ||
898 | { | ||
899 | //m_log.Info("[SPLITSCENE] "+String.Format("Client moving in {0} {1}", scene.RegionInfo.RegionID, pre.AbsolutePosition)); | ||
900 | |||
901 | for (int i = 0; i < sceneURL.Length; i++) | ||
902 | { | ||
903 | if (i == scene.splitID) | ||
904 | { | ||
905 | continue; | ||
906 | } | ||
907 | |||
908 | if (isLocalNeighbour[i]) | ||
909 | { | ||
910 | //m_log.Info("[SPLITSCENE] "+"Synchronize ScenePresence (Local) [region:{0}=>{1}, client:{2}]", | ||
911 | // scene.RegionInfo.RegionID, regionPortList[i], pre.ToString()); | ||
912 | LocalUpdatePhysics(regionPortList[i], pre.UUID, pre.AbsolutePosition, pre.Velocity, pre.PhysicsActor.Flying); | ||
913 | } | ||
914 | else | ||
915 | { | ||
916 | //m_log.Info("[SPLITSCENE] "+"Synchronize ScenePresence (Remote) [region port:{0}, client:{1}, position:{2}, velocity:{3}, flying:{4}]", | ||
917 | // regionPortList[i], pre.ToString(), pre.AbsolutePosition.ToString(), | ||
918 | // pre.Velocity.ToString(), pre.PhysicsActor.Flying); | ||
919 | |||
920 | |||
921 | Util.XmlRpcCommand(sceneURL[i], "UpdatePhysics", | ||
922 | regionPortList[i], pre.UUID.GetBytes(), | ||
923 | pre.AbsolutePosition.GetBytes(), pre.Velocity.GetBytes(), | ||
924 | pre.PhysicsActor.Flying); | ||
925 | |||
926 | /* | ||
927 | byte[] buff = new byte[12+12+1]; | ||
928 | |||
929 | Buffer.BlockCopy(pre.AbsolutePosition.GetBytes(), 0, buff, 0, 12); | ||
930 | Buffer.BlockCopy(pre.Velocity.GetBytes(), 0, buff, 12, 12); | ||
931 | buff[24] = (byte)((pre.PhysicsActor.Flying)?1:0); | ||
932 | |||
933 | // create header | ||
934 | InternalPacketHeader header = new InternalPacketHeader(); | ||
935 | |||
936 | header.type = 1; | ||
937 | header.throttlePacketType = 0; | ||
938 | header.numbytes = buff.Length; | ||
939 | header.agent_id = pre.UUID.Guid; | ||
940 | header.region_port = regionPortList[i]; | ||
941 | |||
942 | //Send | ||
943 | tcpClientList[i].send(header, buff); | ||
944 | */ | ||
945 | } | ||
946 | } | ||
947 | } | ||
948 | // ++i; | ||
949 | } | ||
950 | } | ||
951 | } | ||
952 | |||
953 | public bool SynchronizePackets(IScene scene, Packet packet, UUID agentID, ThrottleOutPacketType throttlePacketType) | ||
954 | { | ||
955 | if (!isSplit) | ||
956 | { | ||
957 | return false; | ||
958 | } | ||
959 | |||
960 | Scene localScene = (Scene) scene; | ||
961 | |||
962 | for (int i = 0; i < sceneURL.Length; i++) | ||
963 | { | ||
964 | if (i == localScene.splitID) | ||
965 | { | ||
966 | continue; | ||
967 | } | ||
968 | |||
969 | if (isLocalNeighbour[i]) | ||
970 | { | ||
971 | //m_log.Info("[SPLITSCENE] "+"Synchronize Packet (Local) [type:{0}, client:{1}]", | ||
972 | // packet.Type.ToString(), agentID.ToString()); | ||
973 | LocalUpdatePacket(regionPortList[i], agentID, packet, throttlePacketType); | ||
974 | } | ||
975 | else | ||
976 | { | ||
977 | //m_log.Info("[SPLITSCENE] "+"Synchronize Packet (Remote) [type:{0}, client:{1}]", | ||
978 | // packet.Type.ToString(), agentID.ToString()); | ||
979 | // to bytes | ||
980 | byte[] buff = packet.ToBytes(); | ||
981 | |||
982 | // create header | ||
983 | InternalPacketHeader header = new InternalPacketHeader(); | ||
984 | |||
985 | header.type = 0; | ||
986 | header.throttlePacketType = (int) throttlePacketType; | ||
987 | header.numbytes = buff.Length; | ||
988 | header.agent_id = agentID.Guid; | ||
989 | header.region_port = regionPortList[i]; | ||
990 | |||
991 | //Send | ||
992 | tcpClientList[i].send(header, buff); | ||
993 | |||
994 | PacketPool.Instance.ReturnPacket(packet); | ||
995 | } | ||
996 | } | ||
997 | |||
998 | return true; | ||
999 | } | ||
1000 | |||
1001 | private void LocalUpdatePacket(int regionPort, UUID agentID, Packet packet, ThrottleOutPacketType throttlePacketType) | ||
1002 | { | ||
1003 | Scene scene; | ||
1004 | |||
1005 | RegionInfo region = SearchRegionFromPortNum(regionPort); | ||
1006 | |||
1007 | // m_log.Info("[SPLITSCENE] "+"LocalUpdatePacket [region port:{0}, client:{1}, packet type:{2}]", | ||
1008 | // regionPort, agentID.ToString(), packet.GetType().ToString()); | ||
1009 | |||
1010 | if (sceneManager.TryGetScene(region.RegionID, out scene)) | ||
1011 | { | ||
1012 | ScenePresence pre = scene.GetScenePresences().Find(delegate(ScenePresence x) { return x.UUID == agentID; }); | ||
1013 | |||
1014 | if (pre == null) | ||
1015 | { | ||
1016 | m_log.ErrorFormat("[SPLITSCENE] [LocalUpdatePacket] ScenePresence is missing... ({0})", agentID.ToString()); | ||
1017 | return; | ||
1018 | } | ||
1019 | if (pre.ControllingClient is LLClientView) | ||
1020 | { | ||
1021 | if (((LLClientView)pre.ControllingClient).IsActive) | ||
1022 | { | ||
1023 | ((LLClientView)pre.ControllingClient).OutPacket(packet, throttlePacketType); | ||
1024 | } | ||
1025 | else | ||
1026 | { | ||
1027 | PacketPool.Instance.ReturnPacket(packet); | ||
1028 | } | ||
1029 | } | ||
1030 | else | ||
1031 | { | ||
1032 | PacketPool.Instance.ReturnPacket(packet); | ||
1033 | } | ||
1034 | } | ||
1035 | } | ||
1036 | |||
1037 | public void SynchronizePacketRecieve(InternalPacketHeader header, byte[] buff) | ||
1038 | { | ||
1039 | // m_log.Info("[SPLITSCENE] "+"entering SynchronizePacketRecieve[type={0}]", header.type); | ||
1040 | |||
1041 | if (!isSplit) | ||
1042 | { | ||
1043 | return; | ||
1044 | } | ||
1045 | |||
1046 | switch (header.type) | ||
1047 | { | ||
1048 | case 0: | ||
1049 | |||
1050 | byte[] zero = new byte[3000]; | ||
1051 | |||
1052 | // deserialize packet | ||
1053 | int packetEnd = buff.Length - 1; | ||
1054 | // packetEnd = buff.Length; | ||
1055 | |||
1056 | try | ||
1057 | { | ||
1058 | //m_log.Info("[SPLITSCENE] "+"PacketPool.Instance : {0}", (PacketPool.Instance == null)?"null":"not null"); | ||
1059 | //m_log.Info("[SPLITSCENE] "+"buff length={0}", buff.Length); | ||
1060 | |||
1061 | Packet packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero); | ||
1062 | |||
1063 | LocalUpdatePacket(header.region_port, new UUID(header.agent_id), | ||
1064 | packet, (ThrottleOutPacketType) header.throttlePacketType); | ||
1065 | } | ||
1066 | catch (Exception e) | ||
1067 | { | ||
1068 | m_log.Error("[SPLITSCENE] " + e); | ||
1069 | m_log.Error("[SPLITSCENE] " + e.StackTrace); | ||
1070 | } | ||
1071 | |||
1072 | break; | ||
1073 | |||
1074 | case 1: | ||
1075 | |||
1076 | int regionPort = header.region_port; | ||
1077 | UUID scenePresenceID = new UUID(header.agent_id); | ||
1078 | Vector3 position = new Vector3(buff, 0); | ||
1079 | Vector3 velocity = new Vector3(buff, 12); | ||
1080 | bool flying = ((buff[24] == 1) ? true : false); | ||
1081 | |||
1082 | LocalUpdatePhysics(regionPort, scenePresenceID, position, velocity, flying); | ||
1083 | |||
1084 | break; | ||
1085 | |||
1086 | default: | ||
1087 | m_log.Info("[SPLITSCENE] " + "Invalid type"); | ||
1088 | break; | ||
1089 | } | ||
1090 | |||
1091 | // m_log.Info("[SPLITSCENE] "+"exiting SynchronizePacketRecieve"); | ||
1092 | } | ||
1093 | } | ||
1094 | } | ||
diff --git a/ThirdParty/3Di/LoadBalancer/Resources/LoadBalancer.addin.xml b/ThirdParty/3Di/LoadBalancer/Resources/LoadBalancer.addin.xml deleted file mode 100644 index ac6ac15..0000000 --- a/ThirdParty/3Di/LoadBalancer/Resources/LoadBalancer.addin.xml +++ /dev/null | |||
@@ -1,12 +0,0 @@ | |||
1 | <Addin id="LoadBalancer" version="0.1"> | ||
2 | <Runtime> | ||
3 | <Import assembly="OpenSim.ApplicationPlugins.LoadBalancer.dll" /> | ||
4 | </Runtime> | ||
5 | <Dependencies> | ||
6 | <Addin id="OpenSim" version="0.5" /> | ||
7 | <Addin id="RegionProxy" version="0.1" /> | ||
8 | </Dependencies> | ||
9 | <Extension path="/OpenSim/Startup"> | ||
10 | <Plugin id="LoadBalancer" type="OpenSim.ApplicationPlugins.LoadBalancer.LoadBalancerPlugin" /> | ||
11 | </Extension> | ||
12 | </Addin> | ||
diff --git a/ThirdParty/3Di/LoadBalancer/TcpClient.cs b/ThirdParty/3Di/LoadBalancer/TcpClient.cs deleted file mode 100644 index 1acba24..0000000 --- a/ThirdParty/3Di/LoadBalancer/TcpClient.cs +++ /dev/null | |||
@@ -1,225 +0,0 @@ | |||
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.IO; | ||
30 | using System.Net; | ||
31 | using System.Net.Sockets; | ||
32 | using System.Threading; | ||
33 | |||
34 | namespace OpenSim.ApplicationPlugins.LoadBalancer | ||
35 | { | ||
36 | public class AsynchronousClient | ||
37 | { | ||
38 | private static ManualResetEvent connectDone = new ManualResetEvent(false); | ||
39 | private static ManualResetEvent sendDone = new ManualResetEvent(false); | ||
40 | |||
41 | public static Socket StartClient(string hostname, int port) | ||
42 | { | ||
43 | try | ||
44 | { | ||
45 | IPHostEntry ipHostInfo = Dns.GetHostEntry(hostname); | ||
46 | IPAddress ipAddress = ipHostInfo.AddressList[0]; | ||
47 | IPEndPoint remoteEP = new IPEndPoint(ipAddress, port); | ||
48 | |||
49 | Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | ||
50 | client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client); | ||
51 | connectDone.WaitOne(); | ||
52 | /* | ||
53 | Send(client,"This is a test<EOF>"); | ||
54 | sendDone.WaitOne(); | ||
55 | Receive(client); | ||
56 | receiveDone.WaitOne(); | ||
57 | client.Shutdown(SocketShutdown.Both); | ||
58 | client.Close(); | ||
59 | */ | ||
60 | return client; | ||
61 | } | ||
62 | catch (Exception e) | ||
63 | { | ||
64 | Console.WriteLine(e.ToString()); | ||
65 | throw new Exception("socket error !!"); | ||
66 | } | ||
67 | } | ||
68 | |||
69 | private static void ConnectCallback(IAsyncResult ar) | ||
70 | { | ||
71 | try | ||
72 | { | ||
73 | Socket client = (Socket) ar.AsyncState; | ||
74 | client.EndConnect(ar); | ||
75 | Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint.ToString()); | ||
76 | connectDone.Set(); | ||
77 | } | ||
78 | catch (Exception e) | ||
79 | { | ||
80 | Console.WriteLine(e.ToString()); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | public static void Send(Socket client, byte[] byteData) | ||
85 | { | ||
86 | client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client); | ||
87 | } | ||
88 | |||
89 | private static void SendCallback(IAsyncResult ar) | ||
90 | { | ||
91 | try | ||
92 | { | ||
93 | Socket client = (Socket) ar.AsyncState; | ||
94 | int bytesSent = client.EndSend(ar); | ||
95 | if (bytesSent > 0) | ||
96 | { | ||
97 | //Console.WriteLine("Sent {0} bytes to server.", bytesSent); | ||
98 | } | ||
99 | sendDone.Set(); | ||
100 | } | ||
101 | catch (Exception e) | ||
102 | { | ||
103 | Console.WriteLine(e.ToString()); | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | public class InternalPacketHeader | ||
109 | { | ||
110 | public Guid agent_id; | ||
111 | private byte[] buffer = new byte[32]; | ||
112 | public int numbytes; | ||
113 | public int region_port; | ||
114 | public int throttlePacketType; | ||
115 | public int type; | ||
116 | |||
117 | public void FromBytes(byte[] bytes) | ||
118 | { | ||
119 | MemoryStream memstr = new MemoryStream(bytes); | ||
120 | memstr.Seek(0, SeekOrigin.Begin); | ||
121 | BinaryReader binread = new BinaryReader(memstr); | ||
122 | |||
123 | type = binread.ReadInt32(); | ||
124 | throttlePacketType = binread.ReadInt32(); | ||
125 | numbytes = binread.ReadInt32(); | ||
126 | agent_id = new Guid(binread.ReadBytes(16)); | ||
127 | region_port = binread.ReadInt32(); | ||
128 | |||
129 | binread.Close(); | ||
130 | } | ||
131 | |||
132 | public byte[] ToBytes() | ||
133 | { | ||
134 | int i = 0; | ||
135 | buffer[i++] = (byte) (type % 256); | ||
136 | buffer[i++] = (byte) ((type >> 8) % 256); | ||
137 | buffer[i++] = (byte) ((type >> 16) % 256); | ||
138 | buffer[i++] = (byte) ((type >> 24) % 256); | ||
139 | |||
140 | buffer[i++] = (byte) (throttlePacketType % 256); | ||
141 | buffer[i++] = (byte) ((throttlePacketType >> 8) % 256); | ||
142 | buffer[i++] = (byte) ((throttlePacketType >> 16) % 256); | ||
143 | buffer[i++] = (byte) ((throttlePacketType >> 24) % 256); | ||
144 | |||
145 | buffer[i++] = (byte) (numbytes % 256); | ||
146 | buffer[i++] = (byte) ((numbytes >> 8) % 256); | ||
147 | buffer[i++] = (byte) ((numbytes >> 16) % 256); | ||
148 | buffer[i++] = (byte) ((numbytes >> 24) % 256); | ||
149 | |||
150 | // no endian care | ||
151 | Buffer.BlockCopy(agent_id.ToByteArray(), 0, buffer, i, 16); | ||
152 | i += 16; | ||
153 | |||
154 | buffer[i++] = (byte) (region_port % 256); | ||
155 | buffer[i++] = (byte) ((region_port >> 8) % 256); | ||
156 | buffer[i++] = (byte) ((region_port >> 16) % 256); | ||
157 | buffer[i++] = (byte) ((region_port >> 24) % 256); | ||
158 | |||
159 | return buffer; | ||
160 | } | ||
161 | } | ||
162 | |||
163 | public class TcpClient | ||
164 | { | ||
165 | public static int internalPacketHeaderSize = 4 * 4 + 16 * 1; | ||
166 | private Socket mConnection; | ||
167 | |||
168 | private string mHostname; | ||
169 | private int mPort; | ||
170 | |||
171 | public TcpClient(string hostname, int port) | ||
172 | { | ||
173 | mHostname = hostname; | ||
174 | mPort = port; | ||
175 | mConnection = null; | ||
176 | } | ||
177 | |||
178 | public void connect() | ||
179 | { | ||
180 | mConnection = AsynchronousClient.StartClient(mHostname, mPort); | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | public void receive() | ||
185 | { | ||
186 | if (mConnection == null) | ||
187 | { | ||
188 | throw new Exception("client not initialized"); | ||
189 | } | ||
190 | try | ||
191 | { | ||
192 | AsynchronousClient.Receive(this.mConnection); | ||
193 | } | ||
194 | catch (Exception e) | ||
195 | { | ||
196 | Console.WriteLine(e.ToString()); | ||
197 | mConnection = null; | ||
198 | } | ||
199 | } | ||
200 | */ | ||
201 | |||
202 | public void send(InternalPacketHeader header, byte[] packet) | ||
203 | { | ||
204 | lock (this) | ||
205 | { | ||
206 | if (mConnection == null) | ||
207 | { | ||
208 | // throw new Exception("client not initialized"); | ||
209 | connect(); | ||
210 | } | ||
211 | |||
212 | AsynchronousClient.Send(mConnection, header.ToBytes()); | ||
213 | |||
214 | /* | ||
215 | for (int i = 0; i < 10; i++) | ||
216 | { | ||
217 | Console.Write(packet[i] + " "); | ||
218 | } | ||
219 | Console.WriteLine(""); | ||
220 | */ | ||
221 | AsynchronousClient.Send(mConnection, packet); | ||
222 | } | ||
223 | } | ||
224 | } | ||
225 | } \ No newline at end of file | ||
diff --git a/ThirdParty/3Di/LoadBalancer/TcpServer.cs b/ThirdParty/3Di/LoadBalancer/TcpServer.cs deleted file mode 100644 index 376c0a5..0000000 --- a/ThirdParty/3Di/LoadBalancer/TcpServer.cs +++ /dev/null | |||
@@ -1,219 +0,0 @@ | |||
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.IO; | ||
30 | using System.Net; | ||
31 | using System.Net.Sockets; | ||
32 | using System.Threading; | ||
33 | |||
34 | namespace OpenSim.ApplicationPlugins.LoadBalancer | ||
35 | { | ||
36 | public class StateObject | ||
37 | { | ||
38 | public const int BufferSize = 2048; | ||
39 | public byte[] buffer = new byte[BufferSize]; | ||
40 | public InternalPacketHeader header = null; | ||
41 | public MemoryStream ms_ptr = new MemoryStream(); | ||
42 | public Socket workSocket = null; | ||
43 | } | ||
44 | |||
45 | public class AsynchronousSocketListener | ||
46 | { | ||
47 | public static ManualResetEvent allDone = new ManualResetEvent(false); | ||
48 | public static string data = null; | ||
49 | |||
50 | #region KIRYU | ||
51 | |||
52 | #region Delegates | ||
53 | |||
54 | public delegate void PacketRecieveHandler(InternalPacketHeader header, byte[] buff); | ||
55 | |||
56 | #endregion | ||
57 | |||
58 | public static PacketRecieveHandler PacketHandler = null; | ||
59 | |||
60 | #endregion | ||
61 | |||
62 | public AsynchronousSocketListener() | ||
63 | { | ||
64 | } | ||
65 | |||
66 | public static void StartListening(int port) | ||
67 | { | ||
68 | IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName()); | ||
69 | IPAddress ipAddress = ipHostInfo.AddressList[0]; | ||
70 | IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port); | ||
71 | |||
72 | Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); | ||
73 | try | ||
74 | { | ||
75 | listener.Bind(localEndPoint); | ||
76 | listener.Listen(100); | ||
77 | while (true) | ||
78 | { | ||
79 | allDone.Reset(); | ||
80 | listener.BeginAccept(new AsyncCallback(AcceptCallback), listener); | ||
81 | allDone.WaitOne(); | ||
82 | } | ||
83 | } | ||
84 | catch (Exception e) | ||
85 | { | ||
86 | Console.WriteLine(e.ToString()); | ||
87 | } | ||
88 | /* | ||
89 | Console.WriteLine("\nPress ENTER to continue..."); | ||
90 | Console.Read(); | ||
91 | */ | ||
92 | } | ||
93 | |||
94 | public static void AcceptCallback(IAsyncResult ar) | ||
95 | { | ||
96 | allDone.Set(); | ||
97 | Socket listener = (Socket) ar.AsyncState; | ||
98 | Socket handler = listener.EndAccept(ar); | ||
99 | StateObject state = new StateObject(); | ||
100 | state.workSocket = handler; | ||
101 | handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); | ||
102 | } | ||
103 | |||
104 | public static void ReadCallback(IAsyncResult ar) | ||
105 | { | ||
106 | StateObject state = (StateObject) ar.AsyncState; | ||
107 | Socket handler = state.workSocket; | ||
108 | |||
109 | try | ||
110 | { | ||
111 | int bytesRead = handler.EndReceive(ar); | ||
112 | |||
113 | //MainLog.Instance.Verbose("TCPSERVER", "Received packet [{0}]", bytesRead); | ||
114 | |||
115 | if (bytesRead > 0) | ||
116 | { | ||
117 | state.ms_ptr.Write(state.buffer, 0, bytesRead); | ||
118 | } | ||
119 | else | ||
120 | { | ||
121 | //MainLog.Instance.Verbose("TCPSERVER", "Connection terminated"); | ||
122 | return; | ||
123 | } | ||
124 | |||
125 | long rest_size = state.ms_ptr.Length; | ||
126 | long current_pos = 0; | ||
127 | while (rest_size > TcpClient.internalPacketHeaderSize) | ||
128 | { | ||
129 | if ((state.header == null) && (rest_size >= TcpClient.internalPacketHeaderSize)) | ||
130 | { | ||
131 | //MainLog.Instance.Verbose("TCPSERVER", "Processing header"); | ||
132 | |||
133 | // reading header | ||
134 | state.header = new InternalPacketHeader(); | ||
135 | |||
136 | byte[] headerbytes = new byte[TcpClient.internalPacketHeaderSize]; | ||
137 | state.ms_ptr.Position = current_pos; | ||
138 | state.ms_ptr.Read(headerbytes, 0, TcpClient.internalPacketHeaderSize); | ||
139 | state.ms_ptr.Seek(0, SeekOrigin.End); | ||
140 | state.header.FromBytes(headerbytes); | ||
141 | } | ||
142 | |||
143 | if ((state.header != null) && (rest_size >= state.header.numbytes + TcpClient.internalPacketHeaderSize)) | ||
144 | { | ||
145 | //MainLog.Instance.Verbose("TCPSERVER", "Processing body"); | ||
146 | |||
147 | // reading body | ||
148 | byte[] packet = new byte[state.header.numbytes]; | ||
149 | state.ms_ptr.Position = current_pos + TcpClient.internalPacketHeaderSize; | ||
150 | state.ms_ptr.Read(packet, 0, state.header.numbytes); | ||
151 | |||
152 | /* | ||
153 | for (int i=0; i<state.header.numbytes; i++) | ||
154 | { | ||
155 | System.Console.Write(packet[i] + " "); | ||
156 | } | ||
157 | System.Console.WriteLine(); | ||
158 | */ | ||
159 | |||
160 | state.ms_ptr.Seek(0, SeekOrigin.End); | ||
161 | // call loadbarancer function | ||
162 | if (PacketHandler == null) | ||
163 | { | ||
164 | //MainLog.Instance.Verbose("TCPSERVER", "PacketHandler not found"); | ||
165 | } | ||
166 | else | ||
167 | { | ||
168 | //MainLog.Instance.Verbose("TCPSERVER", "calling PacketHandler"); | ||
169 | PacketHandler(state.header, packet); | ||
170 | } | ||
171 | |||
172 | int read_size = state.header.numbytes + TcpClient.internalPacketHeaderSize; | ||
173 | state.header = null; | ||
174 | |||
175 | rest_size -= read_size; | ||
176 | current_pos += read_size; | ||
177 | |||
178 | if (rest_size < TcpClient.internalPacketHeaderSize) | ||
179 | { | ||
180 | byte[] rest_bytes = new byte[rest_size]; | ||
181 | state.ms_ptr.Position = read_size; | ||
182 | state.ms_ptr.Read(rest_bytes, 0, (int) rest_size); | ||
183 | state.ms_ptr.Close(); | ||
184 | state.ms_ptr = new MemoryStream(); | ||
185 | state.ms_ptr.Write(rest_bytes, 0, (int) rest_size); | ||
186 | break; | ||
187 | } | ||
188 | } | ||
189 | } // while (true) | ||
190 | } | ||
191 | catch (Exception) | ||
192 | { | ||
193 | //MainLog.Instance.Verbose("TCPSERVER", e.ToString()); | ||
194 | //MainLog.Instance.Verbose("TCPSERVER", e.StackTrace); | ||
195 | } | ||
196 | |||
197 | handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), state); | ||
198 | } | ||
199 | } | ||
200 | |||
201 | public class TcpServer | ||
202 | { | ||
203 | private int mPort = 11000; | ||
204 | |||
205 | public TcpServer() | ||
206 | { | ||
207 | } | ||
208 | |||
209 | public TcpServer(int port) | ||
210 | { | ||
211 | mPort = port; | ||
212 | } | ||
213 | |||
214 | public void start() | ||
215 | { | ||
216 | AsynchronousSocketListener.StartListening(mPort); | ||
217 | } | ||
218 | } | ||
219 | } \ No newline at end of file | ||