Annotation of /trunk/ModularRex/RexParts/RexPython/RexEventManager.cs
Parent Directory
|
Revision Log
Revision 67 - (view) (download)
| 1 : | mikkopa | 12 | using System; |
| 2 : | mikkopa | 18 | using System.Reflection; |
| 3 : | mikkopa | 12 | using System.Collections.Generic; |
| 4 : | using System.Text; | ||
| 5 : | using OpenMetaverse; | ||
| 6 : | mikkopa | 39 | using OpenSim; |
| 7 : | mikkopa | 12 | using OpenSim.Framework; |
| 8 : | mikkopa | 63 | using OpenSim.Region.Framework.Scenes; |
| 9 : | mikkopa | 18 | using log4net; |
| 10 : | mikkopa | 12 | |
| 11 : | namespace ModularRex.RexParts.RexPython | ||
| 12 : | { | ||
| 13 : | [Serializable] | ||
| 14 : | class RexEventManager | ||
| 15 : | { | ||
| 16 : | mikkopa | 19 | private RexScriptEngine m_scriptEngine; |
| 17 : | mikkopa | 18 | private static readonly ILog m_log |
| 18 : | mikkopa | 42 | = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
| 19 : | private ModrexObjects m_rexObjects; | ||
| 20 : | mikkopa | 12 | |
| 21 : | // tuco fixme, is there a better way to do this search??? | ||
| 22 : | private EntityBase GetEntityBase(uint vId) | ||
| 23 : | { | ||
| 24 : | mikkopa | 19 | SceneObjectPart part = m_scriptEngine.World.GetSceneObjectPart(vId); |
| 25 : | mikkopa | 12 | if (part != null && (EntityBase)(part.ParentGroup) != null) |
| 26 : | return (EntityBase)(part.ParentGroup); | ||
| 27 : | else | ||
| 28 : | return null; | ||
| 29 : | } | ||
| 30 : | |||
| 31 : | mikkopa | 19 | public RexEventManager(RexScriptEngine scriptEngine) |
| 32 : | mikkopa | 12 | { |
| 33 : | mikkopa | 19 | m_scriptEngine = scriptEngine; |
| 34 : | mikkopa | 18 | m_log.InfoFormat("[RexScriptEngine]: Hooking up to server events"); |
| 35 : | mikkopa | 42 | |
| 36 : | mikkopa | 63 | OpenSim.Region.Framework.Interfaces.IRegionModule module = m_scriptEngine.World.Modules["RexObjectsModule"]; |
| 37 : | mikkopa | 42 | if (module != null && module is ModrexObjects) |
| 38 : | { | ||
| 39 : | m_rexObjects = (ModrexObjects)module; | ||
| 40 : | m_rexObjects.OnPythonClassChange += onPythonClassChange; | ||
| 41 : | //myScriptEngine.World.EventManager.OnPythonClassChange += OnPythonClassChange; //this was launched from SceneObjectPart | ||
| 42 : | } | ||
| 43 : | |||
| 44 : | mikkopa | 19 | m_scriptEngine.World.EventManager.OnObjectGrab += touch_start; |
| 45 : | mikkopa | 12 | // myScriptEngine.World.EventManager.OnRezScript += OnRezScript; |
| 46 : | // myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript; | ||
| 47 : | // myScriptEngine.World.EventManager.OnFrame += OnFrame; | ||
| 48 : | mikkopa | 42 | m_scriptEngine.World.EventManager.OnNewClient += OnNewClient; |
| 49 : | //m_scriptEngine.World.EventManager.OnNewPresence += OnNewPresence; //this ain't triggered in OpenSim no more | ||
| 50 : | mikkopa | 19 | m_scriptEngine.World.EventManager.OnRemovePresence += OnRemovePresence; |
| 51 : | m_scriptEngine.World.EventManager.OnShutdown += OnShutDown; | ||
| 52 : | mikkopa | 12 | |
| 53 : | ///TODO: | ||
| 54 : | ///These events were added to forked version. Some of them can be handled | ||
| 55 : | ///other way, some need changes to core and some need changes to physics engine. | ||
| 56 : | mikkopa | 18 | //myScriptEngine.World.EventManager.OnAddEntity += OnAddEntity; //this was previously launched from Scene or InnerScene |
| 57 : | //myScriptEngine.World.EventManager.OnRemoveEntity += OnRemoveEntity; //this was previously launched from Scene | ||
| 58 : | //myScriptEngine.World.EventManager.OnPrimVolumeCollision += OnPrimVolumeCollision; //this was launched from PhysicActor | ||
| 59 : | mikkopa | 19 | m_scriptEngine.World.EventManager.OnChatFromWorld += OnRexScriptListen; |
| 60 : | m_scriptEngine.World.EventManager.OnChatBroadcast += OnRexScriptListen; | ||
| 61 : | m_scriptEngine.World.EventManager.OnChatFromClient += OnRexScriptListen; | ||
| 62 : | mikkopa | 66 | |
| 63 : | mikkopa | 67 | m_scriptEngine.World.AddCommand(scriptEngine, "python", "python help", |
| 64 : | mikkopa | 66 | "Gives more help on python commands", PythonScriptCommand); |
| 65 : | mikkopa | 63 | //OpenSim.OpenSim.RegisterCmd("python", PythonScriptCommand, "Rex python commands. Type \"python help\" for more information."); |
| 66 : | mikkopa | 12 | } |
| 67 : | |||
| 68 : | mikkopa | 42 | private void onPythonClassChange(UUID id) |
| 69 : | { | ||
| 70 : | SceneObjectPart sop = m_scriptEngine.World.GetSceneObjectPart(id); | ||
| 71 : | if (sop != null) | ||
| 72 : | { | ||
| 73 : | OnPythonClassChange(sop.LocalId); | ||
| 74 : | } | ||
| 75 : | else | ||
| 76 : | { | ||
| 77 : | m_log.Warn("[RexScriptEngine]: Scene Object Part not found. Could not initiate OnPythonClassChange"); | ||
| 78 : | } | ||
| 79 : | } | ||
| 80 : | |||
| 81 : | mikkopa | 66 | private void PythonScriptCommand(string module, string[] cmdparams) |
| 82 : | mikkopa | 15 | { |
| 83 : | try | ||
| 84 : | { | ||
| 85 : | if (cmdparams.Length >= 1) | ||
| 86 : | { | ||
| 87 : | mikkopa | 66 | string command = cmdparams[1].ToLower(); //[0] == python |
| 88 : | mikkopa | 15 | switch (command) |
| 89 : | { | ||
| 90 : | case "help": | ||
| 91 : | mikkopa | 18 | m_log.Info("[RexScriptEngine]: Python commands available:"); |
| 92 : | m_log.Info("[RexScriptEngine]: python restart - restarts the python engine"); | ||
| 93 : | mikkopa | 15 | break; |
| 94 : | case "restart": | ||
| 95 : | mikkopa | 19 | m_scriptEngine.RestartPythonEngine(); |
| 96 : | mikkopa | 15 | break; |
| 97 : | default: | ||
| 98 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: Unknown PythonScriptEngine command:" + cmdparams[0]); |
| 99 : | mikkopa | 15 | break; |
| 100 : | } | ||
| 101 : | } | ||
| 102 : | } | ||
| 103 : | catch (Exception e) | ||
| 104 : | { | ||
| 105 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnPythonScriptCommand: " + e.ToString()); |
| 106 : | mikkopa | 15 | } |
| 107 : | } | ||
| 108 : | mikkopa | 12 | |
| 109 : | mikkopa | 19 | public void touch_start(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs)//(uint localID, Vector3 offsetPos, IClientAPI remoteClient) |
| 110 : | mikkopa | 12 | { |
| 111 : | string EventParams = "\"touch_start\"," + localID.ToString() + "," + "\"" + remoteClient.AgentId.ToString() + "\""; | ||
| 112 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 113 : | mikkopa | 12 | } |
| 114 : | |||
| 115 : | /* | ||
| 116 : | public void OnRezScript(uint localID, UUID itemID, string script) | ||
| 117 : | { | ||
| 118 : | |||
| 119 : | } | ||
| 120 : | public void OnRemoveScript(uint localID, UUID itemID) | ||
| 121 : | { | ||
| 122 : | |||
| 123 : | } | ||
| 124 : | |||
| 125 : | public void OnFrame() | ||
| 126 : | { | ||
| 127 : | |||
| 128 : | } | ||
| 129 : | mikkopa | 42 | */ |
| 130 : | mikkopa | 12 | |
| 131 : | public void OnNewClient(IClientAPI vClient) | ||
| 132 : | { | ||
| 133 : | mikkopa | 42 | //string EventParams = "\"new_client\"," + "\"" + vClient.AgentId.ToString() + "\""; |
| 134 : | //myScriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); | ||
| 135 : | ScenePresence sp = m_scriptEngine.World.GetScenePresence(vClient.AgentId); | ||
| 136 : | OnNewPresence(sp); | ||
| 137 : | mikkopa | 12 | } |
| 138 : | mikkopa | 42 | |
| 139 : | mikkopa | 12 | |
| 140 : | public void OnNewPresence(ScenePresence vPresence) | ||
| 141 : | { | ||
| 142 : | try | ||
| 143 : | { | ||
| 144 : | mikkopa | 14 | //IRexBot related |
| 145 : | mikkopa | 12 | if (vPresence.ControllingClient is IRexBot) |
| 146 : | { | ||
| 147 : | string EventParams = "\"add_bot\"," + vPresence.LocalId.ToString() + "," + "\"" + vPresence.UUID.ToString() + "\""; | ||
| 148 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 149 : | mikkopa | 12 | } |
| 150 : | else | ||
| 151 : | { | ||
| 152 : | string EventParams = "\"add_presence\"," + vPresence.LocalId.ToString() + "," + "\"" + vPresence.UUID.ToString() + "\""; | ||
| 153 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 154 : | mikkopa | 42 | m_log.Debug("[REXSCRIPT]: CreateEventWithName(" + EventParams + ")"); |
| 155 : | mikkopa | 12 | } |
| 156 : | mikkopa | 14 | |
| 157 : | //Tie up some RexClientView events | ||
| 158 : | RexNetwork.RexClientView rex; | ||
| 159 : | if (vPresence.ClientView.TryGet(out rex)) | ||
| 160 : | { | ||
| 161 : | tuco | 50 | rex.OnRexStartUp += OnRexClientStartUp; |
| 162 : | rex.OnRexClientScriptCmd += OnRexClientScriptCommand; | ||
| 163 : | mikkopa | 14 | } |
| 164 : | mikkopa | 12 | } |
| 165 : | catch (Exception e) | ||
| 166 : | { | ||
| 167 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnNewPresence: " + e.ToString()); |
| 168 : | mikkopa | 12 | } |
| 169 : | } | ||
| 170 : | |||
| 171 : | public void OnRemovePresence(UUID uuid) | ||
| 172 : | { | ||
| 173 : | try | ||
| 174 : | { | ||
| 175 : | // python handles if this presence was bot or human | ||
| 176 : | string EventParams = "\"remove_presence\"," + "\"" + uuid.ToString() + "\""; | ||
| 177 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 178 : | mikkopa | 12 | } |
| 179 : | catch (Exception e) | ||
| 180 : | { | ||
| 181 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnRemovePresence: " + e.ToString()); |
| 182 : | mikkopa | 12 | } |
| 183 : | } | ||
| 184 : | |||
| 185 : | public void OnShutDown() | ||
| 186 : | { | ||
| 187 : | mikkopa | 42 | m_log.Info("[RexScriptEngine]: REX OnShutDown"); |
| 188 : | mikkopa | 12 | } |
| 189 : | |||
| 190 : | public void OnAddEntity(uint localID) | ||
| 191 : | { | ||
| 192 : | mikkopa | 42 | throw new NotImplementedException("OnAddEntity not implemeted"); |
| 193 : | //try | ||
| 194 : | //{ | ||
| 195 : | // string PythonClassName = "rxactor.Actor"; | ||
| 196 : | // string PythonTag = ""; | ||
| 197 : | mikkopa | 12 | |
| 198 : | mikkopa | 42 | // RexObjects.RexObjectGroup tempobj = (RexObjects.RexObjectGroup)GetEntityBase(localID); |
| 199 : | // //SceneObjectGroup tempobj = (SceneObjectGroup)GetEntityBase(localID); | ||
| 200 : | // if (tempobj != null && tempobj.RootPart != null && tempobj.RootPart.RexClassName.Length > 0) | ||
| 201 : | // PythonClassName = tempobj.RootPart.RexClassName; | ||
| 202 : | mikkopa | 12 | |
| 203 : | mikkopa | 42 | // // Create the actor directly without using an event. |
| 204 : | // m_scriptEngine.CreateActorToPython(localID.ToString(), PythonClassName, PythonTag); | ||
| 205 : | //} | ||
| 206 : | //catch (Exception e) | ||
| 207 : | //{ | ||
| 208 : | // m_log.WarnFormat("[RexScriptEngine]: OnAddEntity: " + e.ToString()); | ||
| 209 : | //} | ||
| 210 : | mikkopa | 12 | } |
| 211 : | |||
| 212 : | public void OnRemoveEntity(uint localID) | ||
| 213 : | { | ||
| 214 : | try | ||
| 215 : | { | ||
| 216 : | string EventParams = "\"remove_entity\"," + "\"" + localID.ToString() + "\""; | ||
| 217 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 218 : | mikkopa | 12 | } |
| 219 : | catch (Exception e) | ||
| 220 : | { | ||
| 221 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnRemoveEntity: " + e.ToString()); |
| 222 : | mikkopa | 12 | } |
| 223 : | } | ||
| 224 : | |||
| 225 : | public void OnPythonClassChange(uint localID) | ||
| 226 : | { | ||
| 227 : | try | ||
| 228 : | { | ||
| 229 : | string PythonClassName = "rxactor.Actor"; | ||
| 230 : | string PythonTag = ""; | ||
| 231 : | |||
| 232 : | mikkopa | 42 | SceneObjectPart tempobj = m_scriptEngine.World.GetSceneObjectPart(localID); |
| 233 : | if (tempobj != null) | ||
| 234 : | mikkopa | 12 | { |
| 235 : | mikkopa | 42 | |
| 236 : | tuco | 57 | RexFramework.RexObjectProperties rexObj = m_rexObjects.GetObject(tempobj.UUID); |
| 237 : | mikkopa | 42 | if (rexObj != null) |
| 238 : | mikkopa | 12 | { |
| 239 : | mikkopa | 42 | int tagindex = rexObj.RexClassName.IndexOf("?", 0); |
| 240 : | if (tagindex > -1) | ||
| 241 : | { | ||
| 242 : | PythonClassName = rexObj.RexClassName.Substring(0, tagindex); | ||
| 243 : | PythonTag = rexObj.RexClassName.Substring(tagindex + 1); | ||
| 244 : | } | ||
| 245 : | else | ||
| 246 : | PythonClassName = rexObj.RexClassName; | ||
| 247 : | |||
| 248 : | if (rexObj.RexClassName.Length > 0) | ||
| 249 : | mikkopa | 43 | { |
| 250 : | mikkopa | 42 | tempobj.SetScriptEvents(rexObj.ParentObjectID, (int)scriptEvents.touch_start); |
| 251 : | mikkopa | 43 | tempobj.SendFullUpdateToAllClients(); |
| 252 : | } | ||
| 253 : | mikkopa | 12 | } |
| 254 : | mikkopa | 42 | if (m_scriptEngine.IsEngineStarted) |
| 255 : | m_scriptEngine.CreateActorToPython(localID.ToString(), PythonClassName, PythonTag); | ||
| 256 : | mikkopa | 12 | } |
| 257 : | } | ||
| 258 : | catch (Exception e) | ||
| 259 : | { | ||
| 260 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnPythonClassChange: " + e.ToString()); |
| 261 : | mikkopa | 12 | } |
| 262 : | } | ||
| 263 : | |||
| 264 : | mikkopa | 14 | public void OnRexClientScriptCommand(RexNetwork.RexClientView remoteClient, UUID agentID, List<string> commands) |
| 265 : | mikkopa | 12 | { |
| 266 : | try | ||
| 267 : | { | ||
| 268 : | string Paramlist = ""; | ||
| 269 : | mikkopa | 14 | foreach (string s in commands) |
| 270 : | mikkopa | 12 | Paramlist = Paramlist + "," + "\"" + s + "\""; |
| 271 : | mikkopa | 14 | |
| 272 : | string EventParams = "\"client_event\",\"" + agentID.ToString() + "\"" + Paramlist; | ||
| 273 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 274 : | mikkopa | 12 | } |
| 275 : | catch (Exception e) | ||
| 276 : | { | ||
| 277 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnRexClientScriptCommand: " + e.ToString()); |
| 278 : | mikkopa | 12 | } |
| 279 : | } | ||
| 280 : | |||
| 281 : | public void OnPrimVolumeCollision(uint ownID, uint colliderID) | ||
| 282 : | { | ||
| 283 : | try | ||
| 284 : | { | ||
| 285 : | string EventParams = "\"primvol_col\"," + ownID.ToString() + "," + "\"" + colliderID.ToString() + "\""; | ||
| 286 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 287 : | mikkopa | 12 | } |
| 288 : | catch (Exception e) | ||
| 289 : | { | ||
| 290 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnPrimVolumeCollision: " + e.ToString()); |
| 291 : | mikkopa | 12 | } |
| 292 : | } | ||
| 293 : | |||
| 294 : | mikkopa | 18 | /// <summary> |
| 295 : | /// Listens to all world scripts and clients. Sends event from that to python scripts | ||
| 296 : | /// </summary> | ||
| 297 : | public void OnRexScriptListen(object sender, OSChatMessage chat)//uint vPrimLocalId, int vChannel, string vName, UUID vId, string vMessage) | ||
| 298 : | mikkopa | 12 | { |
| 299 : | try | ||
| 300 : | { | ||
| 301 : | mikkopa | 18 | if (chat.Message != "") |
| 302 : | { | ||
| 303 : | uint localId = 0; | ||
| 304 : | string sid = "0"; | ||
| 305 : | string name = chat.From; | ||
| 306 : | mikkopa | 12 | |
| 307 : | mikkopa | 19 | SceneObjectPart sop = m_scriptEngine.World.GetSceneObjectPart(chat.SenderUUID); |
| 308 : | mikkopa | 18 | if (sop != null) |
| 309 : | { | ||
| 310 : | localId = sop.LocalId; | ||
| 311 : | sid = sop.ParentGroup.LocalId.ToString(); | ||
| 312 : | } | ||
| 313 : | |||
| 314 : | if (chat.Sender != null) | ||
| 315 : | { | ||
| 316 : | mikkopa | 19 | ScenePresence sp = m_scriptEngine.World.GetScenePresence(chat.Sender.AgentId); |
| 317 : | mikkopa | 18 | if (sp != null) |
| 318 : | { | ||
| 319 : | localId = sp.LocalId; | ||
| 320 : | sid = chat.Sender.AgentId.ToString(); | ||
| 321 : | if (name == "" || name == null) name = chat.Sender.Name; | ||
| 322 : | } | ||
| 323 : | } | ||
| 324 : | |||
| 325 : | //if (chat.SenderObject != null) | ||
| 326 : | //{ | ||
| 327 : | // m_log.Info("sender is an "+chat.SenderObject.GetType()); | ||
| 328 : | //} | ||
| 329 : | |||
| 330 : | string eventParams = "\"listen\"," + localId + "," + chat.Channel.ToString() + "," + | ||
| 331 : | "\"" + name + "\"" + "," + "\"" + sid + "\"" + "," + "\"" + chat.Message + "\""; | ||
| 332 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + eventParams + ")"); |
| 333 : | mikkopa | 18 | //m_log.Info(eventParams); |
| 334 : | mikkopa | 12 | } |
| 335 : | } | ||
| 336 : | catch (Exception e) | ||
| 337 : | { | ||
| 338 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnRexScriptListen: " + e.ToString()); |
| 339 : | mikkopa | 12 | } |
| 340 : | } | ||
| 341 : | |||
| 342 : | mikkopa | 14 | public void OnRexClientStartUp(RexNetwork.RexClientView client, UUID agentID, string status) |
| 343 : | mikkopa | 12 | { |
| 344 : | try | ||
| 345 : | { | ||
| 346 : | mikkopa | 14 | string EventParams = "\"client_startup\",\"" + agentID.ToString() + "\",\"" + status + "\""; |
| 347 : | mikkopa | 19 | m_scriptEngine.ExecutePythonCommand("CreateEventWithName(" + EventParams + ")"); |
| 348 : | mikkopa | 12 | } |
| 349 : | catch (Exception e) | ||
| 350 : | { | ||
| 351 : | mikkopa | 18 | m_log.WarnFormat("[RexScriptEngine]: OnRexClientStartUp: " + e.ToString()); |
| 352 : | mikkopa | 12 | } |
| 353 : | } | ||
| 354 : | |||
| 355 : | |||
| 356 : | |||
| 357 : | |||
| 358 : | // TODO: Replace placeholders below | ||
| 359 : | // These needs to be hooked up to OpenSim during init of this class. | ||
| 360 : | // When queued in EventQueueManager they need to be LSL compatible (name and params) | ||
| 361 : | |||
| 362 : | //public void state_entry() { } // | ||
| 363 : | public void state_exit() { } | ||
| 364 : | //public void touch_start() { } | ||
| 365 : | public void touch() { } | ||
| 366 : | public void touch_end() { } | ||
| 367 : | public void collision_start() { } | ||
| 368 : | public void collision() { } | ||
| 369 : | public void collision_end() { } | ||
| 370 : | public void land_collision_start() { } | ||
| 371 : | public void land_collision() { } | ||
| 372 : | public void land_collision_end() { } | ||
| 373 : | public void timer() { } | ||
| 374 : | public void listen() { } | ||
| 375 : | public void on_rez() { } | ||
| 376 : | public void sensor() { } | ||
| 377 : | public void no_sensor() { } | ||
| 378 : | public void control() { } | ||
| 379 : | public void money() { } | ||
| 380 : | public void email() { } | ||
| 381 : | public void at_target() { } | ||
| 382 : | public void not_at_target() { } | ||
| 383 : | public void at_rot_target() { } | ||
| 384 : | public void not_at_rot_target() { } | ||
| 385 : | public void run_time_permissions() { } | ||
| 386 : | public void changed() { } | ||
| 387 : | public void attach() { } | ||
| 388 : | public void dataserver() { } | ||
| 389 : | public void link_message() { } | ||
| 390 : | public void moving_start() { } | ||
| 391 : | public void moving_end() { } | ||
| 392 : | public void object_rez() { } | ||
| 393 : | public void remote_data() { } | ||
| 394 : | public void http_response() { } | ||
| 395 : | |||
| 396 : | } | ||
| 397 : | |||
| 398 : | |||
| 399 : | } |
| ViewVC Help | |
| Powered by ViewVC 1.0.0 |

