See site in english Voir le site en francais
Website skin:
home  download  forum  link  contact

Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length

Author Topic: [Tutorial] Sauver les scenarios c'est pas si simple+solution !!!  (Read 6061 times)

0 Members and 1 Guest are viewing this topic.

Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
26 August 2007, 20:03:08

Cliquez le lien ci-dessous pour retourner au sommaire des "tutorials pour créer une DLL Orbiter"
http://orbiter.dansteph.com/forum/index.php?topic=6335.msg95352#msg95352


Sauver les scenarios c'est pas si simple+solution !!!


Au menu:

1-Introduction
2-Une classe pour notre salut
3-Pratique: installation et utilisation de la classe "SaveRestoreScenario"
4-Comment ca marche ? le principe ? en savoir plus ?
5-De l'utilité des fonctions multi-usage




Introduction:

Quand on fait un addon, vient le moment de sauver nos variables pour pouvoir recharger les scenario.
Quand on regarde l'exemple du ShuttleA c'est gentillet, ca mange pas de pain, facile quoi.
(bien que le code sois inutilement compliqué mais bon)

Code: [Select]
// --------------------------------------------------------------
// Read status from scenario file
// --------------------------------------------------------------

void ShuttleA::clbkLoadStateEx (FILEHANDLE scn, void *vs)
{
char *line;
while (oapiReadScenario_nextline (scn, line)) {
if (!strnicmp (line, "[glow=yellow,2,300]PODANGLE[/glow]", 8 )) {
sscanf (line+8, "%lf%lf", pod_angle+0, pod_angle+1);
} else if (!strnicmp (line, "[glow=yellow,2,300]DOCKSTATE[/glow]", 9 )) {
sscanf (line+9, "%d%lf", &dock_status, &dock_proc);
} else if (!strnicmp (line, "[glow=yellow,2,300]AIRLOCK[/glow]", 7)) {
sscanf (line+7, "%d%lf", &lock_status, &lock_proc);
} else {
ParseScenarioLineEx (line, vs);
// unrecognised option - pass to Orbiter's generic parser
}
}
}
// --------------------------------------------------------------
// Write status to scenario file
// --------------------------------------------------------------

void ShuttleA::clbkSaveState (FILEHANDLE scn)
{
char cbuf[256];
// default vessel parameters
VESSEL2::clbkSaveState (scn);
// custom parameters
sprintf (cbuf, "%0.4f %0.4f", pod_angle[0], pod_angle[1]);
oapiWriteScenario_string (scn, "[glow=yellow,2,300]PODANGLE[/glow]", cbuf);
sprintf (cbuf, "%d %0.4f", dock_status, dock_proc);
oapiWriteScenario_string (scn, "[glow=yellow,2,300]DOCKSTATE[/glow]", cbuf);
sprintf (cbuf, "%d %0.4f", lock_status, lock_proc);
oapiWriteScenario_string (scn, "[glow=yellow,2,300]AIRLOCK[/glow]", cbuf);
}


Gentil ? oui  mais ca sauve que trois variables ce code la, tout ça pour 3 valeurs ? En plus le save et le load
doivent etre strictement identique, la moinde erreur et c'est la couille.
On continue sur le fait qu'Orbiter utilise une méthode de lecture totalement innefficace et lente qui peu occasionner des milliers de tests pour quelques dizaines de variables.
Et pour finir ca donne des scénarios long comme des jours sans pain (qui ce souvient des scenarios des DGIII?)

-difficulté de maintien d'ecriture et de lecture du code
-Lenteur de devellopement, prise de tête
-Risque d'erreur très élevé.
-innefficacité, lenteur de chargement
-Long scenarios

Le seul avantage c'est qu'on peut "éditer" les scenario à la main, mais avez vous vraiment envie que le gars
tripote dans les valeurs de vos système étroitement lié les un aux autres ? Avec l'éditeur de scenario est il vraiment
encore utile de laisser c'est possibilité à un prix aussi grand ?


