R
Baldur schrieb:
Ich hab ja oben geschrieben, daß mein Savegame aus mehreren Datenblöcken besteht und jedes Objekt seinen eigenen Block hat, bzw. selbst dafür verantwortlich ist sich selbst zu speichern und zu laden. Wäre also auch Team-kompatibel.
trennt dein team wer welche objekte bearbeiten darf? bei uns arbeiten wir alle zusammen am code, von daher kann es sein, dass alles von jedem modifiziert wird.
Bei der Map ging es wirklich nur um irgendwelche Gamestates, Questflags, etc. und bei dem Skript bezog ich mich auch nur auf Skripte, die Questmechaniken, etc steuern. Da ists dann auch einfach ein "getVar" und "setVar" für die Skripte zu implementieren als jede Variable einzeln rüber zu hieven.
damit emulierst du quasi ein property system?
(bin mir noch nicht ganz sicher was du mit scripten und der map machst und wann)
An der Map selbst musste ich noch garnichts konvertieren, da hinzufügen von Keys ja eh ohne Probleme geht. Konvertierungen gibts dafür in den Objekt-Daten, die dort von den Objekten selbst behandelt werden.
das heisst, dein objekt waechst und waechst damit es ueber versionen hinweg konvertieren kann? du hast dann eine map, die string,string ist und zudem code der die strings dann noch modifiziert bzw die daraus extrahierten daten?
Das Ganze läuft übrigens auf Android und das Autosave ist fast nicht spürbar im Spiel
ach, das verstehst du unter 'groesseres projekt'
Wie läuft denn die Konvertierung wenn du structs verwendest?
struct SaveGame_V1 {
uint32_t hp;
uint32_t xp;
};
struct Savegame_V2 {
uint32_t hp;
uint64_t xp;
}
switch(version) {
case 1: {
SaveGame_V1 sg1;
SaveGame_V2 sg2;
read(&sg1);
sg2.hp = sg1.hp;
sg2.xp = sg1.xp;
...
break;
}
}
peuse code kann z.B. sein:
read(version)
read(block,size)//block -> raw daten
void* pCurrent=block;
void* pNext=tempblock;
switch(version)
case 1: (SVersion2*)pNext->InitFrom((SVersion1*)pCurrent);std::swap(pNext,pCurrent);
case 2: (SVersion3*)pNext->InitFrom((SVersion2*)pCurrent);std::swap(pNext,pCurrent);
case 3: (SVersion4*)pNext->InitFrom((SVersion3*)pCurrent);std::swap(pNext,pCurrent);
case 4: (SVersion5*)pNext->InitFrom((SVersion4*)pCurrent);std::swap(pNext,pCurrent);
...
}
return pCurrent;
Wird doch auch schnell unübersichtlich und fehleranfällig.
sieht mir uebersichtlich aus, einfach zu maintainen und sehr sicher (bezogen auf stabilitaet), wie konvertierst du von version1 bis version 100 wenn sich wertebereich, datentypen, layout etc. aendert?
Da doch eher gleich auf das Memory-Dump verzichten und die Variablen einzeln lesen.
SaveGame sg;
sg.hp = readUInt32();
switch(version) {
case 1:
sg.xp = readUInt32();
break;
case 2:
sg.xp = readUInt64();
break;
}
(ich weise darauf hin daß der Code als Pseudocode zu lesen ist)
damit behandelst du isoliert den einfachen fall dass sich ein datentyp aendert. was ist mit dem beispiel wo 5 leute an diesem code arbeiten und eben float sg.xp hinzugefuegt wird und falls der wert nicht vorhanden ist, 2.5 engenommen wird. dann berechnet man in der naechsten version, dass "mana" in etwa sg.xp * 6.7 ist und in der version die danach kommt, hat jemand sg.xp mit 1.6 skaliert. wenn du also version 0 laedst, musst du
case version0:
sg.xp = 2.5f
sg.mana = 16.75f
break;
case version1:
sg.xp = readFloat()
sg.mana = sg.xp*6.7f
break;
case version2:
sg.xp = readFloat()*1.6f
sg.mana = sg.xp*6.7f/1.6f
break;
case version3:
sg.xp = readFloat()
sg.mana = readFloat()
break;
das schaut mir fuer die kleine abhaengigkeit schon leicht unuebersichtlich aus, musst du dann pro version die du hinzufuegst, alle versionen die vorher potentiel geladen werden im switch case anpassen? steigert das dann dein maintainance mit O(VersionCount^2) ?