2024-01-20 21:02:41 -06:00
using LANCommander.PlaynitePlugin.Models ;
using LANCommander.SDK ;
2024-01-24 23:36:52 -06:00
using LANCommander.SDK.Exceptions ;
2024-01-20 21:02:41 -06:00
using LANCommander.SDK.Helpers ;
using LANCommander.SDK.PowerShell ;
2024-01-24 23:36:52 -06:00
using Microsoft.Extensions.Logging ;
2024-01-22 19:10:57 -06:00
using Microsoft.Toolkit.Uwp.Notifications ;
2024-01-21 12:34:30 -06:00
using Playnite.SDK ;
2024-01-20 21:02:41 -06:00
using Playnite.SDK.Models ;
2024-01-21 12:34:30 -06:00
using Playnite.SDK.Plugins ;
2024-01-20 21:02:41 -06:00
using System ;
using System.Collections.Generic ;
2024-01-21 13:14:25 -06:00
using System.Collections.ObjectModel ;
2024-01-20 21:02:41 -06:00
using System.Diagnostics ;
using System.Drawing ;
using System.IO ;
using System.Linq ;
using System.Reflection ;
2024-01-21 12:34:30 -06:00
using System.Threading ;
2024-01-20 21:02:41 -06:00
using System.Threading.Tasks ;
using System.Windows.Forms ;
namespace LANCommander.PlaynitePlugin
{
public class DownloadQueueController
{
2024-01-24 23:36:52 -06:00
public static readonly Playnite . SDK . ILogger Logger = LogManager . GetLogger ( ) ;
2024-01-20 21:02:41 -06:00
private LANCommanderLibraryPlugin Plugin { get ; set ; }
2024-01-21 14:44:36 -06:00
public DownloadQueue DownloadQueue { get ; set ; }
2024-01-20 21:02:41 -06:00
private Stopwatch Stopwatch { get ; set ; }
2024-01-21 12:34:30 -06:00
public delegate void OnInstallCompleteHandler ( Game game , string installDirectory ) ;
public event OnInstallCompleteHandler OnInstallComplete ;
2024-01-24 23:36:52 -06:00
public delegate void OnInstallFailHandler ( Game game ) ;
public event OnInstallFailHandler OnInstallFail ;
2024-01-25 00:46:10 -06:00
public delegate void OnInstallCancelledHandler ( Game game ) ;
public event OnInstallCancelledHandler OnInstallCancelled ;
2024-01-20 21:02:41 -06:00
public DownloadQueueController ( LANCommanderLibraryPlugin plugin )
{
Plugin = plugin ;
2024-01-21 14:44:36 -06:00
DownloadQueue = new DownloadQueue ( ) ;
2024-01-20 21:02:41 -06:00
Stopwatch = new Stopwatch ( ) ;
Plugin . LANCommanderClient . Games . OnArchiveExtractionProgress + = Games_OnArchiveExtractionProgress ;
Plugin . LANCommanderClient . Games . OnArchiveEntryExtractionProgress + = Games_OnArchiveEntryExtractionProgress ;
2024-01-22 19:10:57 -06:00
ToastNotificationManagerCompat . OnActivated + = NotificationHandler ;
}
private void NotificationHandler ( ToastNotificationActivatedEventArgsCompat e )
{
try
{
var args = ToastArguments . Parse ( e . Argument ) ;
var gameId = Guid . Parse ( args [ "gameId" ] ) ;
switch ( args [ "action" ] )
{
case "play" :
Plugin . PlayniteApi . StartGame ( gameId ) ;
break ;
case "viewInLibrary" :
default :
Plugin . PlayniteApi . MainView . SwitchToLibraryView ( ) ;
Plugin . PlayniteApi . MainView . SelectGame ( gameId ) ;
break ;
}
}
catch { }
2024-01-20 21:02:41 -06:00
}
private void Games_OnArchiveEntryExtractionProgress ( object sender , SDK . ArchiveEntryExtractionProgressArgs e )
{
2024-01-21 14:44:36 -06:00
if ( DownloadQueue . CurrentItem . CancellationToken ! = null & & DownloadQueue . CurrentItem . CancellationToken . IsCancellationRequested )
2024-01-20 21:02:41 -06:00
{
Plugin . LANCommanderClient . Games . CancelInstall ( ) ;
}
}
private void Games_OnArchiveExtractionProgress ( long position , long length , SDK . Models . Game game )
{
2024-01-21 12:34:30 -06:00
if ( Stopwatch . ElapsedMilliseconds > 500 )
{
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = >
{
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . Size = length ;
DownloadQueue . CurrentItem . Speed = ( double ) ( position - DownloadQueue . CurrentItem . TotalDownloaded ) / ( Stopwatch . ElapsedMilliseconds / 1000d ) ;
DownloadQueue . CurrentItem . TotalDownloaded = position ;
2024-01-22 01:50:30 -06:00
DownloadQueue . CurrentItem . TimeRemaining = $"{GetTimeRemaining(DownloadQueue.CurrentItem)} Remaining" ;
2024-01-21 12:52:12 -06:00
Plugin . DownloadQueueSidebarItem . ProgressMaximum = length ;
Plugin . DownloadQueueSidebarItem . ProgressValue = position ;
2024-01-21 12:34:30 -06:00
} ) ;
Stopwatch . Restart ( ) ;
}
2024-01-20 21:02:41 -06:00
}
2024-01-22 01:50:30 -06:00
private void ChangeCurrentItemStatus ( DownloadQueueItemStatus status )
{
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = > {
2024-01-25 00:46:10 -06:00
switch ( status )
{
case DownloadQueueItemStatus . Downloading :
DownloadQueue . CurrentItem . ProgressIndeterminate = false ;
DownloadQueue . CurrentItem . Status = status ;
break ;
default :
DownloadQueue . CurrentItem . ProgressIndeterminate = true ;
DownloadQueue . CurrentItem . Status = status ;
break ;
}
2024-01-22 01:50:30 -06:00
} ) ;
}
private string GetTimeRemaining ( DownloadQueueItem item )
{
2024-01-24 23:02:15 -06:00
if ( item . Speed < = 0 )
2024-01-22 01:50:30 -06:00
return "∞" ;
var timespan = TimeSpan . FromSeconds ( ( item . Size - item . TotalDownloaded ) / item . Speed ) ;
if ( timespan . Days > 0 )
return timespan . ToString ( @"d\:hh\:mm\:ss" ) ;
if ( timespan . Hours > 0 )
2024-01-24 23:02:15 -06:00
return timespan . ToString ( @"h\:mm\:ss" ) ;
2024-01-22 01:50:30 -06:00
else
return timespan . ToString ( @"mm\:ss" ) ;
}
2024-01-20 21:02:41 -06:00
public void Add ( Game game )
{
var gameId = Guid . Parse ( game . GameId ) ;
2024-01-24 19:00:22 -06:00
var gameInfo = Plugin . LANCommanderClient . Games . Get ( gameId ) ;
if ( gameInfo . BaseGame ! = null )
{
var baseGame = Plugin . PlayniteApi . Database . Games . Where ( g = > g . GameId = = gameInfo . BaseGame . Id . ToString ( ) ) . FirstOrDefault ( ) ;
if ( baseGame ! = null & & ! baseGame . IsInstalled )
Plugin . PlayniteApi . InstallGame ( baseGame . Id ) ;
}
2024-01-20 21:02:41 -06:00
2024-01-21 14:44:36 -06:00
if ( ! DownloadQueue . Items . Any ( i = > i . Game . Id = = game . Id ) )
2024-01-20 21:02:41 -06:00
{
2024-02-03 17:36:40 -06:00
if ( String . IsNullOrWhiteSpace ( game . CoverImage ) )
game . CoverImage = Path . Combine ( Path . GetDirectoryName ( Assembly . GetExecutingAssembly ( ) . Location ) , "default_cover.png" ) ;
2024-01-21 14:44:36 -06:00
DownloadQueue . Items . Add ( new DownloadQueueItem ( )
2024-01-21 12:34:30 -06:00
{
CoverPath = Plugin . PlayniteApi . Database . GetFullFilePath ( game . CoverImage ) ,
Game = game ,
Title = game . Name ,
QueuedOn = DateTime . Now ,
} ) ;
2024-01-22 01:50:30 -06:00
2024-01-22 02:38:53 -06:00
if ( DownloadQueue . Items . Count = = 1 & & DownloadQueue . CurrentItem = = null )
ProcessQueue ( ) ;
2024-01-22 01:50:30 -06:00
game . IsInstalled = false ;
game . IsInstalling = true ;
Plugin . PlayniteApi . Database . Games . Update ( game ) ;
2024-01-21 12:34:30 -06:00
}
2024-01-20 21:02:41 -06:00
}
2024-01-21 13:14:25 -06:00
public void Remove ( DownloadQueueItem downloadQueueItem )
{
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( $"Removing {downloadQueueItem.Title} from the queue" ) ;
2024-01-24 23:36:52 -06:00
if ( DownloadQueue . Items . Contains ( downloadQueueItem ) )
2024-01-22 01:50:30 -06:00
{
2024-01-24 23:36:52 -06:00
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = >
{
DownloadQueue . Items . Remove ( downloadQueueItem ) ;
} ) ;
}
else if ( DownloadQueue . CurrentItem . Equals ( downloadQueueItem ) )
{
DownloadQueue . CurrentItem = null ;
}
2024-01-21 13:14:25 -06:00
}
2024-01-21 14:44:36 -06:00
public bool Exists ( Game game )
2024-01-20 21:02:41 -06:00
{
2024-01-21 14:44:36 -06:00
return DownloadQueue . Items . Any ( i = > i . Game . Id = = game . Id ) ;
}
2024-01-21 12:34:30 -06:00
2024-01-21 14:44:36 -06:00
public void ProcessQueue ( )
{
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Processing download queue" ) ;
2024-01-21 14:44:36 -06:00
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = >
2024-01-21 12:34:30 -06:00
{
2024-01-22 02:38:53 -06:00
DownloadQueue . CurrentItem = DownloadQueue . Items . FirstOrDefault ( ) ;
2024-01-21 12:34:30 -06:00
2024-01-21 14:44:36 -06:00
if ( DownloadQueue . CurrentItem ! = null )
DownloadQueue . Items . Remove ( DownloadQueue . CurrentItem ) ;
} ) ;
Task . Run ( ( ) = > Install ( ) ) ;
2024-01-20 21:02:41 -06:00
}
2024-01-25 00:46:10 -06:00
public void CancelInstall ( )
{
if ( DownloadQueue . CurrentItem . Status = = DownloadQueueItemStatus . Downloading )
{
ChangeCurrentItemStatus ( DownloadQueueItemStatus . Canceled ) ;
Plugin . LANCommanderClient . Games . CancelInstall ( ) ;
}
}
2024-01-21 12:34:30 -06:00
private void Install ( )
2024-01-20 21:02:41 -06:00
{
2024-01-21 14:44:36 -06:00
if ( DownloadQueue . CurrentItem = = null )
2024-01-20 21:02:41 -06:00
return ;
2024-01-21 14:44:36 -06:00
var gameId = Guid . Parse ( DownloadQueue . CurrentItem . Game . GameId ) ;
2024-01-21 12:34:30 -06:00
var game = Plugin . LANCommanderClient . Games . Get ( gameId ) ;
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( $"Initiating install for {game.Title}" ) ;
2024-01-24 19:00:22 -06:00
if ( game . BaseGame ! = null )
2024-01-24 23:44:27 -06:00
{
Logger ? . Trace ( "Game is reliant on another game for installation. Installing the base game first..." ) ;
2024-01-24 19:00:22 -06:00
var baseGame = Plugin . PlayniteApi . Database . Games . Where ( g = > g . GameId = = game . BaseGame . Id . ToString ( ) ) . FirstOrDefault ( ) ;
if ( baseGame ! = null & & ! baseGame . IsInstalled )
Plugin . PlayniteApi . InstallGame ( baseGame . Id ) ;
}
2024-01-20 21:02:41 -06:00
Stopwatch . Restart ( ) ;
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . InProgress = true ;
2024-01-20 21:02:41 -06:00
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . Game . IsInstalling = true ;
Plugin . PlayniteApi . Database . Games . Update ( DownloadQueue . CurrentItem . Game ) ;
2024-01-20 21:02:41 -06:00
2024-01-22 01:50:30 -06:00
ChangeCurrentItemStatus ( DownloadQueueItemStatus . Downloading ) ;
2024-01-24 23:36:52 -06:00
string installDirectory ;
try
{
installDirectory = Plugin . LANCommanderClient . Games . Install ( game . Id ) ;
}
2024-01-25 00:46:10 -06:00
catch ( InstallCanceledException ex )
{
OnInstallCancelled ? . Invoke ( DownloadQueue . CurrentItem . Game ) ;
Remove ( DownloadQueue . CurrentItem ) ;
Stopwatch . Stop ( ) ;
return ;
}
2024-01-24 23:36:52 -06:00
catch ( InstallException ex )
{
Plugin . PlayniteApi . Notifications . Add ( $"InstallFail-{DownloadQueue.CurrentItem.Game.Id}" , ex . Message , NotificationType . Error ) ;
ShowFailedNotification ( DownloadQueue . CurrentItem ) ;
OnInstallFail ? . Invoke ( DownloadQueue . CurrentItem . Game ) ;
Remove ( DownloadQueue . CurrentItem ) ;
Stopwatch . Stop ( ) ;
return ;
}
catch ( Exception ex )
{
Logger ? . Error ( ex , $"An unknown error occurred while trying to install {game.Title}" ) ;
Plugin . PlayniteApi . Notifications . Add ( $"InstallFail-{DownloadQueue.CurrentItem.Game.Id}" , $"An unknown error occurred while trying to install {game.Title}" , NotificationType . Error ) ;
ShowFailedNotification ( DownloadQueue . CurrentItem ) ;
OnInstallFail ? . Invoke ( DownloadQueue . CurrentItem . Game ) ;
Remove ( DownloadQueue . CurrentItem ) ;
Stopwatch . Stop ( ) ;
return ;
}
2024-01-20 21:02:41 -06:00
Stopwatch . Stop ( ) ;
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( $"Game successfully installed to {installDirectory}" ) ;
2024-01-21 12:52:12 -06:00
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = >
{
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . ProgressIndeterminate = true ;
2024-01-21 12:52:12 -06:00
Plugin . DownloadQueueSidebarItem . ProgressValue = 1 ;
Plugin . DownloadQueueSidebarItem . ProgressMaximum = 1 ;
} ) ;
2024-01-21 12:34:30 -06:00
if ( game . Redistributables ! = null & & game . Redistributables . Any ( ) )
2024-01-22 01:50:30 -06:00
{
ChangeCurrentItemStatus ( DownloadQueueItemStatus . InstallingRedistributables ) ;
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Installing redistributables" ) ;
2024-01-21 12:34:30 -06:00
Plugin . LANCommanderClient . Redistributables . Install ( game ) ;
2024-01-22 01:50:30 -06:00
}
2024-01-20 21:02:41 -06:00
2024-01-21 12:34:30 -06:00
var manifest = ManifestHelper . Read ( installDirectory , gameId ) ;
2024-01-20 21:02:41 -06:00
Plugin . UpdateGame ( manifest , installDirectory ) ;
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Attempting to download the latest save" ) ;
2024-01-22 01:50:30 -06:00
ChangeCurrentItemStatus ( DownloadQueueItemStatus . DownloadingSaves ) ;
2024-01-20 21:02:41 -06:00
Plugin . SaveController = new LANCommanderSaveController ( Plugin , null ) ;
2024-01-21 12:34:30 -06:00
Plugin . SaveController . Download ( gameId , installDirectory ) ;
2024-01-20 21:02:41 -06:00
2024-01-22 01:50:30 -06:00
ChangeCurrentItemStatus ( DownloadQueueItemStatus . RunningScripts ) ;
2024-02-03 17:35:22 -06:00
try
{
RunInstallScript ( game ) ;
RunNameChangeScript ( game ) ;
RunKeyChangeScript ( game ) ;
}
catch ( Exception ex )
{
Logger ? . Error ( ex , "Failed to execute post-install scripts" ) ;
}
2024-01-20 21:02:41 -06:00
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . CompletedOn = DateTime . Now ;
DownloadQueue . CurrentItem . TotalDownloaded = DownloadQueue . CurrentItem . Size ;
2024-01-22 01:50:30 -06:00
ChangeCurrentItemStatus ( DownloadQueueItemStatus . Idle ) ;
2024-01-20 21:02:41 -06:00
2024-01-24 23:44:27 -06:00
Logger . Trace ( "Installation process completed successfully. Running final cleanup and marking the game as installed" ) ;
2024-01-24 23:36:52 -06:00
ShowCompletedNotification ( DownloadQueue . CurrentItem ) ;
2024-01-20 21:02:41 -06:00
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . InProgress = false ;
2024-01-20 21:02:41 -06:00
2024-01-21 14:44:36 -06:00
OnInstallComplete ? . Invoke ( DownloadQueue . CurrentItem . Game , installDirectory ) ;
2024-01-20 21:02:41 -06:00
2024-01-21 12:52:12 -06:00
Plugin . PlayniteApi . MainView . UIDispatcher . Invoke ( ( ) = >
{
2024-01-21 14:44:36 -06:00
DownloadQueue . CurrentItem . ProgressIndeterminate = false ;
2024-01-22 01:50:30 -06:00
DownloadQueue . Completed . Add ( DownloadQueue . CurrentItem ) ;
2024-01-21 12:52:12 -06:00
Plugin . DownloadQueueSidebarItem . ProgressValue = 0 ;
2024-01-22 01:50:30 -06:00
Plugin . DownloadQueueSidebarItem . ProgressMaximum = 1 ;
2024-01-21 12:52:12 -06:00
} ) ;
2024-01-22 01:50:30 -06:00
DownloadQueue . CurrentItem . Game . IsInstalled = true ;
DownloadQueue . CurrentItem . Game . IsInstalling = false ;
2024-02-07 00:06:51 -06:00
DownloadQueue . CurrentItem . Game . LastActivity = DateTime . Now ;
DownloadQueue . CurrentItem . Game . Added = DateTime . Now ;
2024-01-22 01:50:30 -06:00
Plugin . PlayniteApi . Database . Games . Update ( DownloadQueue . CurrentItem . Game ) ;
2024-01-20 21:02:41 -06:00
ProcessQueue ( ) ;
}
2024-01-24 23:36:52 -06:00
private void ShowCompletedNotification ( DownloadQueueItem queueItem )
{
new ToastContentBuilder ( )
. AddText ( "Game Installed" )
. AddText ( $"{queueItem.Title} has finished installing!" )
. AddArgument ( "gameId" , queueItem . Game . Id . ToString ( ) )
. AddButton (
new ToastButton ( )
. SetContent ( "Play" )
. AddArgument ( "action" , "play" )
)
. AddButton (
new ToastButton ( )
. SetContent ( "View in Library" )
. AddArgument ( "action" , "viewInLibrary" )
)
. AddAppLogoOverride ( new Uri ( $"file:///{queueItem.CoverPath}" , UriKind . Absolute ) , ToastGenericAppLogoCrop . None )
. Show ( ) ;
}
private void ShowFailedNotification ( DownloadQueueItem queueItem )
{
new ToastContentBuilder ( )
. AddText ( "Install Failed" )
. AddText ( $"{queueItem.Title} could not be installed!" )
. AddArgument ( "gameId" , queueItem . Game . Id . ToString ( ) )
. AddButton (
new ToastButton ( )
. SetContent ( "View in Library" )
. AddArgument ( "action" , "viewInLibrary" )
)
. AddAppLogoOverride ( new Uri ( $"file:///{queueItem.CoverPath}" , UriKind . Absolute ) , ToastGenericAppLogoCrop . None )
. Show ( ) ;
}
2024-01-20 21:02:41 -06:00
private int RunInstallScript ( SDK . Models . Game game )
{
var installDirectory = Plugin . LANCommanderClient . Games . GetInstallDirectory ( game ) ;
var manifest = ManifestHelper . Read ( installDirectory , game . Id ) ;
var path = ScriptHelper . GetScriptFilePath ( installDirectory , game . Id , SDK . Enums . ScriptType . Install ) ;
if ( File . Exists ( path ) )
{
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Running install script" ) ;
2024-01-20 21:02:41 -06:00
var script = new PowerShellScript ( ) ;
script . AddVariable ( "InstallDirectory" , installDirectory ) ;
script . AddVariable ( "GameManifest" , manifest ) ;
script . AddVariable ( "DefaultInstallDirectory" , Plugin . Settings . InstallDirectory ) ;
script . AddVariable ( "ServerAddress" , Plugin . Settings . ServerAddress ) ;
script . UseFile ( ScriptHelper . GetScriptFilePath ( installDirectory , game . Id , SDK . Enums . ScriptType . Install ) ) ;
return script . Execute ( ) ;
}
return 0 ;
}
private int RunNameChangeScript ( SDK . Models . Game game )
{
var installDirectory = Plugin . LANCommanderClient . Games . GetInstallDirectory ( game ) ;
var manifest = ManifestHelper . Read ( installDirectory , game . Id ) ;
var path = ScriptHelper . GetScriptFilePath ( installDirectory , game . Id , SDK . Enums . ScriptType . NameChange ) ;
var oldName = GameService . GetPlayerAlias ( installDirectory , game . Id ) ;
2024-01-27 11:10:33 -08:00
var newName = Plugin . Settings . DisplayName ;
2024-01-20 21:02:41 -06:00
if ( File . Exists ( path ) )
{
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Running name change script" ) ;
2024-01-20 21:02:41 -06:00
var script = new PowerShellScript ( ) ;
script . AddVariable ( "InstallDirectory" , installDirectory ) ;
script . AddVariable ( "GameManifest" , manifest ) ;
script . AddVariable ( "DefaultInstallDirectory" , Plugin . Settings . InstallDirectory ) ;
script . AddVariable ( "ServerAddress" , Plugin . Settings . ServerAddress ) ;
script . AddVariable ( "OldPlayerAlias" , oldName ) ;
script . AddVariable ( "NewPlayerAlias" , newName ) ;
script . UseFile ( path ) ;
GameService . UpdatePlayerAlias ( installDirectory , game . Id , newName ) ;
return script . Execute ( ) ;
}
return 0 ;
}
private int RunKeyChangeScript ( SDK . Models . Game game )
{
var installDirectory = Plugin . LANCommanderClient . Games . GetInstallDirectory ( game ) ;
var manifest = ManifestHelper . Read ( installDirectory , game . Id ) ;
var path = ScriptHelper . GetScriptFilePath ( installDirectory , game . Id , SDK . Enums . ScriptType . KeyChange ) ;
if ( File . Exists ( path ) )
{
2024-01-24 23:44:27 -06:00
Logger ? . Trace ( "Running key change script" ) ;
2024-01-20 21:02:41 -06:00
var script = new PowerShellScript ( ) ;
var key = Plugin . LANCommanderClient . Games . GetAllocatedKey ( manifest . Id ) ;
script . AddVariable ( "InstallDirectory" , installDirectory ) ;
script . AddVariable ( "GameManifest" , manifest ) ;
script . AddVariable ( "DefaultInstallDirectory" , Plugin . Settings . InstallDirectory ) ;
script . AddVariable ( "ServerAddress" , Plugin . Settings . ServerAddress ) ;
script . AddVariable ( "AllocatedKey" , key ) ;
script . UseFile ( path ) ;
2024-02-03 18:32:42 -06:00
GameService . UpdateCurrentKey ( installDirectory , game . Id , key ) ;
2024-01-20 21:02:41 -06:00
return script . Execute ( ) ;
}
return 0 ;
}
}
}