New Region Modules/de

From OpenSimulator

(Difference between revisions)
Jump to: navigation, search
(Created page with "{{Quicklinks|New_Region_Modules}} '''Hinweis:''' Diese Dokumentation ist für OpenSimulator Version 0.9.3.x aktualisiert, das auf .NET 8.0 Runtime läuft. = Übersicht = Di...")
 
 
Line 1: Line 1:
 
{{Quicklinks|New_Region_Modules}}
 
{{Quicklinks|New_Region_Modules}}
 +
 +
= New Region Modules =
  
 
'''Hinweis:''' Diese Dokumentation ist für OpenSimulator Version 0.9.3.x aktualisiert, das auf .NET 8.0 Runtime läuft.
 
'''Hinweis:''' Diese Dokumentation ist für OpenSimulator Version 0.9.3.x aktualisiert, das auf .NET 8.0 Runtime läuft.

Latest revision as of 01:53, 16 December 2025

Contents

[edit] New Region Modules

Hinweis: Diese Dokumentation ist für OpenSimulator Version 0.9.3.x aktualisiert, das auf .NET 8.0 Runtime läuft.

[edit] Übersicht

Diese Seite beschreibt das OpenSimulator Region-Modul-System, das eine flexible, erweiterbare Architektur zum Hinzufügen von Funktionalität zu Regionen bereitstellt. Region-Module ermöglichen es Entwicklern, OpenSim zu erweitern, ohne die Kern-Codebasis zu ändern, und unterstützen dynamisches Laden, Region-Neustarts und Hot-Swapping von Funktionalität.

[edit] Warum neue Region-Module?

Das vorherige RegionModule-System hatte mehrere Einschränkungen:

  • Inkonsistente API: Der Initialisierungs-Lebenszyklus unterstützte dynamisches Region-Management nicht richtig
  • Region-Neustart-Probleme: Das Hinzufügen oder Entfernen von Regionen zur Laufzeit war problematisch
  • Unklare Semantik: Die Beziehung zwischen Initialise und PostInitialise war mehrdeutig

Zum Beispiel, wenn eine neue Region hinzugefügt wurde:

  • Initialise nicht aufrufen: Modul ist für diese Region nicht initialisiert → fehlende Funktionalität
  • Initialise aufrufen: PostInitialise wurde bereits aufgerufen → inkonsistenter Zustand

Das neue System behebt diese Probleme mit einem gut definierten Lebenszyklus und klarer Semantik für geteilte vs. nicht-geteilte Module.

[edit] Modultypen

Das neue Region-Modul-System basiert auf drei Interfaces:

[edit] IRegionModuleBase

Das Basis-Interface, das alle Region-Module implementieren:

 public interface IRegionModuleBase
 {
     string Name { get; }
     Type ReplaceableInterface { get; }
     void Initialise(IConfigSource source);
     void Close();
     void AddRegion(Scene scene);
     void RegionLoaded(Scene scene);
     void RemoveRegion(Scene scene);
 }

[edit] Interface-Mitglieder

  • Name: Gibt den Namen des Moduls zurück (wird für Identifikation und Logging verwendet)
  • ReplaceableInterface: Gibt den Typ des Interfaces zurück, das dieses Modul bereitstellt (für Modul-Ersetzung)
  • Initialise: Wird einmal aufgerufen, wenn das Modul geladen wird
  • Close: Wird aufgerufen, wenn das Modul heruntergefahren wird
  • AddRegion: Wird aufgerufen, wenn eine Region zu diesem Modul hinzugefügt wird
  • RegionLoaded: Wird aufgerufen, nachdem alle Module zu einer Region hinzugefügt wurden
  • RemoveRegion: Wird aufgerufen, wenn eine Region von diesem Modul entfernt wird

[edit] ISharedRegionModule

Für Module, die einmal instanziiert werden und über alle Regionen hinweg geteilt werden:

 public interface ISharedRegionModule : IRegionModuleBase
 {
     void PostInitialise();
 }

Anwendungsfälle:

  • Zentrale Dienste (Benutzerverwaltung, Asset-Dienste, etc.)
  • Regions-übergreifende Funktionalität
  • Singleton-Pattern-Anforderungen
  • Ressourcenintensive Module, die nur einmal existieren sollten

