Author Topic: Save all characters on restart.  (Read 1133 times)

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« on: December 05, 2012, 06:40:10 am »


               We have an issue with this plugin I have been trying to correct. When the server restarts, it doesn't save players resulting in a potential exploit. This is our restart script:


#include "core_debug"
 
#include "conf_module"
#include "pf_msg_util"
#include "hpy_inc_sftfix"
 
void pf_reset( string sModuleName )
{

    WriteTimestampedLogEntry( "*** Executing restart module on " + sModuleName );
    ExportAllCharacters();
    SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "1");//this line is the entire nwscriptside of the reset plugin
 
}
 
//Function that saves the characters and scheduels next save
void pf_autosave( )
{
 string sModuleName = GetModuleName( );     // Get module name
 string sMsg;
 object oAnnouncer = DbgGetObjectByTag( MODULE_ANNOUNCER_TAG );
 if ( OBJECT_INVALID == oAnnouncer )
 {
    PrintString( "pf_autosave: unable to find announcer creature: tag=" + MODULE_ANNOUNCER_TAG );
 }
 
 if (GetLocalInt(GetModule(), "core_lastmsg"))
 {
    SetLocalInt(GetModule(), "core_lastmsg", 0);
    sMsg = "A minute or less until reset";
    AssignCommand( oAnnouncer, ActionSpeakString( sMsg, TALKVOLUME_SHOUT ));
    SetLocalString( GetModule( ), MODULE_RESET_TIME_MESSAGE, sMsg );
    return;
 }
 
 
 
 
 int nCount = GetLocalInt( GetModule( ), MODULE_SAVECOUNT_NAME ); //Increment Save counter
 SetLocalInt( GetModule( ), MODULE_SAVECOUNT_NAME, ++nCount );
 
 float fTimeLeft = MODULE_RESET_TIME * 60.0 - IntToFloat( nCount ) * MODULE_AUTOSAVE_INTERVAL;
 
 if ( fTimeLeft >= 60.0 )
 {
    float nHours = fTimeLeft / 60.0;
    sMsg = "The server will reset in about " + IntToString( FloatToInt( nHours )) + " hours";
    if ( IntToFloat( FloatToInt( nHours )) == nHours )
    {
      AssignCommand( oAnnouncer, ActionSpeakString( sMsg, TALKVOLUME_SHOUT ));
    }
 }
 else
 {
    if ( fTimeLeft <= 20.0 && IntToFloat( FloatToInt( fTimeLeft / 5.0 )) == fTimeLeft / 5.0 )
    {
      sMsg = "The server will reset in " + IntToString(FloatToInt(fTimeLeft)) + " minutes";
      WriteTimestampedLogEntry( "*** Less than 20 minutes left till reset: " + FloatToString( fTimeLeft ));
      AssignCommand( oAnnouncer, ActionSpeakString( sMsg, TALKVOLUME_SHOUT ));
    }
 }
 SetLocalString( GetModule( ), MODULE_RESET_TIME_MESSAGE, sMsg );
 
 //A player could after seeing the message hand items to another player and have them log thus
 //duplicating those items
 if ( fTimeLeft <= 0.0 )
 {
    WriteTimestampedLogEntry( "*** Timeleft < 0, doing delay restart " + FloatToString( fTimeLeft ));
    pf_reset( sModuleName );
 }
 if(fTimeLeft == MODULE_AUTOSAVE_INTERVAL){
    SetLocalInt(GetModule(), "core_lastmsg", 1);
    DelayCommand( (MODULE_AUTOSAVE_INTERVAL - 1.0) * 60.0, pf_autosave( ));
 }
 DelayCommand( MODULE_AUTOSAVE_INTERVAL * 60.0, pf_autosave( )); // Schedule the next save in 5 minutes is 300.0
}

I am hoping to find a way to export all characters just prior to the restart. If we can do this I would also like to eliminate the random delay on the restart timer (which was an attempt to prevent this exploit.)

The ExportAllCharacters(); function near the top of the scrip does not seem to work. Any help would be appreciated.

P.S. For now, I have a failsafe in the on aquire and unaquire events preventing this exploit (export character function). However it has the consequence of disabling barter, and also preventing shifters (and other polymorphed characters) from picking up items without experiencing the biobug where by they lose all merged properties, and have to reshift to get them back.
               
               

               


                     Modifié par Lazarus Magni, 06 décembre 2012 - 08:30 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Save all characters on restart.
« Reply #1 on: December 05, 2012, 09:08:57 am »


               try this

void pf_reset( string sModuleName )
{
object oPC = GetFirstPC();
 while(GetIsPC(oPC))
 {
 BootPC(oPC);
 oPC = GetNextPC();
 }
WriteTimestampedLogEntry( "*** Executing restart module on " + sModuleName );
DelayCommand(3.0,SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "1"));
}