En bref vous tenez autant que ca a finir avec un code de chargement comme le DGIII ci-dessous ?
En plus il manque un tiers (image trop grande), et je n'ai pas mis la fonction save qui fait la même longueurs.... :trucdeouf:




Oui l'exemple ShuttleA était gentillet mais vous serez tout d'accord avec moi pour pousser maintenant un énorme hurlement d'horreur !!!!


NOOOOOOOOOOOOOOOOOOOOOOOOOOONNNNNNNNNNNNNNNNNNNNNN !!! :wall:



Message modifié ( 29-05-2008 21:32 )


Offline Schimz

  • Legend
  • ******
  • Posts: 1598
  • Karma: 1
Reply #1 - 26 August 2007, 20:10:19
En voilà un tuto que j'attendais :)



Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #2 - 26 August 2007, 20:28:22
Une classe pour notre salut

Heureusement tonton Dan ne va pas vous laisser dans la panade, fidèle au bon principe du programmeur "Si t'es
pas feignant t'es vraiment un gland" (désolé, la rime ;) ) je vous fournis une classe toutes prête qui vous permettras de sauver des centaines de variables en quelques lignes. La classe m'a pris deux heure à écrire. J'aurais du la faire beaucoup plus tôt. :pfff:

Avantages:

-Permet la sauvegarde de 1000 variables "compressée" et 1000 variables "ancien systeme"
-"Compresse" la sauvegarde, au lieu d'une ligne par variable des dizaine de variables par ligne.
-Facilité d’écriture: une fonction par variable pour la déclarer comme "à sauver et restorer" et le reste est automatique.
-Facilité de lecture
-Compatibilité ascendante: chaque variable à une ID unique, les vieux scénarios des versions précédente de vos addons marcheront toujours et serons converti automatiquement.
-Robustesse: écriture et lecture rigoureusement identique sans effort.
-Possibilité de garder des variables "éditable" selon l'ancien système (nom + variable)
-Simplicité

Principe d'utilisation:

Au tout début du code dans clbkSetClassCap ou une fonction appelée par clbkSetClassCap vous donner à l'aide d'une fonction l'ID et la variable à sauvegarder:

Code: [Select]
Scn.SavedVariable(1, &NombreTotalEquipage);
Scn.SavedVariable(2, &QuantiteOxygeneCabine);
Scn.SavedVariable(3, &QuantiteCo2EnCabine);

Et voila, autre chose ? non, c'est tout !

Sauvegarde et chargement des variables déclarée ainsi ce feront automatiquement. Cette classe offre en plus  la possibilité de sauver des variables "non compressée" avec un nom pour garder la possibilité d'édition:

Code: [Select]
Scn.SavedVariable("[glow=yellow,2,300]NombreEquipage[/glow]", &NombreTotalEquipage);
Scn.SavedVariable("[glow=yellow,2,300]QuantiteOxygene[/glow]", &QuantiteOxygeneCabine);
Scn.SavedVariable("[glow=yellow,2,300]QuantiteCo2[/glow]", &QuantiteCo2EnCabine);

A ne pas abuser, à part la simplicité on retombe dans les anciens défauts: longueur du scenario, perte de performance, mais la sauvegarde et le chargement restent automatique.


NOTE:

"Compressé" ça donnera ça dans le scenario, ici 135 valeurs qui auraient pris 135 lignes tiennent sur 4 lignes
(le forum les coupes)