Lebenszyklus:

  • Eine Instanz wird beim Serverstart erstellt
  • Initialise wird einmal aufgerufen
  • PostInitialise wird aufgerufen, nachdem alle geteilten Module initialisiert sind
  • AddRegion wird für jede Region aufgerufen
  • RegionLoaded wird für jede Region aufgerufen, nachdem alle Module hinzugefügt wurden
  • RemoveRegion wird aufgerufen, wenn Regionen entfernt werden
  • Close wird beim Server-Herunterfahren aufgerufen

[edit] INonSharedRegionModule

Für Module, die separat für jede Region instanziiert werden:

 public interface INonSharedRegionModule : IRegionModuleBase
 {
 }

Anwendungsfälle:

  • Regions-spezifische Funktionalität
  • Module, die regionsspezifischen Status verwalten
  • Module, die Isolation zwischen Regionen benötigen
  • Leistungskritische Module, die von Parallelisierung profitieren

Lebenszyklus:

  • Neue Instanz wird für jede Region erstellt
  • Initialise wird aufgerufen, wenn die Modul-Instanz erstellt wird
  • AddRegion wird unmittelbar nach der Initialisierung aufgerufen
  • RegionLoaded wird aufgerufen, nachdem alle Module zur Region hinzugefügt wurden
  • RemoveRegion wird aufgerufen, wenn die Region entfernt wird
  • Close wird nach dem Entfernen der Region aufgerufen

Hinweis: PostInitialise existiert nicht für nicht-geteilte Module, weil es keinen garantierten Punkt gibt, an dem alle Instanzen erstellt wurden (neue Regionen können jederzeit hinzugefügt werden).

[edit] Ein Region-Modul erstellen

[edit] Grundlegende Einrichtung

[edit] 1. Erforderliche Referenzen hinzufügen

Fügen Sie die notwendigen Namespaces in Ihren Code ein:

 using System;
 using Mono.Addins;
 using Nini.Config;
 using OpenSim.Region.Framework.Interfaces;
 using OpenSim.Region.Framework.Scenes;

Ihr Projekt benötigt Referenzen zu:

  • Mono.Addins.dll
  • Nini.dll
  • OpenSim.Region.Framework.dll
  • OpenSim.Framework.dll

[edit] 2. Assembly-Attribute

Fügen Sie diese Attribute zu Ihrer Assembly hinzu (typischerweise in Properties/AssemblyInfo.cs oder am Anfang Ihrer Haupt-Moduldatei):

 [assembly: Addin("MyModule", OpenSim.VersionInfo.VersionNumber + ".1")]
 [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]

Parameter:

  • Erster Parameter: Der eindeutige Name Ihres Moduls
  • Version: An OpenSim-Version gebunden für Kompatibilitätsverfolgung
  • AddinDependency: Deklariert Abhängigkeit von OpenSim.Region.Framework

[edit] 3. Modul-Klassen-Attribut

Markieren Sie Ihre Modul-Klasse mit dem Extension-Attribut:

 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "MyModuleId")]
 public class MyModule : ISharedRegionModule
 {
     // Implementierung
 }

Wichtig: Der Path und NodeName müssen genau wie gezeigt sein. Die Id sollte für Ihr Modul eindeutig sein.