Booting the character does automatically save him, but you might also consider saving the HP/area/other informations, if this isn't handled by OnClientExit event.
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #2 on: December 05, 2012, 10:50:10 am »


               True, booting works, unfortunately it also means all players who are on when the restart occurs also lose where they are in the mod (this information is stored during a mod session, but is wiped 5 min after a restart to prevent boss camping). Currently that location is perserved if they log back in within 5 min of a restart... if they are booted however, anyone in the middle of a run when the restart occurs will have to start all over again (some of our runs take 4-6 hours or more... so... that kinda hurts.)
               
               

               


                     Modifié par Lazarus Magni, 05 décembre 2012 - 10:55 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Save all characters on restart.
« Reply #3 on: December 05, 2012, 12:42:20 pm »


               uh? i dont understand, what you are describing would happened with exportallcharacter method too. To save player's location requires custom method such as variable on skin or using database.
               
               

               
            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Save all characters on restart.
« Reply #4 on: December 05, 2012, 02:54:59 pm »


               Im with ShaDoOoW on this one
I dont see the difference between booting the players a second or so before the reboot occurs, and the current method that allows them to remain online, but disconnects them via timeout.
(Im assuming you are executing the reboot, while they are connected?)

Either way, when the server resumes/restarts, when they rejoin, as far as the server is concerned, it just rebooted, it doesnt care what way the players were disconnected.
After all - once you execute the restart command, no further code is executed, so its not like a save command was being carried out afterwards.
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #5 on: December 05, 2012, 07:03:21 pm »


               Well let me just say I inherited this mod, and it’s massive, and I don’t know all the in and outs of how it works. I do know however that if the player is not connected when the restart occurs the players location is not preserved. And for those that were connected when it occurs, their locations are only preserved for 3-5 min after the server restarts, and then it is cleared.

I suspect it has to do with the on logout and on load events. I will dig for the code and see if I can find it and post it.

I wonder however, if a similar tactic as Shadooow posted initially might not work with exporting characters followed by a 2 or 3 second delay on the restart command?

I also found this thread here:
http://social.biowar...01462/1#5512985  
But we don't use a heartbeat script in the module event. I am under the impression this can lag large mods...

And then there is this:
http://social.biowar...-9025529-1.html

I wonder if the updated plugin may have fixed this issue on it's own?
               
               

               


                     Modifié par Lazarus Magni, 05 décembre 2012 - 07:03 .
                     
                  


            

Baaleos

  • Administrator
  • Hero Member
  • *****
  • Posts: 1916
  • Karma: +0/-0
Save all characters on restart.
« Reply #6 on: December 05, 2012, 08:20:45 pm »


               Unless this plugin actually contains c++ code within it that triggers a save, the code that saves player locations MUST be in nwscript somewhere in the module.

The places it is likely to exist is
1. The script that performs the reset itself.
2. onClientExit (although this does NOT get fired if the server is shutdown, players are usually just timedout from the server if it is shutdown via nwnx)
3. onHeartbeat
4. default.nss - the player heartbeat script.

Check these locations for anything that uses

SetPersistentLocation
or
StoreCampaignLocation  

(you might wna search all scripts for that via the nwscript editor)
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #7 on: December 05, 2012, 09:21:22 pm »


               Well our on load event is this:

#include "aps_include"
#include "wys_i_common"
#include "wys_i_booze"
#include "x2_inc_switches"
#include "pf_autosave"
#include "pc_export_inc"

void BootPCs()
{
object oPC = GetFirstPC();
while(GetIsObjectValid(oPC))
{
BootPC(oPC);
oPC = GetNextPC();
}
}

void ClearRecentRestart( )
{
WriteTimestampedLogEntry( "[Location] Clearing RecentRestart Flag" );
DeleteLocalInt( GetModule( ), "RecentRestart" );

string sSQL = "TRUNCATE " + MODULE_PC_LOCATION_TABLE;
SQLExecDirect( sSQL );
}

