New Region Modules/de
From OpenSimulator
(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]
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]
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
- Developing OpenSim Addins - Erstellen von Addon-Paketen
- Installing 3rd party addins - Installation von Addons
- OpenSimulator Wiki - Hauptdokumentation