[edit] Beispiel: Geteiltes Region-Modul

 using System;
 using System.Reflection;
 using log4net;
 using Mono.Addins;
 using Nini.Config;
 using OpenSim.Region.Framework.Interfaces;
 using OpenSim.Region.Framework.Scenes;
 
 [assembly: Addin("ExampleSharedModule", OpenSim.VersionInfo.VersionNumber + ".1")]
 [assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
 
 namespace MyNamespace
 {
     [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ExampleSharedModule")]
     public class ExampleSharedModule : ISharedRegionModule
     {
         private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
         private bool m_enabled = false;
         private List<Scene> m_scenes = new List<Scene>();
         
         public string Name 
         { 
             get { return "ExampleSharedModule"; } 
         }
         
         public Type ReplaceableInterface 
         { 
             get { return null; } 
         }
         
         public void Initialise(IConfigSource config)
         {
             IConfig moduleConfig = config.Configs["ExampleModule"];
             if (moduleConfig != null)
             {
                 m_enabled = moduleConfig.GetBoolean("Enabled", false);
                 m_log.InfoFormat("[EXAMPLE MODULE]: Enabled = {0}", m_enabled);
             }
         }
         
         public void PostInitialise()
         {
             if (!m_enabled)
                 return;
                 
             m_log.Info("[EXAMPLE MODULE]: PostInitialise aufgerufen");
             // Geteilte Ressourcen hier initialisieren
         }
         
         public void AddRegion(Scene scene)
         {
             if (!m_enabled)
                 return;
                 
             m_scenes.Add(scene);
             m_log.InfoFormat("[EXAMPLE MODULE]: Region {0} hinzugefügt", scene.RegionInfo.RegionName);
             
             // Interfaces registrieren
             scene.RegisterModuleInterface<IExampleModule>(this);
             
             // Events anhängen
             scene.EventManager.OnNewClient += OnNewClient;
         }
         
         public void RegionLoaded(Scene scene)
         {
             if (!m_enabled)
                 return;
                 
             m_log.InfoFormat("[EXAMPLE MODULE]: Region {0} geladen", scene.RegionInfo.RegionName);
             
             // Andere Module hier zugreifen
             // var otherModule = scene.RequestModuleInterface<IOtherModule>();
         }
         
         public void RemoveRegion(Scene scene)
         {
             if (!m_enabled)
                 return;
                 
             m_scenes.Remove(scene);
             
             // Events abhängen
             scene.EventManager.OnNewClient -= OnNewClient;
             
             // Interfaces abmelden
             scene.UnregisterModuleInterface<IExampleModule>(this);
             
             m_log.InfoFormat("[EXAMPLE MODULE]: Region {0} entfernt", scene.RegionInfo.RegionName);
         }
         
         public void Close()
         {
             m_log.Info("[EXAMPLE MODULE]: Wird heruntergefahren");
             // Geteilte Ressourcen aufräumen
         }
         
         private void OnNewClient(IClientAPI client)
         {
             // Neuen Client behandeln
         }
     }
 }

[edit] Beispiel: Nicht-geteiltes Region-Modul

 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ExampleNonSharedModule")]
 public class ExampleNonSharedModule : INonSharedRegionModule
 {
     private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
     private bool m_enabled = false;
     private Scene m_scene;
     
     public string Name 
     { 
         get { return "ExampleNonSharedModule"; } 
     }
     
     public Type ReplaceableInterface 
     { 
         get { return null; } 
     }
     
     public void Initialise(IConfigSource config)
     {
         IConfig moduleConfig = config.Configs["ExampleModule"];
         if (moduleConfig != null)
         {
             m_enabled = moduleConfig.GetBoolean("Enabled", false);
         }
     }
     
     public void AddRegion(Scene scene)
     {
         if (!m_enabled)
             return;
             
         m_scene = scene;
         m_log.InfoFormat("[EXAMPLE MODULE]: Zur Region {0} hinzugefügt", scene.RegionInfo.RegionName);
         
         scene.RegisterModuleInterface<IExampleModule>(this);
         scene.EventManager.OnFrame += OnFrame;
     }
     
     public void RegionLoaded(Scene scene)
     {
         if (!m_enabled)
             return;
             
         // Andere Module zugreifen
     }
     
     public void RemoveRegion(Scene scene)
     {
         if (!m_enabled)
             return;
             
         scene.EventManager.OnFrame -= OnFrame;
         scene.UnregisterModuleInterface<IExampleModule>(this);
         
         m_log.InfoFormat("[EXAMPLE MODULE]: Von Region {0} entfernt", scene.RegionInfo.RegionName);
     }
     
     public void Close()
     {
         // Regionsspezifische Ressourcen aufräumen
     }
     
     private void OnFrame()
     {
         // Regionsspezifische Frame-Verarbeitung
     }
 }

[edit] Modul-Lebenszyklus

[edit] Server-Start

1. Modul-Laden

  • Region-Server startet
  • Alle Modul-Klassen werden über Mono.Addins geladen (keine bestimmte Reihenfolge)

2. Geteilte Modul-Initialisierung

  • Für jedes ISharedRegionModule: Eine Instanz wird erstellt, Initialise wird aufgerufen
  • Für jedes ISharedRegionModule: PostInitialise wird aufgerufen

3. Scene-Erstellung (für jede Scene)

  • Für jedes INonSharedRegionModule: Neue Instanz wird erstellt, Initialise wird aufgerufen
  • Für alle Module (geteilt und nicht-geteilt): AddRegion(scene) wird aufgerufen
  • Module werden mit der Scene verknüpft
  • Für alle Module: RegionLoaded(scene) wird aufgerufen
  • An diesem Punkt können Module auf andere Module über scene.RequestModuleInterface<T>() zugreifen

[edit] Eine Region hinzufügen (Laufzeit)

  • Für jedes INonSharedRegionModule: Neue Instanz wird erstellt, Initialise wird aufgerufen
  • Für alle Module (geteilt und nicht-geteilt): AddRegion(scene) wird aufgerufen
  • Für alle Module: RegionLoaded(scene) wird aufgerufen

[edit] Eine Region entfernen

  • Für alle Module (geteilt und nicht-geteilt): RemoveRegion(scene) wird aufgerufen
  • Nur für INonSharedRegionModules: Close wird aufgerufen
  • Modul-Referenzen werden aus der Scene entfernt
  • INonSharedRegionModule-Instanzen können vom Garbage Collector erfasst werden

Hinweis: Aufgrund des Verhaltens der .NET/Mono-Laufzeitumgebung werden einige Ressourcen möglicherweise nicht sofort freigegeben, selbst bei ordnungsgemäßer Bereinigung.

[edit] Eine Region neu starten

Ein Region-Neustart wird implementiert als: 1. Region entfernen 2. Region hinzufügen

Avatare in der Region werden während des Neustarts getrennt.

Wichtig: Aufgrund von .NET/Mono-Laufzeitbeschränkungen erfolgt möglicherweise kein echtes Entladen von Modulen. Einige Ressourcen können über Neustarts hinweg im Speicher verbleiben.

[edit] Server-Herunterfahren

  • Für jede Scene: Region entfernen-Schritte werden durchgeführt
    • RemoveRegion wird für alle Module aufgerufen
    • Close wird für alle INonSharedRegionModules aufgerufen
  • Close wird für alle ISharedRegionModules aufgerufen
  • Module können vom Garbage Collector erfasst werden

[edit] Best Practices

[edit] Konfiguration

Verwenden Sie das enabled-Muster für Module, die optional sein sollten:

 [ExampleModule]
     Enabled = true
     ; Weitere Einstellungen...

Überprüfen Sie dies in Initialise und kehren Sie früh zurück, wenn deaktiviert:

 public void Initialise(IConfigSource config)
 {
     IConfig cfg = config.Configs["ExampleModule"];
     if (cfg == null || !cfg.GetBoolean("Enabled", false))
         return;
         
     m_enabled = true;
     // Initialisierung fortsetzen...
 }

[edit] Ressourcenverwaltung

  • In RemoveRegion aufräumen: Event-Handler abmelden, Interfaces abmelden
  • In Close aufräumen: Ressourcen freigeben, Verbindungen schließen
  • Operationen abgleichen: Was Sie in AddRegion hinzufügen, entfernen Sie in RemoveRegion
  • Memory Leaks vermeiden: Immer Event-Handler abhängen, um Referenzen zu vermeiden

[edit] Modul-Abhängigkeiten

Greifen Sie auf andere Module in RegionLoaded zu, nicht in AddRegion:

 public void RegionLoaded(Scene scene)
 {
     // Sicher - alle Module wurden hinzugefügt
     IOtherModule other = scene.RequestModuleInterface<IOtherModule>();
     if (other != null)
     {
         // Modul verwenden
     }
 }

[edit] Modultyp wählen

Verwenden Sie ISharedRegionModule wenn:

  • Modul Singleton-Dienste bereitstellt
  • Modul regionsübergreifende Funktionalität verwaltet
  • Modul ressourcenintensiv ist und nur einmal existieren sollte
  • Beispiel: Asset-Service, Benutzerverwaltung

Verwenden Sie INonSharedRegionModule wenn:

  • Modul regionsspezifischen Status verwaltet
  • Modul-Funktionalität regionsspezifisch ist
  • Modul von Isolation zwischen Regionen profitiert
  • Beispiel: Terrain-Modul, Vegetations-Modul

[edit] ReplaceableInterface

Verwenden Sie ReplaceableInterface, um die Ersetzung Ihres Moduls zu ermöglichen:

 public Type ReplaceableInterface 
 { 
     get { return typeof(IMyModuleInterface); } 
 }

Dies ermöglicht es Drittanbieter-Modulen, Ihre Standard-Implementierung zu ersetzen.

[edit] Bereitstellung

[edit] Manuelle Bereitstellung

1. Bauen Sie Ihre Modul-Assembly 2. Kopieren Sie die .dll in den bin-Ordner von OpenSim 3. Starten Sie OpenSim neu 4. Modul wird automatisch über Mono.Addins entdeckt und geladen

[edit] Verwendung von Prebuild

Für die Integration in das Build-System von OpenSim erstellen Sie einen prebuild.xml-Eintrag:

 <Project name="OpenSim.Region.Modules.Example" path="addon-modules/ExampleModule" type="Library">
   <Configuration name="Debug">
     <Options>
       <OutputPath>../../bin/</OutputPath>
     </Options>
   </Configuration>
   
   <ReferencePath>../../bin/</ReferencePath>
   <Reference name="Mono.Addins"/>
   <Reference name="Nini"/>
   <Reference name="log4net"/>
   <Reference name="OpenSim.Region.Framework"/>
   <Reference name="OpenSim.Framework"/>
   
   <Files>
     <Match pattern="*.cs" recurse="true"/>
   </Files>
 </Project>

[edit] Verwendung von .NET SDK-Projekten

Moderne .NET 8.0-Projekte können SDK-Style .csproj verwenden:

 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <TargetFramework>net8.0</TargetFramework>
     <OutputPath>../../bin/</OutputPath>
     <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
   </PropertyGroup>
   
   <ItemGroup>
     <Reference Include="Mono.Addins" HintPath="../../bin/Mono.Addins.dll" />
     <ProjectReference Include="../../OpenSim/Region/Framework/OpenSim.Region.Framework.csproj" />
   </ItemGroup>
 </Project>

[edit] Fehlerbehebung

[edit] Modul wird nicht geladen

  • Überprüfen Sie, ob das Extension-Attribut korrekt ist
  • Prüfen Sie, ob Assembly-Attribute vorhanden sind
  • Stellen Sie sicher, dass die Mono.Addins.dll-Referenz korrekt ist
  • Überprüfen Sie das OpenSim-Log auf Addin-Ladefehler
  • Überprüfen Sie, ob die .dll im bin-Verzeichnis liegt

[edit] Modul nicht gefunden

  • Überprüfen Sie, ob die Name-Eigenschaft des Moduls mit der Konfiguration übereinstimmt
  • Überprüfen Sie, ob das Modul in der Konfiguration aktiviert ist
  • Prüfen Sie auf Tippfehler im Extension-Id-Attribut

[edit] Methoden werden nicht aufgerufen

  • Überprüfen Sie, ob die Interface-Implementierung vollständig ist
  • Prüfen Sie, ob m_enabled oder ähnliche Flags die Ausführung nicht verhindern
  • Stellen Sie sicher, dass das Modul die Konfigurationsprüfungen in Initialise bestanden hat

[edit] Memory Leaks

  • Hängen Sie alle Event-Handler in RemoveRegion ab
  • Melden Sie alle Modul-Interfaces ab
  • Leeren Sie Collections und Referenzen in Close
  • Verwenden Sie WeakReference für langlebige Referenzen, wenn angemessen

[edit] .NET 8.0 spezifische Probleme

  • Stellen Sie sicher, dass alle referenzierten Assemblies .NET 8.0-kompatibel sind
  • Prüfen Sie auf veraltete API-Verwendung
  • Überprüfen Sie, ob async/await-Muster korrekt verwendet werden

[edit] Erweiterte Themen

[edit] Modul-Ersetzung

Implementieren Sie ReplaceableInterface, um Modul-Ersetzung zu ermöglichen:

 public Type ReplaceableInterface 
 { 
     get { return typeof(IMyService); } 
 }

Der Loader wird: 1. Alle Module laden 2. Für Module mit ReplaceableInterface die Aktivierung aufschieben 3. Nur aktivieren, wenn kein anderes Modul dieses Interface registriert hat

[edit] Modul-übergreifende Kommunikation

Verwenden Sie Modul-Interfaces für die Kommunikation:

 // Interface definieren
 public interface IMyModule
 {
     void DoSomething();
 }
 
 // Im Modul implementieren
 public class MyModule : ISharedRegionModule, IMyModule
 {
     public void AddRegion(Scene scene)
     {
         scene.RegisterModuleInterface<IMyModule>(this);
     }
     
     public void DoSomething()
     {
         // Implementierung
     }
 }
 
 // Von einem anderen Modul aus verwenden
 public void RegionLoaded(Scene scene)
 {
     IMyModule myMod = scene.RequestModuleInterface<IMyModule>();
     if (myMod != null)
         myMod.DoSomething();
 }

[edit] Leistungsüberlegungen

  • Geteilte Module: Geringerer Speicher-Overhead, potenzielle Engpässe
  • Nicht-geteilte Module: Höhere Speichernutzung, bessere Parallelisierung
  • Event-Handler: Seien Sie effizient, vermeiden Sie blockierende Operationen
  • Asynchrone Operationen: Verwenden Sie Task-basierte async für langläufige Operationen

[edit] Siehe auch

[edit] Referenzen

Personal tools
General
About This Wiki