Code: [Select]
SAVEVAR00
0§0,1§0,2§0,3§0,4§0,5§0,6§0,7§0,8§0,9§0,10§0,11§0,12§0,13§0,14§0,15§0,16§0,17§0,18§0,19§0,20§0,21§0,22§0,
23§0,24§0,25§0,26§0,27§2,28§2,29§2,30§2,31§0,32§2,33§0,34§2,35§0,36§0,37§2,38§2,39§0,40§0,41§0,42§0,
SAVEVAR01
43§0,44§0,45§0,46§0,47§0,48§0,49§0,50§3,51§0,52§3,53§0,54§3,55§3,56§0,57§0,58§0,59§0,60§0,61§0,62§0,63§
0,64§0,65§1,66§1,67§1,68§1,69§0,70§1,71§0,72§1,73§0,74§0,75§1,76§1,77§0,78§2,79§0,80§1,81§0,82§1,83§1,
SAVEVAR02
84§1,85§0,86§0,87§1,88§0,89§3,90§2,91§3,92§3,93§3,94§2,95§0,96§2,97§0,98§0,99§0,100§2,101§0,102§0,103§3
,104§3,105§0,106§0,107§0,108§3,109§3,110§2,111§3,112§2,113§2,114§2,115§2,116§0,117§0,118§2,119§0,120§0
SAVEVAR03
121§2,122§2,123§2,124§2,125§2,126§0,127§2,128§0,129§3,130§3,131§3,132§0,133§0,134§0,135§0,

Pour vous c'est transparent, pas besoin de vous occuper de ce qui ce passe dans le scenario.



Message modifié ( 24-06-2011 13:56 )


Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #3 - 26 August 2007, 22:00:30
Pratique: installation et utilisation de la classe "SaveRestoreScenario":


Installation:

1-télécharger l'exemple shuttlePB ici: http://www.dansteph.com/publie/ShuttlePB_SaveScenarioSDK.zip
2-Copiez SaveRestoreScenario.cpp et SaveRestoreScenario.h dans votre projet et incluez les.
3-Dans le header de votre classe tapez: #include "SaveRestoreScenario.h"
4-Dans la classe de votre addon declarez le handle de la classe SaveRestoreScenario.

Au final ca devrais donner dans ce genre la:


Code: [Select]
#include "orbitersdk.h"

#include "SaveRestoreScenario.h"  // header du SDK

class MyAddon: public VESSEL2
{
public:
MyAddon(OBJHANDLE hVessel, int flightmodel): VESSEL2 (hVessel, flightmodel) {}
SaveRestoreScenario Scn;  // handle de la classe
                .....
}

5-Modifiez les fonction orbiter de chargement et sauvegarde exactement comme suis:

Code: [Select]
// --------------------------------------------------------------
// Read status from scenario file
// --------------------------------------------------------------
void [glow=yellow,2,300]MonAddon[/glow]::clbkLoadStateEx (FILEHANDLE scn, void *vs)
{
char *line;
while (oapiReadScenario_nextline (scn, line))
{
if(Scn.LoadScenario(line)==TRUE)
continue;
ParseScenarioLineEx (line, vs);
}
}
// --------------------------------------------------------------
// Write status to scenario file
// --------------------------------------------------------------
void [glow=yellow,2,300]MonAddon[/glow]::clbkSaveState (FILEHANDLE scn)
{
VESSEL2::clbkSaveState (scn);
Scn.SaveScenario(scn);
}

N'oubliez pas de changer le nom de la class "MonAddon" pour la votre.

Voila c'est tout. L'installation est finie. Vous n 'aurez plus jamais besoin de toucher les fonctions de sauvegarde/restoration.


Utilisation:

1-Au début de clbkSetClassCap vous allez declarer chaque variable à sauver de cette maniere:

Code: [Select]
void MonAddon::clbkSetClassCaps (FILEHANDLE cfg)
{

////////////////////////////////////////////////////////////
// SAVERESTORESCENARIO DEMO
// Here you declare the variable that will be saved
////////////////////////////////////////////////////////////

Scn.InitClass();

// Variables compressée (utilisation normale)
Scn.SavedVariable(0, &UneVarialeInt);
Scn.SavedVariable(1, MonNom);
Scn.SavedVariable(2, &UneVariableDouble);
Scn.SavedVariable(3, &UneAutreDouble);

// Variable avec nom, sauvegarde classique pour pouvoir éditer le scenario
// A n'utiliser que pour les cas ou c'est vraiment nécessaire.
Scn.SavedVariable("[glow=yellow,2,300]Altitude[/glow]",&Altitude);
Scn.SavedVariable("[glow=yellow,2,300]CapitainName[/glow]",NomCapitaine);
///////////////////////////////////////////////////////////
}

