Bienvenue sur JeuxOnLine - MMO, MMORPG et MOBA !
Les sites de JeuxOnLine...
 

Panneau de contrôle

Recherche | Retour aux forums

JOL Archives

[Mondes Persistants] Editer les sauvegards

Par Gargantuel le 9/9/2002 à 11:01:10 (#2124525)

J'ai trouvé un post où on explique comment faire evoluer un monde persistant non pas en editant le module original mais la sauvegarde en elle-meme.
Par contre ca a l'air chaud de chez chaud ...
Quelqu'un a déjà essayé ce truc ?

Saved Game Editing Tutorial

Version 1.0

With this procedure I have successfully changed the placement of tiles, and insert/deleted NPC's and animals from a saved game. It's long and intricate and there are a lot of places where it can go wrong. As Derek French said in his post where I started this from, it's not for the faint of heart. That being said, lets dig in...

Step One: Save a game. I didn't try this on the official campaign, because Bioware has stated that they treat the official campaign differently then other modules, I tried this on my Sundabar ALFA module.

The saved game is going to be in the nwn/saves directory. Each saved game has a directory under that. In the saved game directory is the .sav file.


Step Two: Rename the .sav file to .mod Then you will need to copy the .mod file into your modules directory. Once in the modules directory, open the .mod file with the toolset. You have to be careful with this, as the .sav file will have the name of the module. You DO NOT want to overwrite you existing module. BE SURE to backup the original module before attempting any of this.

Step Three: Once you have the new .mod file in your modules directory, you are ready to fire up the toolset and make whatever changes you need to make.

Now while making these changes, make note of everything that you change.

Step Four: Once you are done making the changes, export them all. This will create .erf file in the directory of your choice.

Step Five: Once you have all the resources that you changed exported, then open that directory with windows explorer, and rename all the .erf files to .hak

Step Six: Now go to the saved game directory, and rename the .mod file in that directory to .hak

Step Seven: Once you have renamed that file, open it with the HakPak utility, located in the nwn/utils directory.

Step Eight: Export all the items in that .hak file HAK directory that you will need to create in the saved game directory.

Step Nine: Once all that information is exported, close this copy of the hak editor, and open a new copy. You will need to open each of the .hak files that you exported a few minutes ago from the toolset. Open each of these .hak files, and export them into the same directory that you just exported the contents of the saved game .hak If it asks to overwrite resources, just tell it yes.

When you have finished going through each of the .hak files from the toolset, close the hak editor.

Step Ten: Now reopen the saved game .hak file and remove all the resources that are in the file. You should have a blank file when you are done. Then click add, and readd all the resources that are in the HAK export directory that we created a minute ago. Just select them all, and then click open. This will add them all back, with the changed ones instead of the original ones from the saved game.

Step Eleven: Once you have done that save the file. After you have saved the file, close the hak editor.

Step Twelve: Now go back to windows explorer, and rename the saved game .hak file back to a .sav file.

You should now be able to load the saved game with NWN and your changes should now be reflected.

I haven't tested this all with PC's and the like, and I haven't messed with scripts updates or conversation updates, but they should be the same.

This should be a good jumping point for editing saved games, and making updates in a PW.

If I've completely screwed up this version, please let me know. I've tested, and been able to edit things about a dozen times before I posted this revision.

Good luck, and remember to backup everything that you care about before attempting ANY of the above...

Trismuss


Pour plus d'informations :
http://www.alandfaraway.net/tutorials/SavedGameEditing.asp
http://www.neises.com/forums/showthread.php?s=&threadid=2826
http://nwn.bioware.com/forums/viewtopic.html?topic=74251&forum=46

Par miriandel le 9/9/2002 à 16:42:35 (#2126724)

Selon moi, pas utilisable comme méthode.

Tu n'avais pas réussi un truc avec ton parseur ?

Par Kemay le 9/9/2002 à 21:28:50 (#2128691)

Bioware déconseille d'utiliser les sauvegardes pour modifier les modules. Et j'ai souvent entendu que ça menait à des sauvegardes corrompues. David Gaider a déjà dit plusieurs fois cependant qu'ils envisageaient éventuellement de faire un éditeur de sauvegarde officiel, mais cmme d'hab, demande beaucoup de travail, babla, peut être dans une extension blabla...

J'essaierai quand j'aurai un peu plus de temps et je te dirai ce que ça donne. Ce serait quand même chouette de ne plus avoir à passer par les "logs parsés" :)

Par Gargantuel le 10/9/2002 à 11:08:20 (#2130743)

Provient du message de miriandel
Selon moi, pas utilisable comme méthode.

Tu n'avais pas réussi un truc avec ton parseur ?

Si.
Mais ca n'est pas parfait.
La limitation du nombre d'instructions pouvant etre lancé par un script est galère de chez galère.
Pour info cette limitation a été mise en place pour stopper les scripts qui partent en boucle. Le problème c'est quand tu développes des fonctions qui appellent d'autres fonctions, tu as vite fait de l'atteindre cette limite :(
Ce qui m'oblige à faire des callscript pour spliter mes executions.

Bref c'est galère.

Par tonton le grognon le 10/9/2002 à 13:41:38 (#2131577)

je connaissais cette methode... mais j'ai jamais osé la mettre en pratique du fait que c'est compliqué.. et si tu te rates, tu perds ta sauvegarde (penser à faire une sauvergarder de la sauvegarde avant de la bricoler :D )

Par miriandel le 10/9/2002 à 14:55:41 (#2132037)

Je suis allé jeter un coup d'oeil au site de PWOM, ils ont semble-t-il un système qui fonctionne.

Hélas, ce système ne prend pas en charge la gestion des "placeables" semble-t-il.

Or, je ne peux pas concevoir un monde persistant où le PJ ne peut placer dans sa maison/château des coffres, lits, armoires, etc

Si le monde persistant ne doit que sauver des int/float/location ou des objets de l'inventaire du PJ, on n'a pas besoin d'un système de sauvegarde, il existe déjà.
Et on n'a pas besoin d'un monde persistant non plus, car où en est l'intérêt sinon de donner au PJ l'accès à la propriété (enfin ça, c'est mon point de vue).

Je me sens un peu paumé dans ce truc, de l'info circule dans tous les sens, mais j'arrive pas à tenir le début de la solution :aide:

Par Kemay le 10/9/2002 à 15:16:50 (#2132153)

Si le monde persistant ne doit que sauver des int/float/location ou des objets de l'inventaire du PJ, on n'a pas besoin d'un système de sauvegarde, il existe déjà.
En fait le premier problème lié au mondes persistants dans NWN est que le système de sauvegarde de variables aussi simples que des integer n'existe pas par défaut :( Le PWUM est effectivement un système qui fonctionne pour sauvegarder des variables de types int, float, strings, location et je crois que mad gnome a fait un add-on pour sauver les entrées du journal aussi.

Je suis tout à fait d'accord avec toi sur le fait qu'un monde persistant n'a pas d'intérêt sans la possibilité d'offrir un accès à la priorité. Cependant le PWUM te le permet plus au moins si tu fais quelques bidouillages. La propriété d'un placeable par exemple n'est qu'une variable locale sur cet objet qui définit le PC comme son propriétaire. En fait le problème que je vois et qui limite grandement les possibilités des mondes persistants, c'est l'impossibilité (en fait c'est possible mais extrêmement laborieux) de sauvegarder le contenu d'un container. En effet ça va lui faire une belle jambe au joueur de posséder une maison si a chaque fois que le serveur est relancé, tous ses coffres etc sont vides... L'autre aspect embêtant, c'est l'impossibilité (là encore en fait ce serait thériquement faisable de bidouiller pour contourner le problème mais extrêmement pénible) de récupérer un bluerprint par script. Ca permettrait de résoudre le problème ci-dessus, de faire des vrais cadavres de PCs lorsqu'ils décèdent, et de sauvegarder de façon "presque satisfaisante" les objets d'un module.

En conclusion, à moins qu'on ne puisse réellement éditer les sauvegardes de modules, il faudrait être extrêmement nombreux et motivés et se taper un enorme boulot long et ingrat pour pouvoir envisager de créer un monde persistant réellement intéressant ou alors avoir un nombre très restreint de joueurs de façon à pouvoir "personnaliser" le module joueur par joueur à chaque mise à jour. Enfin, en tout cas, c'est la conclusion à laquelle je suis arrivé et j'espère avoir tort :)

Par miriandel le 10/9/2002 à 16:53:41 (#2132780)

Oh et puis ça m'énerve ce truc, krot à la fin, je m'y colle :hardos:

Le truc que je vois/crois voir/crois commencer à voir/espère envisager de comprendre, est d'utiliser le OnEnter et OnExit des zones.

Exemple: j'ai une staff dans un coffre placé par le PJ, comment je sauve ça ?

1° Créer des blueprints pour tous les placeables autorisés (coffres, armoires, lits...) avec un tag commençant toujours par "PO_", et surtout un tag égal au blueprintresref pour que ces objets puissent être recréés par CreateObject

2° Quand le PJ quitte sa maison:


void EnSortantDeMaMaison()
{ // quand ce code est appele, on est toujours dans l'area
object PJ = GetExitingObject();
int i,y=0;
object maHouse = OBJECT_SELF;
object PO = GetFirstObjectInArea(maHouse);
while (GetIsObjectValid(PO)) { //ai-je des objets de type "PO" ?
if( (GetObjectType(PO) == OBJECT_TYPE_PLACEABLE) && (GetStringLeft(GetTag(PO), 2) == "PO") ) {
// sauver le nom et la position de l'objet
SetLocalString(PJ, "PO_"+IntToString(i), GetTag(PO));
SetLocalLocation(PJ, "POL_"+IntToString(i), GetLocation(PO));
// chercher les objets dans le container
object item = GetFirstItemInInventory(PO);
while (GetIsObjectValid(item)) { // stocker gentiment
SetLocalString(PJ, "PO_"+IntToString(i)+"_"+IntToString(y++), GetTag(item));
item = GetNextItemInInventory(PO);
};
i++;
};
PO = GetNextObjectInArea(maHouse);
};
}


3° Quand le PJ rentre dans sa maison
void EnEntrantDansMaMaison() {
object PJ = GetEnteringObject(); //home sweet home!
int i,y = 0;
string item;
object Container;
location POL;
// scanner le PJ pour trouver ses possessions. Ca chatouille un peu, pas grave.
string PO = GetLocalString(PJ, "PO_"+IntToString(i));
while (!(PO==")) {
POL = GetLocalLocation(PJ, "POL_"+IntToString(i));
Container = CreateObject(OBJECT_TYPE_PLACEABLE, PO, POL);
//ai-je affaire a un container contenant du contenu ?
item = GetLocalString(PJ, "PO_"+IntToString(i)+"_"+IntToString(y));
while (!(item==")) {
CreateItemOnObject(item, Container); //impasse sur le stacksize, plus tard
DeleteLocalString(PJ, "PO_"+IntToString(i)+"_"+IntToString(y++));
item = GetLocalString(PJ, "PO_"+IntToString(i)+"_"+IntToString(y));
}
DeleteLocalString(PJ, "PO_"+IntToString(i));
DeleteLocalLocation(PJ, "POL_"+IntToString(i++));
}
}


Ca paraît tellement simple, que je me demande si je viens pas de perdre le quart d'heure pour écrire ces scripts.

Absolument rien de testé évidemment, y a de l'idée ?

Par Skanzo Sylan le 10/9/2002 à 18:18:25 (#2133370)

Pourquoi PO_????

C'est pour les portraits ça...

heu...

OK Je sort, poumpouloum :cool:

Par Gargantuel le 10/9/2002 à 18:33:39 (#2133475)

Bon puisque Miriandel veut s'y coller voilà l'état d'avancement de mon boulot.

Systeme de sauvegarde/restauration des factions (états des factions les unes par rapport aux autres) :

Attention ce systeme s'appuie sur :
- PWUM
- mon systeme de log et mon parser XML (voir les autres posts)

/////////////////////////////
// FileName a_inc_factions //
/////////////////////////////
// Created By: Gargantuel //
// Created On: 02/08/2002 //
/////////////////////////////

// Doit imperativement s'executer avec au moins un joueur dans le module
void SaveFactions()
{
object oPC = GetFirstPC();
int iFactionSourceNumber=1;
string sTagFactions = ";
string sFactionLeft = "iza_faction";
string sFactionSourceName = sFactionLeft + IntToString(iFactionSourceNumber);
object sFactionSource = GetObjectByTag(sFactionSourceName);
while (sFactionSource != OBJECT_INVALID)
{
int iReputation = GetReputation(sFactionSource, oPC);
string sTag = BuildXMLTag(sFactionSourceName, IntToString(iReputation));
sTag = SetXMLParameter(sTag, "target", "PC");
sTagFactions = SetXMLTag(sTagFactions, sTag, 0, 1);

int iFactionTargetNumber=1;
string sFactionTargetName = sFactionLeft + IntToString(iFactionTargetNumber);
object sFactionTarget = GetObjectByTag(sFactionTargetName);
while (sFactionTarget != OBJECT_INVALID)
{
iReputation = GetReputation(sFactionSource, sFactionTarget);
sTag = BuildXMLTag(sFactionSourceName, IntToString(iReputation));
sTag = SetXMLParameter(sTag, "target", sFactionTargetName);
sTagFactions = SetXMLTag(sTagFactions, sTag, 0, 1);
iFactionTargetNumber++;
sFactionTargetName = sFactionLeft + IntToString(iFactionTargetNumber);
sFactionTarget = GetObjectByTag(sFactionTargetName);
}
iFactionSourceNumber++;
sFactionSourceName = sFactionLeft + IntToString(iFactionSourceNumber);
sFactionSource = GetObjectByTag(sFactionSourceName);
}
SetPWUMString("FACTIONS", sTagFactions);
WriteLog(sTagFactions, LOG_INFO);
}


// Doit imperativement s'executer avec au moins un joueur dans le module
void RestoreFactions()
{
object oPC = GetFirstPC();
string sTagFactions = GetPWUMString("FACTIONS");

int iIndex = MoveFirstXMLTag(sTagFactions);
string sTag = ";
string sFactionSourceName = ";
string sFactionTargetName = ";
int iReputation = 0;
string sSourceName = ";
while (iIndex != -1)
{
sTag = GetXMLTag(sTagFactions, ", iIndex);
if (sTag != "INVALID_TAG")
{
string sNewSourceName = GetXMLName(sTag);
if (sSourceName != sNewSourceName)
{
if (sSourceName != ")
DelayCommand(0.0, ExecuteScript("iza_rstfaction", GetObjectByTag(sSourceName)));
sSourceName = sNewSourceName;
}
object oSource = GetObjectByTag(sSourceName);
if (oSource == OBJECT_INVALID)
WriteLog(("[Restauration des factions] Object invalide pour le tag :" + sSourceName), LOG_ERROR);
string sBuffer = GetLocalString(oSource, "Faction") + sTag;
SetLocalString(oSource, "Faction", sBuffer);
}
iIndex = MoveNextXMLTag(sTagFactions, ", iIndex);
}
if (sSourceName != ")
DelayCommand(0.0, ExecuteScript("iza_rstfaction", GetObjectByTag(sSourceName)));

}

le script iza_rstfaction :

void main()
{
string sData = GetLocalString(OBJECT_SELF, "Faction");
string sFactionSourceName = ";
string sFactionTargetName = ";
int iReputation = 0;
string sSourceName = ";
string sTag = ";
int iIndex = MoveFirstXMLTag(sData);
WriteLog((GetTag(OBJECT_SELF) + " a en buffer :" + sData), LOG_INFO);
while (iIndex != -1)
{
sTag = GetXMLTag(sData, ", iIndex);
sFactionSourceName = GetXMLName(sTag);
sFactionTargetName = GetXMLParameterValue(sTag, "target");
iReputation = StringToInt(GetXMLValue(sTag));
WriteLog(("Restauration des factions : " +sFactionSourceName+"-"+sFactionTargetName+"="+IntToString(iReputation)), LOG_DEBUG);
AdjustReputation(GetObjectByTag(sFactionSourceName), GetObjectByTag(sFactionTargetName), iReputation);
iIndex = MoveNextXMLTag(sData, ", iIndex);
}
}


Grosso modo, ca utilise une zone inacessible (iza_faction) où se trouve 1 NPC par faction (mettez des murs entres les NPC pour eviter qu'ils se tapent dessus)
Les NPC doivent s'appeller iza_faction1, iza_faction2, etc ...

Si vous vous demandez pourquoi j'utilise le callscript iza_rstfaction c'est à cause de ce p**** de limitation d'instructions (ERROR: TOO MANY INSTRUCTIONS)

Voila c'est un peu indigeste, meme pour un scripteur mais avec ca un PW peut avoir un systeme de faction qui évolue et qui ne sera pas perdu à chaque modif du module.

Par Gargantuel le 10/9/2002 à 18:41:12 (#2133538)

On continue dans la lancée : Gestion des objets uniques :

Un objet unique est soit dans le module, soit dans l'inventaire d'un joueur mais pas les 2 ;)

Tout objet unique doit se terminer pas "_UNK".

Voici le script :


string UNIK_ITEM = "_UNK";
void MyAcquireItem(object oItem, object oItemAcquiredFrom)
{
WriteLog(GetTag(oItem), LOG_DEBUG);
if (GetStringRight(GetTag(oItem), 4) == UNIK_ITEM)
{
string sValue = ";
if (GetIsPC(oItemAcquiredFrom))
sValue = GetPCPlayerName(oItemAcquiredFrom);
else
sValue = GetTag(oItemAcquiredFrom);
string sTagName = GetTag(oItem);
string sTag = BuildXMLTag(sTagName, sValue);
string sData = GetPWUMString("Item");
SetPWUMString("Item", SetXMLTag(sData, sTag));
WriteLog(GetPWUMString("Item"), LOG_DEBUG);
}
}
void MyUnAcquireItem(object oItem)
{
WriteLog(GetTag(oItem), LOG_DEBUG);
if (GetStringRight(GetTag(oItem), 4) == UNIK_ITEM)
{
string sData = GetPWUMString("Item");
SetPWUMString("Item", RemoveXMLTag(sData, GetTag(oItem)));
WriteLog(GetPWUMString("Item"), LOG_DEBUG);
}
}


A appeler dans le acquire/unacquire du module.

A partir de la il faut ecrire un petit script qui lit les messages XML de la string sur le OnModuleLoad et qui detruit tous les objets possedés par les joueurs. Je prévois aussi de stocker la localisation des objets unique mais y a encore du boulot.

Par miriandel le 10/9/2002 à 20:09:51 (#2134114)

Ben, ben y a pas, si Bioware a pas réagi positivement d'ici la fin de la programmation de mon module, hop, faudra bien y passer :hardos:

Autre chose maintenant, tant qu'on est dans l'échevelé, si mes souvenirs sont bons, il interdisent la lecture d'un fichier externe pour éviter les virus.
Je me demande quand même s'il n'est pas possible de cracker le compilateur pour avoir accès au bon vieux iostream.h.
Aussi dit, aussitôt cherché, et... une référence à iostream existe dans NWNToolset.exe !

Y a bien un fou qui a cracké l'angle de caméra (ça laisse pantois quand même...), pourquoi pas le compilo ?

Oui, bon, on peut rêver, je retourne à mon module, ma semaine de vacances est en train d'y passer :rasta:

Par miriandel le 10/9/2002 à 20:53:36 (#2134431)

Je viens de trouver ça, aussi.

Ca a l'air pas mal son Itemizer, mais pas encore trouvé de download.
J'y ai vu des posts de Kemay, petit cachotier...

Par Kemay le 11/9/2002 à 0:04:54 (#2135586)

lol pas de cachotteries ;)

J'avais fait un système comme le token itemizer peu après la sortie de NWN. Cette histoire d'imposibilité de sauver des variables locales était la première chose qui m'a dérangé. J'ai fini par laissé tomber à cause des bugs sur les fonctions GetFirstObjectInInventory et CreateItemOnOnbject. Même si le système marchait, ça prenait jusqu'à 5-10 secondes pour sauvegarder une 50aine d'integer. Ces bugs semblent être fixés avec la 1.24, mais je ne suis toujours pas convaincu que cette solution ait un avantage sur celle des logs.

Par exemple ça ne permet que de sauvegarder des int, évetuellement des floats, mais dès qu'on veut passer aux strings ça devient trop compliqué pour être vraiment intéressant. Peut-être comme Knat le suggérait, un système cumulant les deux méthodes pourrait être intéressant. J'ai les scripts du token itemizer ainsi que ceux que j'avais faits à l'époque si tu veux y jeter un oeil. Mais avec la correction des bugs de la version 1.24 ces scripts deviennent obsolètes. Eventuellement je peux les réécrire en prenant en compte ces corrections.

Par miriandel le 11/9/2002 à 0:17:27 (#2135639)

Ah, ça me plairaît assez d'y jeter un oeil, si tu peux
me les mailer :merci:

Si j'ai bien compris, ce sytème évite de passer par une quelconque manipulation, les valeurs sont sauvées sur les persos du servervault ?
C'est ce qui me séduit dans ce système: pas de manips !

D'autant que je ne vois guère d'intention affichée chez Bioware de nous faciliter le travail... :maboule:

Par Kemay le 11/9/2002 à 0:36:51 (#2135715)

Provient du message de miriandel
Absolument rien de testé évidemment, y a de l'idée ?
Pour les placeables:
Effectivement il y a de l'idée :) Les détails qui me chiffonnent un peu cela dit:
- Je pense qu'il vaut mieux "sauver" les local strings sur le module (si le joueur crashe par exemple, tout serait perdu sinon) dans le style: SetLocalString(GetModule(), GetPCPlayerName(oPJ)+GetName(oPJ)+"PO_"+etc...); ou SetLocalString(GetModule(), GetPCPublicKey(oPJ)+GetName(oPJ)+"PO_"+etc...);
Personnellement je préfère GetPCPlayerName à GetPCPublicKey mais les deux sont uniques.
- L'autre chose, c'est que je sauvegarderai ces strings pas à la sortie/entrée dans une zone mais au moment où l'objet est déposé (onUnaquired, ou onDisturbed pour les containers, etc...)

Quand je parlais de travail long et ingrat, je pensais aussi principalement à un détail que tu soulèves, les blueprint et les tags ont besoin, si ce n'est d'être identiques, au moins que l'on puisse déterminer l'un en fonction de l'autre, et cela signifie refaire tous les objets avec un code de dénomination pour les tags et les blueprints ou faire des scripts retrouvant tous les blueprints à partir des tags... Et ça ça ava être long, et pénible à faire, très pénible...

Cela dit l'idée de mondes persistants dans NWN me séduit énormément,et peut-être que si on s'y met tous ensemble on arrivera à quelquechose. C'est vrai que pouvoir utiliser iostream.h serait du bonheur mais je doute que ça arrive un jour, ce serait autrement plus compliqué que de faire sauter les limites des angles de caméra, même si moi aussi ce camera hack m'a sidéré :)

Par Taern le 11/9/2002 à 18:40:30 (#2140231)

Effectivement la question vaut vraiment le coup d'être creusée, mais après avoir réfléchi à la solution de sauvegarder "manuellement" (par script en fait) les données, je me suis rendu compte que ce serais un enfer pour les mondes persistants : cela fait beaucoup trop de sacrifices (positions des monstres, état du monde, pleins d'autres choses "secondaires" perdues). La solution d'éditer les sauvegardes semble elle aussi très prometteuse (puisque une sauvegarde fait deja tout le boulot) mais apparemment encore très compliquée.

En attendant de trancher ;) j'aimerais quelques éclaircissements : d'abord, quand on sauvegarde, toutes les variables locales sont également sauvées ? Et c'est quoi ce mystérieux iostram.h ? ;)

-Taern

Peut-être une nouvelle piste

Par Kemay le 14/9/2002 à 8:49:00 (#2154265)

Zaddix a laissé un post intéressant sur le forum du persistent world centre :

Hello,

I've been a lurker here for a while now and have been following various development projects for true persistent local variables, world state, etc.

I was wondering if anyone has played with the module.ifo file located in the mod/sav file. Stored in this file are all of the local variables lists for a module (for creatures, areas, PCs, etc). I was thinking that a tool which copied all of these variable lists from the saved game module.ifo to a new build of a module (mod file) would solve the persistent data problem eloquently.

Any thoughts on this idea?

JOL Archives 1.0.1
@ JOL / JeuxOnLine