void main()
{
//Added in the new shifter fix's auto save system
pc_export_onmoduleload();

// Init placeholders for ODBC gateway
SQLInit( );
WriteTimestampedLogEntry( "*** Started module " + GetModuleName( ));

// Add standard alcoholic Beverages
DelayCommand( 230.0, WysAddStdBooze( ));
//wait 5 minutes after module is started to do the first save
ExecuteScript( MODULE_NAME_INIT, OBJECT_SELF );

// Save locations of PC's
// Save characters to the vault
DelayCommand( MODULE_INITIAL_AUTOSAVE_INTERVAL * 60.0, pf_autosave( ));

//Crafting exploit
SetLocalInt( GetModule( ), "X2_L_DO_NOT_ALLOW_CRAFTSKILLS", TRUE );

if ( GetGameDifficulty( ) == GAME_DIFFICULTY_CORE_RULES || GetGameDifficulty( ) == GAME_DIFFICULTY_DIFFICULT )
{
// * Setting the switch below will enable a seperate Use Magic Device Skillcheck for
// * rogues when playing on Hardcore+ difficulty. This only applies to scrolls
SetModuleSwitch( MODULE_SWITCH_ENABLE_UMD_SCROLLS, TRUE );

// * Activating the switch below will make AOE spells hurt neutral NPCS by default
SetModuleSwitch( MODULE_SWITCH_AOE_HURT_NEUTRAL_NPCS, FALSE );
}

// * AI: Activating the switch below will make the creaures using the WalkWaypoint function
// * able to walk across areas
SetModuleSwitch( MODULE_SWITCH_ENABLE_CROSSAREA_WALKWAYPOINTS, TRUE );

// * Spells: Activating the switch below will make the Glyph of Warding spell behave differently:
// * The visual glyph will disappear after 6 seconds, making them impossible to spot
// SetModuleSwitch( MODULE_SWITCH_ENABLE_INVISIBLE_GLYPH_OF_WARDING, TRUE);

// * Craft Feats: Want 50 charges on a newly created wand? We found this unbalancing,
// * but since it is described this way in the book, here is the switch to get it back...
// SetModuleSwitch( MODULE_SWITCH_ENABLE_CRAFT_WAND_50_CHARGES, TRUE);

// * Craft Feats: Use this to disable Item Creation Feats if you do not want
// * them in your module
SetModuleSwitch( MODULE_SWITCH_DISABLE_ITEM_CREATION_FEATS, TRUE );

// * Palemaster: Deathless master touch in PnP only affects creatures up to a certain size.
// * We do not support this check for balancing reasons, but you can still activate it...
// SetModuleSwitch( MODULE_SWITCH_SPELL_CORERULES_DMASTERTOUCH, TRUE);

// * Epic Spellcasting: Some Epic spells feed on the liveforce of the caster. However this
// * did not fit into NWNs spell system and was confusing, so we took it out...
// SetModuleSwitch( MODULE_SWITCH_EPIC_SPELLS_HURT_CASTER, TRUE);

// * Epic Spellcasting: Some Epic spells feed on the liveforce of the caster. However this
// * did not fit into NWNs spell system and was confusing, so we took it out...
// SetModuleSwitch( MODULE_SWITCH_RESTRICT_USE_POISON_TO_FEAT, TRUE);

// * Spellcasting: Some people don't like caster's abusing expertise to raise their AC
// * Uncommenting this line will drop expertise mode whenever a spell is cast by a player
SetModuleSwitch( MODULE_VAR_AI_STOP_EXPERTISE_ABUSE, TRUE );


// * Item Event Scripts: The game's default event scripts allow routing of all item related events
// * into a single file, based on the tag of that item. If an item's tag is "test", it will fire a
// * script called "test" when an item based event (equip, unequip, acquire, unacquire, activate,...)
// * is triggered. Check "x2_it_example.nss" for an example.
// * This feature is disabled by default.
SetModuleSwitch( MODULE_SWITCH_ENABLE_TAGBASED_SCRIPTS, TRUE );

SetLocalString( GetModule( ), "X2_S_UD_SPELLSCRIPT", "mod_spellhook" );
SetLocalInt( GetModule( ), "RecentRestart", 1 );
DelayCommand( 600.0, ClearRecentRestart( ));

}

And our on logout event is this:

#include "pf_dupchk"
#include "k_inc_leto"

//Function that cleans up Local ints if the character has not logged back in
//This does not clean up the "death status" or hp status only spellstatus to free up memory
void Cleanup( string sName )
{
int nCount;
if ( GetLocalInt( GetModule( ), sName + "_logged" ) == 1 )
{
for ( nCount = 0; nCount <= 413; ++nCount )
{
DeleteLocalInt( GetModule( ), sName + "_Spell_" + IntToString( nCount ) + "_" );
}
DeleteLocalInt( GetModule( ), sName + "_losefeatsuses" );
}
}

void ClearDominated( object oPC )
{
effect eBuff = GetFirstEffect( oPC );
while ( GetIsEffectValid( eBuff ))
{
if ( GetEffectType( eBuff ) == EFFECT_TYPE_DOMINATED )
{
RemoveEffect( oPC, eBuff );
}
eBuff = GetNextEffect( oPC );
}
}