C'est tout, sauvegarde et restoration des variables serons automatique.

Comme vous le voyez la même fonction sert indifférement à des variables int, float, double, dword, bool ,char à la seule exception que les char ne doivent pas avoir de signe & devant (voir "monNom"). De toute facon le compilateur ne vous laissera pas faire si vous essayer.


ATTENTION ATTENTION ATTENTION

-La premier fonction doit absolument etre l'initialisation: "Scn.InitClass();"

-On ne doit sauver QUE des variables de classe ou globale, une locale spécifique à clbkSetClassCap fera planter Orbiter.

-Les ID (1 2 3 etc etc) doivent rester toujours pareil pour chaque variable si vous voulez garder une compatibilité avec d'ancien scenario. En bref si vous intercalez une variable et changer les ID vous risquez de vous retrouver au chargement d'un ancien scenario avec "&UneVariableDouble" qui charge "MonNom" et toutes les autres valeurs aussi décallée.

-Il n'y a aucun controle au niveau des char, si vous aviez "char[25]" dans un ancien scenario et que vous le changez pour "char[15]" et charger l'ancien scenario ca fera un joli CTD.  (copiez 25 char sur une zone mémoire de 15 c'est pas bon)


Donc si on supprime une variable on garde les même ID, le "vide" n'est pas grave:

OUI!!!
Code: [Select]
Scn.SavedVariable(0, &UneVarialeInt);
//Scn.SavedVariable(1, MonNom);
Scn.SavedVariable(2, &UneVariableDouble);
Scn.SavedVariable(3, &UneAutreDouble);

NON!!!
Code: [Select]
Scn.SavedVariable(0, &UneVarialeInt);
//Scn.SavedVariable(1, MonNom);
Scn.SavedVariable(1, &UneVariableDouble);  // ancienne "MonNon"
Scn.SavedVariable(2, &UneAutreDouble);      // ancienne "UneVariableDouble"

Si vous faite des boucles for next car vos variables sont dans des tableaux laissez de la place pour de future ajout:
On peux sauver un tableau avec des ID de 0 à 25 le suivant de 50 à 60 etc etc... les vides ne sont pas grave on à des ID dispo de 0 à 1000. Plus tard on pourra rajouter une variable 26, 27, 28 sans casser la comptabilité avec d'ancien scenarios.


Variable locale (à l'intérieur de la fonction) c'est NON !!! elle n'est plus valide en dehors de cette fonction = CTD

NON!!!
Code: [Select]
Fonction clbkSetClassCap()
{
int UneVariableLocale;
Scn.SavedVariable(1, &UneVariableLocale);
}



Message modifié ( 26-08-2007 22:51 )


Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #4 - 26 August 2007, 22:53:59
Comment ca marche ? le principe ? en savoir plus ?

Pour ne pas copier bêtement sans comprendre je vais vous expliquer ici plusieurs notions.

Une variable C++, en interne c'est un POINTEUR vers une zone mémoire

Code: [Select]
int MaVariable=10;
Dans la mémoire ca donne ca, à gauche l'adresse de la mémoire, à droite le contenu:

Code: [Select]
0x045874  00
0x045875  00
0x045876  [b]10[/b]
0x045877  00

Donc "MaVariable" pointe sur l'adresse "0x045876" et le contenu à cette adresse est "10"

A l'aide de l'opérateurs "&"on peut récuperer l'adresse d'une variable et la passer à un pointeur C++ non typé:

Code: [Select]
void * Pointeur=&MaVariable;
"non typé" ca veut dire que le C++ s'en fout que ce soit un pointeur sur int, float, double etc etc, sinon il refuse
de compiler si le type n'est pas égal:

Code: [Select]
char * Pointeur=&MaVariable; // erreur, type int attendu
int * Pointeur=&MaVariable; // la ca marcherais, même type.

Après cette opération le pointeur C++ à la valeur de l'adresse de notre variable c'est à dire 0x045876
on peut donc plus tard récuperer la valeurs a cet endroit de la mémoire:

Code: [Select]
int RecupereValeur=*(int*)Pointeur;
Le "(int*)" c'est pour lui dire que ce qu'on récupere est une valeur de type "int" (le C++ est fortement typé) et le "*" placé tout devant dis qu'on veut récuperer la valeur contenue à l'endroit de la mémoire pointée par le pointeur.

De même on peut changer la valeur de la zone mémoire avec l'opération inverse:

*(int*)Pointeur=35;

Miraculeusement notre variable du début "MaVariable" aura maintenant la valeur 35 au lieu de 10 et sans y toucher.

C'est clair ? :badsmile:

La classe "SaveRestoreScenario" fait exactement ca, elle enregistre dans un tableau des pointeurs de vos variables et dans un autre tableau quel type elle ont.
A la sauvegarde elle va regarder leurs valeurs et l'enregistre dans le scenario selont le bon type (int=1, float = 1.25, char=text) Elle enregistre aussi juste devant l'ID de cette variable.

Au chargement elle récupere d'abord le type, puis la valeur et ce sert du pointeur pour restorer la valeur de la variable. C'est aussi pour cette raison que l'ID est importante.

Exercice amusant pour illustrer et bien comprendre:

copiez/collez le code ci dessous, mettez un point d'arret et lancez en mode débug puis executez en pas à pas en regardant les valeurs changer:

Code: [Select]
int MaVariableDepart=20; // une variable avec la valeur 20
void *Pointeur=&MaVariableDepart; // assigne l'adresse de la variable au pointeur
int RecupereValeur=*(int*)Pointeur; // récupere valeur MaVariableDepart
*(int*)Pointeur=45; // change valeur sans toucher a la variable.
int NouvelleValeur=*(int*)Pointeur; // récupere valeur MaVariableDepart

Ensuite regarder la valeur du pointeur:



Ouvrez le tab "mémoire" et tapez cette adresse en haut:



Comme on est en 32 bits les chiffres sont codé pour le type int sur 4 byte de 8 bits chacun, dans la mémoire les valeurs sont a l'envers (litle indian) ca donne "0000002d" en hexadécimale ce qui en décimale donne bien .... 45.
Si vous le faite depuis le début vous verrez la zone mémoire passer de "14 00 00 00" (20) à "2d 00 00 00" (45)

Vous touchez la du doigt le coeur de l'ordinateur.

En fait à l'intérieur, il y a 32 transistors qui passent de 00000000000000000000000000010100 à 000000000000000000000000000101101  ou les "1" représentent 3 volts et les zéro 0 volt.



Message modifié ( 26-08-2007 23:55 )


Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #5 - 27 August 2007, 00:10:28
De l'utilité des fonctions multi-usage

Une autre notion utilisée par la classe est la fonction multi usage. Avec le même nom on appelle différent corps de
fonctions en regard de quel parametre est passé.

Sans ca au lieu de ceci:


Code: [Select]
Scn.SavedVariable(0, &UneVarialeInt);
Scn.SavedVariable(1, MonNom);
Scn.SavedVariable(2, &UneVariableDouble);
Scn.SavedVariable(3, &UneAutreDouble);

Vous auriez cela:

Code: [Select]
Scn.SavedVariableINT(0, &UneVarialeInt);
Scn.SavedVariableCHAR(1, MonNom);
Scn.SavedVariableDOUBLE(2, &UneVariableDouble);
Scn.SavedVariableDOUBLE(3, &UneAutreDouble);

Des noms de fonctions différente. Un peut chiant.

Le C++ permet de donner le même nom à des fonctions pourvu que les parametres ne soient pas exactement du
même type:

Code: [Select]
Declaration:
void LogError(int NoErreur);
void LogError(char* TextErreur);
void LogError(char* TextErreur,int NoErreur);

On pourra dès lors appeller indiférement la fonction "LogErreur" pour ecrire soit uniquement le numéro d'erreur, soit
un texte, soit un texte avec le numéro. Pratique, un seul nom à ce souvenir.

Code: [Select]
Appel:
LogError(28 );
LogError("Erreur");
LogError("Erreur",35 );

Le C++ ce demmerdera pour appeler la bonne fonction:

Code: [Select]
Implementation de trois corps de fonction qui font les choses différement:
void LogError(int NoErreur)
{
    // ecrit le no d'erreur
}
void LogError(char* TextErreur)
{
   // ecrit le texte de l'erreur
}
void LogError(char* TextErreur,int NoErreur)
{
  // ecrit le texte de l'erreur et le no
}


FIN DU TUTO


Cliquez le lien ci-dessous pour retourner au sommaire des "tutorials pour créer une DLL Orbiter"
http://orbiter.dansteph.com/forum/index.php?topic=6335.msg95352#msg95352




Message modifié ( 27-08-2007 00:23 )


Offline yoann

  • Legend
  • ******
  • Posts: 1913
  • Country: France fr
  • Karma: 7
Reply #6 - 27 August 2007, 00:34:38
Quote
DanSteph a écrit:
Vous touchez la du doigt le coeur de l'ordinateur.
...
32 transistors ...   ...3 volts et les zéro 0 volt.

je savai pas que t'etait electronicien aussi Dan ! :):):)


__________________________________
    Luke, je suis ton pere            kchuuu  pchiiiii

Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #7 - 27 August 2007, 00:44:03
Certificat federal de capacité Suisse 1987

J'ai meme connu les lampes :)

Dan


Offline yoann

  • Legend
  • ******
  • Posts: 1913
  • Country: France fr
  • Karma: 7
Reply #8 - 27 August 2007, 00:46:22
:lol:


__________________________________
    Luke, je suis ton pere            kchuuu  pchiiiii

Offline no matter

  • Legend
  • ******
  • Posts: 2826
  • Karma: 1
Reply #9 - 27 August 2007, 11:16:41
Çà va devenir vite pratique çà, bonne idée, merci.


------------

no matter.

Offline Papyref

  • Legend
  • ******
  • Posts: 4896
  • Country: France fr
  • Karma: 223
  • Je suis dans la Lune ne pas me déranger
Reply #10 - 27 August 2007, 11:32:13
Joli cours. Merci professeur !

:sage: Papyref


Offline picto

  • Legend
  • ******
  • Posts: 5013
  • Country: France fr
  • Karma: 24
  • Criiii Crii Crii
Reply #11 - 27 August 2007, 11:47:14

Sur l'exemple ...
C'est pas 135 .... c'est 136 variables sur 4 lignes.:siffle:
Qu'est ce qu'il fait le Ham ?
Ben, le Ham s' terre !


Pic

Offline DanSteph

  • Administrator
  • Legend
  • *****
  • Posts: 15002
  • Karma: 178
  • Hein, quoi !?
    • FsPassengers
Reply #12 - 27 August 2007, 12:48:46
Et le lit masse ? :siffle:

Dan


Offline picto

  • Legend
  • ******
  • Posts: 5013
  • Country: France fr
  • Karma: 24
  • Criiii Crii Crii
Reply #13 - 27 August 2007, 13:25:43
Oui, et le mérou .... pète !


Pic

Offline tofitouf

  • Legend
  • ******
  • Posts: 1380
  • Karma: 0
Reply #14 - 07 June 2009, 02:50:10
Excellent, je n'avais pas vu cela plus tot....
je viens d'écrire une classe similaire... groumph.
et le passage de paramètre est bien plus simple dans ce cas ci.
bravo ! je vais donc de ce pas corriger ma classe.


« Last Edit: 07 June 2009, 02:50:10 by tofitouf »
---------------------------------------------

PC en rade, codage en panne.... Nom de Zeus