Enthält eine Header-Datei und eine Datenbank, um SKSE DLL-Plugins einfach unabhängig von der Version zu machen.
WICHTIG!Dies ist jetzt in zwei Versionen aufgeteilt: Special Edition (1.5.x) und Anniversary Edition (1.6.x). Die IDs, die auf Adressen verweisen, stimmen in diesen beiden Versionen nicht überein (die ausführbare Datei des Spiels ist zu unterschiedlich, um eine Übereinstimmung zu erzielen, und selbst wenn eine Übereinstimmung zustande käme, wäre der Code innerhalb dieser Funktionen trotzdem unterschiedlich).BeschreibungFür normale Mod-Benutzer:Laden Sie das All-in-One-Paket aus dem Dateibereich herunter und installieren Sie es. Sie können den Mod-Manager verwenden oder es manuell durchführen. Die .bin-Dateien sollten hier abgelegt werden:
Daten/SKSE/Plugins/Den Rest müssen Sie nicht lesen.
Für Autoren des SKSE-DLL-Plugins:Dies ist eine Modder-Ressource (eine Header-Datei). Sie können eine Datenbank laden, die Offsets speichert, sodass Ihr DLL-Plugin versionsunabhängig ist, ohne neu kompiliert werden zu müssen. Die Header-Datei kann im optionalen Bereich der Dateien heruntergeladen werden. Für die Anniversary Edition heißt die Header-Datei versionlibdb.h statt versiondb.h! Wenn Sie CommonLib verwenden, ist dies alles bereits integriert und Sie benötigen hier nichts weiter.
AnwendungDer schnellste Weg:
Spoiler:
Zeigen
#include "versiondb.h"
void * MeineAdresse = NULL;
vorzeichenlos lang lang MyOffset = 0;
bool InitializeOffsets()
{
// Auf dem Stapel zuweisen, damit er entladen wird, wenn wir diese Funktion beenden.
// Es ist nicht erforderlich, die gesamte Datenbank zu laden und ohne Grund Speicher zu verbrauchen.
VersionDb db;
// Datenbank mit der aktuellen ausführbaren Version laden.
wenn (!db.Load())
{
_FATALERROR("Die Versionsdatenbank für die aktuelle ausführbare Datei konnte nicht geladen werden!");
gibt false zurück;
}
anders
{
// "SkyrimSE.exe", "1.5.97.0"
_MESSAGE("Datenbank für %s Version %s geladen.", db.GetModuleName().c_str(), db.GetLoadedVersionString().c_str());
}
// Diese Adresse enthält bereits die Basisadresse des Moduls, sodass wir die Adresse direkt verwenden können.
MeineAdresse = db.FindAddressById(123);
if (MeineAdresse == NULL)
{
_FATALERROR("Adresse konnte nicht gefunden werden!");
gibt false zurück;
}
// Dieser Offset enthält nicht die Basisadresse. Die tatsächliche Adresse wäre ModuleBase + MyOffset.
if (!db.FindOffsetById(123, MeinOffset))
{
_FATALERROR("Offset für mein Ding konnte nicht gefunden werden!");
gibt false zurück;
}
// Alles war erfolgreich.
gibt true zurück;
}
Jetzt fragen Sie sich, was der Wert „123“ bedeutet. Dies ist die ID einer Adresse. Datenbanken mit unterschiedlichen Versionen haben zwar dieselbe ID für eine Adresse, diese kann aber auf unterschiedliche Werte verweisen. So erhalten Sie eine Liste aller ID-Wert-Paare für eine bestimmte Version:
Spoiler:
Zeigen
#include "versiondb.h"
bool DumpSpecificVersion()
{
VersionDb db;
// Versuchen Sie, die Datenbank der Version 1.5.62.0 zu laden, unabhängig von der laufenden ausführbaren Version.
wenn (!db.Load(1, 5, 62, 0))
{
_FATALERROR("Fehler beim Laden der Datenbank für 1.5.62.0!");
gibt false zurück;
}
// Schreiben Sie eine Datei mit dem Namen offsets-1.5.62.0.txt, wobei jede Zeile die ID und den Offset enthält.
db.Dump("offsets-1.5.62.0.txt");
_MESSAGE("Gedumpte Offsets für 1.5.62.0");
gibt true zurück;
}
Geben Sie anstelle von 1, 5, 62 oder 0 die Version ein, die Sie umkehren und mit der Sie vertraut sind. Sie müssen zuerst die entsprechende Datenbankdatei im Verzeichnis /Data/SKSE/Plugins haben.
Nach dem Aufruf sollte im Hauptverzeichnis von Skyrim eine neue Datei mit dem Namen "offsets-1.5.62.0.txt" oder einem beliebigen anderen Dateinamen vorhanden sein. Die Datei hat folgendes Format:
Dezimal-ID
Hex-Versatz
Wenn Sie beispielsweise in 1.5.62.0 eine Adresse 142F4DEF8 (statischer Zeiger des Spielercharakters) haben, die Sie versionsunabhängig machen möchten, gehen Sie wie folgt vor:
1. Suchen Sie in der Offset-Datei nach 2F4DEF8. Denn dies ist der Offset ohne die Basis 140000000
2. Achten Sie darauf, dass die ID 517014 (dezimal!) ist.
3. Wenn Sie diese Adresse zur Laufzeit in Ihrer DLL haben möchten, gehen Sie wie folgt vor:
void* addressOf142F4DEF8 = db.FindAddressById(517014);
Und da haben Sie es.
Die VersionDb-Struktur hat die folgenden Funktionen:
Spoiler:
Zeigen
bool Dump(const std::string& path); // Aktuell geladene Datenbank in Datei ausgeben
bool Load(int major, int minor, int revision, int build); // Laden Sie eine bestimmte Version, wenn die Datei db-major-minor-revision-build.bin im Verzeichnis Data/SKSE/Plugins vorhanden ist
bool Load(); // Lädt die Version für die aktuelle Anwendung
void Clear(); // Aktuell geladene Datenbank löschen
void GetLoadedVersion(int& major, int& minor, int& revision, int& build) const; // Ruft die Version der Datenbankdatei ab, die wir gerade geladen haben
bool GetExecutableVersion(int& major, int& minor, int& revision, int& build) const; // Ruft die Version der aktuell ausgeführten Anwendung ab
const std::string& GetModuleName() const; // Ruft den Namen des aktuell geladenen Datenbankmoduls ab, dies sollte „SkyrimSE.exe“ anzeigen
const std::string& GetLoadedVersionString() const; // Holt die aktuell geladene Version als String, zB "1.5.62.0"
const std::map& GetOffsetMap() const; // Holen Sie sich die Karte von ID zu Offset, wenn Sie sie manuell iterieren müssen
void* FindAddressById(unsigned long long id) const; // Adresse nach ID suchen, dies enthält bereits die Basis und ist die korrekte Adresse. Bei Nichtgefunden wird NULL zurückgegeben!
bool FindOffsetById(unsigned long long id, unsigned long long& result) const; // Offset nach ID suchen, dies ist nur der Offset ohne eingeschlossene Basis.
bool FindIdByAddress(void* ptr, unsigned long long& result) const; // ID anhand der Adresse suchen, dies versucht eine umgekehrte Suche, um die Adresse in eine ID umzuwandeln
bool FindIdByOffset(unsigned long long offset, unsigned long long& result) const; // ID nach Offset suchen, dies versucht eine umgekehrte Suche, um den Offset in eine ID umzuwandeln
Was Sie wissen und beachten sollten:
1. Sie können beliebige (oder alle) Datenbankdateien in Ihr Plugin einbinden, dies kann jedoch die Dateigröße erheblich erhöhen (um ca. 2,5 MB). Bisher war es üblich, diesen Mod stattdessen als Abhängigkeit zu kennzeichnen.
2. Sie sollten die Datenbank IMMER nur einmal beim Start laden, die benötigten Adressen initialisieren/zwischenspeichern und sie entladen lassen. Das Entladen bedeutet lediglich, dass die VersionDb-Struktur gelöscht wird oder verloren geht (sofern Sie sie auf dem Stack allokiert haben). Dadurch wird sichergestellt, dass Sie während der Spiellaufzeit nicht unnötig viel Speicher verbrauchen. Es ist nicht notwendig, die Datenbank während des Spiels geladen zu lassen. Dies ist ein strittiger Punkt, wenn Sie CommonLib verwenden, da diese die Datenbank nur einmal und nicht für jede DLL lädt.
3. Die Datenbank enthält Adressen von Funktionen, globalen Variablen, RTTI, vtables und allen anderen Elementen, auf die möglicherweise verwiesen wird. Sie enthält keine Adressen, die sich in der Mitte von Funktionen oder globalen Variablen befinden. Wenn Sie eine Adresse in der Mitte einer Funktion benötigen, sollten Sie die Basisadresse der Funktion ermitteln und den zusätzlichen Offset selbst hinzufügen. Sie enthält auch keine unnötigen Informationen wie die Ausrichtung um Funktionen (auf die in Rdata verwiesen wird). Der Pdata-Abschnitt wird verworfen, und einige vom Compiler generierte SEH-Informationen aus Rdata werden verworfen.
4. Sie sollten immer das Ergebnis überprüfen, um sicherzustellen, dass die Datenbank erfolgreich geladen wurde (bool Load hat „true“ zurückgegeben) und dass die abgefragten Adressen tatsächlich ein gültiges Ergebnis (nicht NULL) zurückgegeben haben. Wenn das Laden fehlschlägt, bedeutet dies höchstwahrscheinlich, dass die Datei fehlte oder die falsche Version verwendet wurde (z. B. beim Versuch, den SE-Header in AE zu verwenden). Wenn die Abfrage fehlschlägt, bedeutet dies, dass die Adresse in dieser Version nicht gefunden werden konnte. Dies könnte bedeuten, dass sich entweder der Spielcode so stark geändert hat, dass die Adresse für diese Version überhaupt nicht mehr gültig ist, ODER dass die Datenbank selbst die richtige Adresse nicht erkannt hat. In einem dieser Fälle sollten Sie die Plugin-Initialisierung abbrechen, um SKSE mitzuteilen, dass das Laden nicht korrekt war. Oder Sie zeigen manuell eine Fehlermeldung an.
5. Überprüfen Sie vor der Veröffentlichung Ihres DLL-Plugins, ob die Adresse in allen Versionen des Spiels vorhanden ist. Laden Sie dazu jede Version der Datenbankdatei und fragen Sie in jeder Version die gleiche Adress-ID ab, um sicherzustellen, dass sie vorhanden ist:
Spoiler:
Zeigen
bool LoadAll(std::Vektor& alle)
{
statische int-Versionen[] = { 3, 16, 23, 39, 50, 53, 62, 73, 80, 97, -1 };
für (int i = 0; Versionen[i] >= 0; i++)
{
VersionDb * db = neue VersionDb();
wenn (!db->Load(1, 5, Versionen[i], 0))
{
Datenbank löschen;
gibt false zurück;
}
all.push_back(db);
}
gibt true zurück;
}
bool ExistsInAll(std::vector& alle, unsignierte lange lange ID)
{
vorzeichenloses langes langes Ergebnis = 0;
für (auto db: alle)
{
if (!db->FindOffsetById(id, result))
gibt false zurück;
}
gibt true zurück;
}
void FreeAll(std::Vektor& alle)
{
für (auto db: alle)
Datenbank löschen;
alles löschen();
}
bool IsOk()
{
std::Vektoralle;
if (!LoadAll(alle))
{
_FATALERROR("Fehler beim Laden einer oder mehrerer Versionsdatenbanken für die aktuelle ausführbare Datei!");
FreeAll(alle);
gibt false zurück;
}
if (!ExistsInAll(alle, 517014))
{
_FATALERROR("517014 existiert nicht in allen Versionen der Datenbank!");
FreeAll(alle);
gibt false zurück;
}
FreeAll(alle);
// OK!
gibt true zurück;
}
Auf diese Weise können Sie sicher sein, dass Ihr DLL-Mod in allen Versionen funktioniert. Wenn er in einigen Versionen nicht funktioniert, können Sie dies auf Ihrer Mod-Seite schreiben.
6. Manchmal müssen Sie je nach laufender Spielversion etwas anderes tun. Das können Sie mit diesem Code-Schnipsel tun:
Spoiler:
Zeigen
int major = 0, minor = 0, revision = 0, build = 0;
if (!db.GetExecutableVersion(Hauptversion, Nebenversion, Revision, Build))
{
_FATALERROR("Etwas ist schiefgelaufen!");
gibt false zurück;
}
// Das laufende Spiel ist 1.5.x und mindestens Version 1.5.39.0
wenn (Hauptversion == 1 && Nebenversion == 5 && Revision >= 39)
{
// Sachen ... ?
}
7. Bitte beachten Sie: Wenn Sie Ihre SKSE-DLL im Debug-Modus kompilieren, kann die Ladezeit der Datenbank etwa 14 Sekunden betragen! Im Release-Modus beträgt sie etwa 0,2 Sekunden. Dies liegt daran, dass Standardbibliothekscontainer in diesem Modus sehr langsam sind (std map).
Berechtigungen
Mach, was du willst.