void main( )
{
object oPC = GetExitingObject( );
string sName = GetName( oPC );
string sAccount = GetLocalString( oPC, "AccountName" );

RemoveDupeCheckEntry( oPC );
int nSpell; //Spell interger by SPELL_* constants
int nFeat; //Feat represented by Feat_* constants
int nCount; //Number of spell casts memorized and uncast

DeleteLocalInt( GetModule( ), sName + "LoggedIn" );
SetLocalInt( GetModule( ), sName + sAccount + "_SessionActive", 2 );

//Clear dominated effect from player
ClearDominated( oPC );

SetLocalInt( GetModule( ), sName + "_hp", GetCurrentHitPoints( oPC ));
SetLocalInt( GetModule( ), sName + "_logged", 1 );
//SetLocalInt( GetModule( ), sName + "_losefeatsuses", 1 );
//Load up psudo array with spells memorized
for ( nSpell = 0; nSpell <= 413; nSpell++ )
{
nCount = GetHasSpell( nSpell ,oPC );
SetLocalInt( GetModule( ), sName + "_Spell_" + IntToString( nSpell ) + "_", nCount );
}
//Clean up Variables if player does not log in for 5 minutes
DelayCommand( 160.0, Cleanup( sName ));
DelayCommand( 1200.0, SetLocalInt( GetModule( ), sName + sAccount + "_SessionActive", 1 ));

// Leto
string Script = GetLocalString(oPC, "LetoScript");
if( Script != "" )
{
SetLocalString(oPC, "LetoScript", "");
LetoScript(Script);
}
}

You can see there are quite a few things related to clearing recent restart flags, and sessions being active or not. As I said, I am not sure of how it all works, but it does work as I described above, with regards to locations being preserved after a reset.

The core of the whole issue however, is that we need all characters to be saved just prior to restarting. I am going to try:

void pf_reset( string sModuleName )
{
WriteTimestampedLogEntry( "*** Executing restart module on " + sModuleName );
ExportAllCharacters();
DelayCommand(3.0,SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "1"));
}

I am wondering if the export all function I posted initially was not working because it was happening simultaneously with the reset command. I am hoping putting a delay on the reset command will allow all characters to be exported (saved.) Can anyone see any reason why this would not work?

               
               

               


                     Modifié par Lazarus Magni, 05 décembre 2012 - 09:24 .
                     
                  


            

Legacy_virusman

  • Sr. Member
  • ****
  • Posts: 448
  • Karma: +0/-0
Save all characters on restart.
« Reply #8 on: December 06, 2012, 02:51:34 am »


               Try this plugin instead:
http://data.virusman...t-0.2-win32.rar
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #9 on: December 06, 2012, 07:25:27 am »


               Hmm... thanks Virus man, I appreciate the help. I might try that, but I am very, very, VERY happy to report this code:

ExportAllCharacters();
DelayCommand(3.0,SetLocalString(GetModule(), "NWNX!RESETPLUGIN!SHUTDOWN", "1"));

seemed to work. I highly recommend anyone using the restart plugin to add this export and delay to the restart code. I need to test more to be 100% sure, but all indications are that this works. Thank you very much to Shadooow for pointing the way. After our last restart all players who were on at that time recieved an updated bic file into their account folders. In other words they were saved.

P.S. Anyone using the new pluging will have to modify the command accordingly.

P.P.S. If virusman fixed this with the re-updated plugin, than perhaps this code addition is not even an issue. All I know is I am very happy to see this potential exploit fixed either way. Cheers you all!
               
               

               


                     Modifié par Lazarus Magni, 06 décembre 2012 - 08:24 .
                     
                  


            

Legacy_SKIPPNUTTZ

  • Full Member
  • ***
  • Posts: 148
  • Karma: +0/-0
Save all characters on restart.
« Reply #10 on: December 06, 2012, 08:24:03 am »


               

ShaDoOoW wrote...

uh? i dont understand, what you are describing would happened with exportallcharacter method too. To save player's location requires custom method such as variable on skin or using database.


The player location is saved to a mysql database, but only if they are online when the server resets.
-----
The resetplugin that we were using was apparently crashing the server, not restarting it.  It gave the "you have been disconnected" message, not the one that says "server side characters have been saved".

The same thing happens when our admin manually restarts the server.

-----

Hopefully Laz's new method will fix this.
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #11 on: December 06, 2012, 08:28:06 am »


               It seems like this code works Skipp. If not I will try virusman's plugin. But all indications are the code works all by itself. And if virusman's updated plugin does this on it's own, all the better.
               
               

               
            

Legacy_Lazarus Magni

  • Hero Member
  • *****
  • Posts: 1837
  • Karma: +0/-0
Save all characters on restart.
« Reply #12 on: December 06, 2012, 06:58:11 pm »


               BTW Virusman, what's the command for that version of the plugin? Is it "NWNX!RESETPLUGIN!SHUTDOWN" or "NWNX!RESET!SHUTDOWN"?
               
               

               
            

Legacy_virusman

  • Sr. Member
  • ****
  • Posts: 448
  • Karma: +0/-0
Save all characters on restart.
« Reply #13 on: December 13, 2012, 09:20:40 am »


               NWNX!RESET!SHUTDOWN afaik