2014-11-20 13:21:19 +00:00
/ *
* Copyright ( C ) 2010 - 2014 Laurent CLOUET
* Author Laurent CLOUET < laurent . clouet @nopnop.net >
*
2020-05-28 13:28:42 +02:00
* This program is free software ; you can redistribute it and / or
2014-11-20 13:21:19 +00:00
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; version 2
* of the License .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
* /
package com.sheepit.client.standalone ;
2022-07-17 09:48:49 +00:00
import com.sheepit.client.hardware.gpu.hip.HIP ;
2014-11-20 13:21:19 +00:00
import org.kohsuke.args4j.CmdLineException ;
import org.kohsuke.args4j.CmdLineParser ;
import org.kohsuke.args4j.Option ;
2020-04-09 23:39:09 +08:00
import org.kohsuke.args4j.OptionHandlerFilter ;
2014-11-20 13:21:19 +00:00
import java.io.File ;
import java.net.MalformedURLException ;
import java.text.ParseException ;
import java.text.SimpleDateFormat ;
2020-07-28 00:49:36 +10:00
import java.time.LocalDateTime ;
import java.time.format.DateTimeFormatter ;
import java.time.format.DateTimeParseException ;
import java.time.temporal.ChronoUnit ;
2014-11-20 13:21:19 +00:00
import java.util.Calendar ;
import java.util.LinkedList ;
2021-10-02 19:38:04 +00:00
import java.util.List ;
2020-07-28 00:49:36 +10:00
import java.util.regex.Matcher ;
import java.util.regex.Pattern ;
2020-05-28 13:28:42 +02:00
2014-11-20 13:21:19 +00:00
import com.sheepit.client.Client ;
import com.sheepit.client.Configuration ;
import com.sheepit.client.Configuration.ComputeType ;
2020-12-22 21:16:35 -08:00
import com.sheepit.client.Error ;
2014-11-20 13:21:19 +00:00
import com.sheepit.client.Gui ;
import com.sheepit.client.Log ;
import com.sheepit.client.Pair ;
2015-04-08 20:45:54 +01:00
import com.sheepit.client.SettingsLoader ;
2014-11-20 13:21:19 +00:00
import com.sheepit.client.ShutdownHook ;
2019-02-20 13:53:18 +01:00
import com.sheepit.client.Utils ;
2014-11-20 13:21:19 +00:00
import com.sheepit.client.hardware.gpu.GPU ;
import com.sheepit.client.hardware.gpu.GPUDevice ;
2018-08-24 19:46:03 +02:00
import com.sheepit.client.hardware.gpu.nvidia.Nvidia ;
2015-07-06 18:41:28 +01:00
import com.sheepit.client.network.Proxy ;
2020-12-22 21:16:35 -08:00
import com.sheepit.client.os.OS ;
2014-11-20 13:21:19 +00:00
public class Worker {
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_SERVER , usage = " Render-farm server, default https://client.sheepit-renderfarm.com " , metaVar = " URL " , required = false ) private String server = " https://client.sheepit-renderfarm.com " ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_LOGIN , usage = " User's login " , metaVar = " LOGIN " , required = false ) private String login = " " ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_PASSWORD , usage = " User's password or public key (accessible under the Keys tab of the profile page) " , metaVar = " PASSWORD " , required = false ) private String password = " " ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_CACHE_DIR , usage = " Cache/Working directory. Caution, everything in it not related to the render-farm will be removed " , metaVar = " /tmp/cache " , required = false ) private String cache_dir = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_SHARED_ZIP , usage = " Shared directory for downloaded binaries and scenes. Useful when running two or more clients in the same computer/network to download once and render many times. IMPORTANT: This option and value must be identical in ALL clients sharing the directory. " , required = false ) private String sharedDownloadsDir = null ;
2020-10-21 22:03:09 +11:00
2023-01-28 21:13:03 +00:00
@Option ( name = SettingsLoader . ARG_GPU , usage = " Name of the GPU used for the render, for example OPTIX_0 for Nvidia cards. " , metaVar = " OPTIX_0 " , required = false ) private String gpu_device = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_NO_GPU , usage = " Don't detect GPUs " , required = false ) private boolean no_gpu_detection = false ;
2018-08-24 19:46:03 +02:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_COMPUTE_METHOD , usage = " CPU: only use cpu, GPU: only use gpu, CPU_GPU: can use cpu and gpu (not at the same time) if -gpu is not use it will not use the gpu " , metaVar = " CPU " , required = false ) private String method = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_CORES , usage = " Number of cores/threads to use for the render. The minimum is two cores unless your system only has one " , metaVar = " 3 " , required = false ) private int nb_cores = - 1 ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_MEMORY , usage = " Maximum memory allow to be used by renderer, number with unit (800M, 2G, ...) " , required = false ) private String max_ram = null ;
2017-04-04 23:06:31 +02:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_RENDERTIME , usage = " Maximum time allow for each frame (in minutes) " , required = false ) private int max_rendertime = - 1 ;
2017-04-25 13:06:23 +02:00
2024-05-05 10:55:46 +00:00
@Option ( name = SettingsLoader . ARG_LOG_STDOUT , usage = " Display full log " , required = false ) private boolean print_log = false ;
@Option ( name = SettingsLoader . ARG_VERBOSE , usage = " Log DEBUG " , required = false ) private boolean log_debug = false ;
2014-11-20 13:21:19 +00:00
2023-02-10 10:48:14 +01:00
@Option ( name = SettingsLoader . ARG_REQUEST_TIME , usage = " H1:M1-H2:M2,H3:M3-H4:M4 Use the 24h format. For example to request job between 2am-8.30am and 5pm-11pm you should do -request-time 2:00-8:30,17:00-23:00 Caution, it's the requesting job time to get a project, not the working time " , metaVar = " 2:00-8:30,17:00-23:00 " , required = false ) private String request_time = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_SHUTDOWN , usage = " Specify when the client will close and the host computer will shut down in a proper way. The time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now. " , metaVar = " DATETIME or +N " , required = false ) private String shutdown = null ;
2020-07-28 00:49:36 +10:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_SHUTDOWN_MODE , usage = " Indicates if the shutdown process waits for the upload queue to finish (wait) or interrupt all the pending tasks immediately (hard). The default shutdown mode is wait. " , metaVar = " MODE " , required = false ) private String shutdownMode = null ;
2020-07-28 00:49:36 +10:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_PROXY , usage = " URL of the proxy " , metaVar = " http://login:password@host:port " , required = false ) private String proxy = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_EXTRAS , usage = " Extras data push on the authentication request " , required = false ) private String extras = null ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_UI , usage = " Specify the user interface to use, default ' " + GuiSwing . type + " ', available ' " + GuiTextOneLine . type + " ', ' "
2020-05-28 13:28:42 +02:00
+ GuiText . type + " ', ' " + GuiSwing . type + " ' (graphical) " , required = false ) private String ui_type = null ;
2014-11-20 17:50:23 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_CONFIG , usage = " Specify the configuration file " , required = false ) private String config_file = null ;
2015-04-07 20:07:53 +01:00
2024-04-14 09:27:52 +00:00
@Option ( name = SettingsLoader . ARG_LOG_DIRECTORY , usage = " Specify the log directory " , required = false ) private String log_dir = null ;
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_VERSION , usage = " Display application version " , required = false , handler = VersionParameterHandler . class ) private VersionParameterHandler versionHandler ;
2014-11-20 13:21:19 +00:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_SHOW_GPU , usage = " Print available GPU devices and exit " , required = false , handler = ListGpuParameterHandler . class ) private ListGpuParameterHandler listGpuParameterHandler ;
2017-01-19 18:08:26 +01:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_NO_SYSTRAY , usage = " Don't use SysTray " , required = false ) private boolean useSysTray = false ;
2016-04-28 19:37:05 +02:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_PRIORITY , usage = " Set render process priority (19 lowest to -19 highest) " , required = false ) private int priority = 19 ;
2017-01-05 09:58:27 +01:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_TITLE , usage = " Custom title for the GUI Client " , required = false ) private String title = " SheepIt Render Farm " ;
2020-05-28 13:28:42 +02:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_THEME , usage = " Specify the theme to use for the graphical client, default 'light', available 'light', 'dark' " , required = false ) private String theme = null ;
2020-05-28 13:28:42 +02:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_HOSTNAME , usage = " Set a custom hostname name (name change will be lost when client is closed) " , required = false ) private String hostname = null ;
2020-09-09 20:18:46 +10:00
2021-10-02 19:38:04 +00:00
@Option ( name = SettingsLoader . ARG_HEADLESS , usage = " Mark your client manually as headless to block Eevee projects " , required = false ) private boolean headless = java . awt . GraphicsEnvironment . isHeadless ( ) ;
2024-03-26 14:08:46 +00:00
@Option ( name = SettingsLoader . ARG_DISABLE_LARGE_DOWNLOADS , usage = " Disable download of larger projects to preserve internet traffic " , required = false ) private boolean disableLargeDownloads = false ;
2021-07-16 23:37:24 +00:00
2014-11-20 13:21:19 +00:00
public static void main ( String [ ] args ) {
2020-12-22 21:16:35 -08:00
if ( OS . getOS ( ) = = null ) {
System . err . println ( Error . humanString ( Error . Type . OS_NOT_SUPPORTED ) ) ;
System . exit ( 1 ) ;
}
2014-11-20 13:21:19 +00:00
new Worker ( ) . doMain ( args ) ;
}
public void doMain ( String [ ] args ) {
CmdLineParser parser = new CmdLineParser ( this ) ;
try {
parser . parseArgument ( args ) ;
}
catch ( CmdLineException e ) {
System . err . println ( e . getMessage ( ) ) ;
System . err . println ( " Usage: " ) ;
parser . printUsage ( System . err ) ;
System . err . println ( ) ;
2020-04-09 23:39:09 +08:00
System . err . println ( " Example: java " + this . getClass ( ) . getName ( ) + " " + parser . printExample ( OptionHandlerFilter . REQUIRED ) ) ;
2014-11-20 13:21:19 +00:00
return ;
}
2020-06-21 02:31:31 +10:00
ComputeType compute_method = null ;
2014-11-20 13:21:19 +00:00
Configuration config = new Configuration ( null , login , password ) ;
2024-05-05 10:55:46 +00:00
config . setDebugLevel ( log_debug ) ;
2014-11-20 13:21:19 +00:00
config . setPrintLog ( print_log ) ;
2023-10-19 11:09:08 +00:00
config . setPriority ( priority ) ;
2020-05-28 13:28:42 +02:00
config . setDetectGPUs ( ! no_gpu_detection ) ;
2014-11-20 13:21:19 +00:00
2020-10-21 22:03:09 +11:00
if ( sharedDownloadsDir ! = null ) {
File dir = new File ( sharedDownloadsDir ) ;
2022-02-19 12:12:10 +00:00
if ( dir . exists ( ) = = false & & dir . mkdirs ( ) ) {
Log . getInstance ( config ) . debug ( " created shared-zip directory " + dir ) ;
}
else if ( dir . exists ( ) = = false ) {
System . err . println ( " ERROR: The shared-zip directory " + dir + " does not exist and cannot be automatically created " ) ;
return ;
}
if ( dir . canWrite ( ) = = false ) {
System . err . println ( " ERROR: The shared-zip directory " + dir + " must be writeable " ) ;
2020-10-21 22:03:09 +11:00
return ;
}
config . setSharedDownloadsDirectory ( dir ) ;
}
2014-11-20 13:21:19 +00:00
if ( cache_dir ! = null ) {
2020-12-29 15:11:22 +00:00
Pattern cache_dirValidator = Pattern . compile ( " ^( \\ /| \\ \\ |[a-z]:)?[a-z0-9 \\ / \\ \\ \\ s-_.]+$ " , Pattern . CASE_INSENSITIVE ) ;
2020-12-11 20:30:16 +00:00
Matcher cache_dirCandidate = cache_dirValidator . matcher ( cache_dir ) ;
if ( cache_dirCandidate . find ( ) ) {
File a_dir = new File ( cache_dir ) ;
a_dir . mkdirs ( ) ;
if ( a_dir . isDirectory ( ) & & a_dir . canWrite ( ) ) {
config . setCacheDir ( a_dir ) ;
}
else {
System . err . println ( " ERROR: The entered cache path is either not a directory or is not writable! " ) ;
System . exit ( 2 ) ;
}
}
else {
System . err . println ( " ERROR: The entered cache path (-cache-dir parameter) contains invalid characters. Allowed characters are a-z, A-Z, 0-9, /, \\ , ., - and _ " ) ;
System . exit ( 2 ) ;
2014-11-20 13:21:19 +00:00
}
}
2020-04-15 17:40:51 +10:00
// We set a hard limit of 3 concurrent uploads. As the server doesn't allocate more than 3 concurrent jobs to
// a single session to avoid any client taking too many frames and not validating them, we throttle the uploads.
// If we don't set this limit, in a computer with slow uploads the server will return a "no job available" when
// the 4th concurrent job is requested and that will put the client in "wait" mode for some random time. To
// avoid that situation we set this limit.
config . setMaxUploadingJob ( 3 ) ;
2014-11-20 13:21:19 +00:00
2020-06-09 14:47:39 +10:00
// Store the SysTray preference from the user. Please note that we must ! the value of the variable because the way args4j works. If the --no-systray
// parameter is detected, args4j will store (boolean)true in the useSysTray variable but we want to store (boolean)false in the configuration class
// for further checks.
config . setUseSysTray ( ! useSysTray ) ;
2021-07-16 23:37:24 +00:00
config . setHeadless ( headless ) ;
2014-11-20 13:21:19 +00:00
if ( gpu_device ! = null ) {
2023-01-28 14:44:04 +00:00
//catch all the outdated configs
if ( gpu_device . startsWith ( HIP . TYPE ) ) {
System . err . println ( " ERROR: HIP devices are not supported " ) ;
}
else if ( gpu_device . startsWith ( Nvidia . TYPE ) = = false ) {
System . err . println ( " ERROR: The entered GPU_ID is invalid. The GPU_ID should look like ' " + Nvidia . TYPE + " _#. Please use the proper GPU_ID from the GPU list below \ n " ) ;
2020-05-28 23:05:19 +10:00
showGPUList ( parser ) ;
2014-11-20 13:21:19 +00:00
}
2020-05-28 23:05:19 +10:00
2014-11-20 13:21:19 +00:00
GPUDevice gpu = GPU . getGPUDevice ( gpu_device ) ;
if ( gpu = = null ) {
2020-05-28 23:05:19 +10:00
System . err . println ( " ERROR: The entered GPU_ID is invalid. Please use the proper GPU_ID from the GPU list below \ n " ) ;
showGPUList ( parser ) ;
2014-11-20 13:21:19 +00:00
}
2019-08-07 22:17:59 +02:00
config . setGPUDevice ( gpu ) ;
2014-11-20 13:21:19 +00:00
}
if ( request_time ! = null ) {
String [ ] intervals = request_time . split ( " , " ) ;
if ( intervals ! = null ) {
2019-08-07 22:17:59 +02:00
config . setRequestTime ( new LinkedList < Pair < Calendar , Calendar > > ( ) ) ;
2014-11-20 13:21:19 +00:00
SimpleDateFormat timeFormat = new SimpleDateFormat ( " HH:mm " ) ;
for ( String interval : intervals ) {
String [ ] times = interval . split ( " - " ) ;
if ( times ! = null & & times . length = = 2 ) {
Calendar start = Calendar . getInstance ( ) ;
Calendar end = Calendar . getInstance ( ) ;
try {
start . setTime ( timeFormat . parse ( times [ 0 ] ) ) ;
end . setTime ( timeFormat . parse ( times [ 1 ] ) ) ;
}
catch ( ParseException e ) {
2020-05-28 23:05:19 +10:00
System . err . println ( String . format (
" ERROR: The entered time slot (-request-time parameter) doesn't seem to be valid. Please check the format is correct [%s] " ,
e . getMessage ( ) ) ) ;
2014-11-20 13:21:19 +00:00
System . exit ( 2 ) ;
}
if ( start . before ( end ) ) {
2019-08-07 22:17:59 +02:00
config . getRequestTime ( ) . add ( new Pair < Calendar , Calendar > ( start , end ) ) ;
2014-11-20 13:21:19 +00:00
}
else {
2023-06-08 19:33:28 +02:00
// example: for a night render, 20:00,07:00 we will create 2 intervals for it: 20:00-23:59 and 00:00-07:00
try {
Calendar midnightBefore = Calendar . getInstance ( ) ;
midnightBefore . setTime ( timeFormat . parse ( " 23:59 " ) ) ;
Calendar midnightAfter = Calendar . getInstance ( ) ;
midnightAfter . setTime ( timeFormat . parse ( " 00:00 " ) ) ;
config . getRequestTime ( ) . add ( new Pair < Calendar , Calendar > ( start , midnightBefore ) ) ;
config . getRequestTime ( ) . add ( new Pair < Calendar , Calendar > ( midnightAfter , end ) ) ;
}
catch ( ParseException e ) {
System . err . println ( " Failed to parse date " ) ;
System . exit ( 2 ) ;
}
2014-11-20 13:21:19 +00:00
}
}
}
}
}
2014-11-30 23:49:17 +00:00
if ( nb_cores < - 1 | | nb_cores = = 0 ) { // -1 is the default
2020-05-28 23:05:19 +10:00
System . err . println ( " ERROR: The entered number of CPU cores (-cores parameter) is not valid. Please enter a number greater than zero " ) ;
2014-11-20 13:21:19 +00:00
return ;
}
else {
2019-08-07 22:17:59 +02:00
config . setNbCores ( nb_cores ) ;
2014-11-20 13:21:19 +00:00
}
2019-02-20 13:53:18 +01:00
if ( max_ram ! = null ) {
try {
2021-11-16 14:51:53 +00:00
config . setMaxAllowedMemory ( Utils . parseNumber ( max_ram ) / 1024 ) ; // internal value is in KiB
2019-02-20 13:53:18 +01:00
}
2024-06-03 14:02:30 +00:00
catch ( IllegalStateException e ) {
2020-05-28 23:05:19 +10:00
System . err . println (
String . format ( " ERROR: The entered value of maximum memory (-memory parameter) doesn't seem to be a valid number [%s] " , e . getMessage ( ) ) ) ;
2019-02-20 13:53:18 +01:00
return ;
}
2017-04-04 23:06:31 +02:00
}
2017-04-25 13:06:23 +02:00
if ( max_rendertime > 0 ) {
config . setMaxRenderTime ( max_rendertime * 60 ) ;
}
2014-11-20 13:21:19 +00:00
if ( method ! = null ) {
2015-03-31 22:04:09 +01:00
try {
compute_method = ComputeType . valueOf ( method ) ;
2014-11-20 13:21:19 +00:00
}
2015-03-31 22:04:09 +01:00
catch ( IllegalArgumentException e ) {
2020-05-28 23:05:19 +10:00
System . err . println ( String . format (
" ERROR: The entered compute method (-compute-method parameter) is not valid. Available values are CPU, GPU or CPU_GPU [%s] " ,
e . getMessage ( ) ) ) ;
2014-11-20 13:21:19 +00:00
System . exit ( 2 ) ;
}
}
2014-12-02 20:21:12 +00:00
else {
2020-06-21 02:31:31 +10:00
if ( config . getGPUDevice ( ) ! = null ) {
2015-04-07 20:05:48 +01:00
compute_method = ComputeType . GPU ;
2014-12-02 20:21:12 +00:00
}
}
2014-11-20 13:21:19 +00:00
if ( proxy ! = null ) {
try {
2015-07-06 18:41:28 +01:00
Proxy . set ( proxy ) ;
2014-11-20 13:21:19 +00:00
}
catch ( MalformedURLException e ) {
2020-05-28 23:05:19 +10:00
System . err . println ( String . format ( " ERROR: The entered proxy URL (-proxy parameter) doesn't seem to have the right format. Please check it [%s] " ,
e . getMessage ( ) ) ) ;
2014-11-20 13:21:19 +00:00
System . exit ( 2 ) ;
}
}
if ( extras ! = null ) {
config . setExtras ( extras ) ;
}
2020-06-21 02:31:31 +10:00
if ( compute_method ! = null ) {
if ( compute_method = = ComputeType . CPU & & config . getGPUDevice ( ) ! = null ) {
System . err . println (
2020-05-28 23:05:19 +10:00
" ERROR: The compute method is set to use CPU only, but a GPU has also been specified. Change the compute method to CPU_GPU or remove the GPU " ) ;
2020-06-21 02:31:31 +10:00
System . exit ( 2 ) ;
}
else if ( compute_method = = ComputeType . CPU_GPU & & config . getGPUDevice ( ) = = null ) {
System . err . println (
2020-05-28 23:05:19 +10:00
" ERROR: The compute method is set to use both CPU and GPU, but no GPU has been specified. Change the compute method to CPU or add a GPU (via -gpu parameter) " ) ;
2020-06-21 02:31:31 +10:00
System . exit ( 2 ) ;
}
else if ( compute_method = = ComputeType . GPU & & config . getGPUDevice ( ) = = null ) {
System . err . println ( " ERROR: The compute method is set to use GPU only, but not GPU has been specified. Please add a GPU (via -gpu parameter) " ) ;
System . exit ( 2 ) ;
}
else if ( compute_method = = ComputeType . CPU ) {
config . setGPUDevice ( null ) ; // remove the GPU
}
2014-12-02 20:21:12 +00:00
}
config . setComputeMethod ( compute_method ) ;
2014-11-20 13:21:19 +00:00
2015-04-08 20:37:53 +01:00
if ( ui_type ! = null ) {
config . setUIType ( ui_type ) ;
}
2020-04-14 23:24:28 +10:00
if ( theme ! = null ) {
2024-06-07 14:35:47 +00:00
if ( " light " . equals ( theme ) = = false & & " dark " . equals ( theme ) = = false ) {
2020-05-28 23:05:19 +10:00
System . err . println ( " ERROR: The entered theme (-theme parameter) doesn't exist. Please choose either 'light' or 'dark' " ) ;
2020-04-14 23:24:28 +10:00
System . exit ( 2 ) ;
}
2020-05-28 13:28:42 +02:00
2020-04-14 23:24:28 +10:00
config . setTheme ( this . theme ) ;
}
2020-05-28 13:28:42 +02:00
2020-07-28 00:49:36 +10:00
// Shutdown process block
if ( shutdown ! = null ) {
Pattern absoluteTimePattern = Pattern . compile ( " ^([12] \\ d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12] \\ d|3[01]))T([01]?[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]$ " ) ;
Pattern relativeTimePattern = Pattern . compile ( " ^ \\ +([0-9]{2,4})$ " ) ;
LocalDateTime shutdownTime = null ;
Matcher timeAbsolute = absoluteTimePattern . matcher ( shutdown ) ;
Matcher timeRelative = relativeTimePattern . matcher ( shutdown ) ;
if ( timeAbsolute . find ( ) ) {
if ( ( shutdownTime = shutdownTimeParse ( shutdown ) ) ! = null ) {
long diffInMillies = ChronoUnit . MILLIS . between ( LocalDateTime . now ( ) , shutdownTime ) ;
if ( diffInMillies < 0 ) {
System . err . println ( String
. format ( " \ nERROR: The entered shutdown time (%s) is a date on the past. Shutdown time must be at least 30 minutes from now " ,
shutdown ) ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
else if ( diffInMillies < 10 * 60 * 1000 ) { // 10 minutes
System . err . println ( String . format (
" \ nERROR: The specified shutdown time (%s) is expected to happen in less than 10 minutes. Shutdown time must be at least 30 minutes from now " ,
shutdown ) ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
config . setShutdownTime ( diffInMillies ) ;
}
else {
System . err . println ( String . format (
" \ nERROR: The format of the entered shutdown time (%s) is not correct. \ nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes) " ,
shutdown ) ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
}
else if ( timeRelative . find ( ) ) {
int minutesUntilShutdown = Integer . parseInt ( timeRelative . group ( 1 ) ) ;
config . setShutdownTime ( minutesUntilShutdown * 60 * 1000 ) ;
shutdownTime = LocalDateTime . now ( ) . plusMinutes ( minutesUntilShutdown ) ;
}
else {
System . err . println ( String . format (
" \ nERROR: The time especified (%s) is less than 10 minutes or the format is not correct. \ nThe time argument can have two different formats: an absolute date and time in the format yyyy-mm-ddThh:mm:ss (24h format) or a relative time in the format +m where m is the number of minutes from now (min. +10 minutes, max. +9999 minutes) " ,
shutdown ) ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
if ( shutdownMode ! = null ) {
2024-06-07 14:35:47 +00:00
if ( " wait " . equalsIgnoreCase ( shutdownMode ) | | " hard " . equalsIgnoreCase ( shutdownMode ) ) {
2020-07-28 00:49:36 +10:00
config . setShutdownMode ( shutdownMode . toLowerCase ( ) ) ;
}
else {
System . err
. println ( String . format ( " ERROR: The entered shutdown-mode (%s) is invalid. Please enter wait or hard shutdown mode. " , shutdownMode ) ) ;
System . err . println ( " - Wait: the shutdown process is initiated once the current job and all the queued uploads are finished. " ) ;
System . err
. println ( " - Hard: Then shutdown process is executed immediately. Any ongoing rendering process or upload queues will be cancelled. " ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
}
else {
// if no shutdown mode specified, then set "wait" mode by default
config . setShutdownMode ( " wait " ) ;
}
System . out . println ( " ============================================================================== " ) ;
2024-06-07 14:35:47 +00:00
if ( " wait " . equals ( config . getShutdownMode ( ) ) ) {
2020-07-28 00:49:36 +10:00
System . out . println ( String . format (
" WARNING! \ n \ nThe client will stop requesting new jobs at %s. \ nTHE EFFECTIVE SHUTDOWN MIGHT OCCUR LATER THAN THE REQUESTED TIME AS THE UPLOAD \ nQUEUE MUST BE FULLY UPLOADED BEFORE THE SHUTDOWN PROCESS STARTS. \ n \ nIf you want to shutdown the computer sharp at the specified time, please \ ninclude the '-shutdown-mode hard' parameter in the application call " ,
shutdownTime ) ) ;
}
else {
System . out . println ( String . format (
" WARNING! \ n \ nThe client will initiate the shutdown process at %s. \ nALL RENDERS IN PROGRESS AND UPLOAD QUEUES WILL BE CANCELED. \ n \ nIf you prefer to shutdown the computer once the pending jobs are completed, \ nplease include the '-shutdown-mode wait' parameter in the application call " ,
shutdownTime ) ) ;
}
System . out . println ( " ============================================================================== \ n " ) ;
}
else if ( shutdown = = null & & shutdownMode ! = null ) {
System . err . println (
" ERROR: The shutdown-mode parameter cannot be entered alone. Please make sure that you also enter a valid shutdown time (using -shutdown parameter) " ) ;
System . err . println ( " Aborting " ) ;
System . exit ( 2 ) ;
}
2015-04-07 20:07:53 +01:00
if ( config_file ! = null ) {
if ( new File ( config_file ) . exists ( ) = = false ) {
2020-05-28 23:05:19 +10:00
System . err . println (
" ERROR: The entered configuration file (-config parameter) cannot be loaded. Please check that you've entered an existing filename " ) ;
2015-04-07 20:07:53 +01:00
System . exit ( 2 ) ;
}
2019-08-07 22:17:59 +02:00
config . setConfigFilePath ( config_file ) ;
2015-04-07 20:07:53 +01:00
}
2024-03-26 14:08:46 +00:00
config . setDisableLargeDownloads ( disableLargeDownloads ) ;
2021-07-16 23:37:24 +00:00
SettingsLoader settingsLoader = new SettingsLoader ( config_file ) ;
2021-10-02 19:38:04 +00:00
settingsLoader . merge ( config , true ) ;
if ( args . length > 0 ) {
settingsLoader . markLaunchSettings ( List . of ( args ) ) ;
}
2024-04-14 09:27:52 +00:00
// config file and log file should be next to each other
String configFilePath = config . getConfigFilePath ( ) ! = null ? config . getConfigFilePath ( ) : OS . getOS ( ) . getDefaultConfigFilePath ( ) ;
config . setLogDirectory ( log_dir ! = null ? log_dir : ( new File ( configFilePath ) . getParent ( ) ) ) ;
2021-12-30 17:46:20 +00:00
Log . getInstance ( config ) . debug ( " client version " + Configuration . jarVersion ) ;
2014-11-20 13:21:19 +00:00
2020-09-09 20:18:46 +10:00
// Hostname change will overwrite the existing one (default or read from configuration file) but changes will be lost when the client closes
if ( hostname ! = null ) {
Pattern hostnameValidator = Pattern . compile ( " [^a-z0-9-_] " , Pattern . CASE_INSENSITIVE ) ;
Matcher hostnameCandidate = hostnameValidator . matcher ( hostname ) ;
if ( hostnameCandidate . find ( ) ) {
System . err . println (
" ERROR: The entered hostname (-hostname parameter) contains invalid characters. Allowed hostname characters are a-z, A-Z, 0-9, - and _ " ) ;
System . exit ( 2 ) ;
}
else {
config . setHostname ( hostname ) ;
}
}
2014-11-20 17:50:23 +00:00
Gui gui ;
2015-04-08 20:37:53 +01:00
String type = config . getUIType ( ) ;
if ( type = = null ) {
type = " swing " ;
}
switch ( type ) {
2015-04-08 20:32:24 +01:00
case GuiTextOneLine . type :
2019-08-07 22:17:59 +02:00
if ( config . isPrintLog ( ) ) {
2020-05-28 23:05:19 +10:00
System . err . println (
" ERROR: The oneLine UI and the --verbose parameter cannot be used at the same time. Please either change the ui to text or remove the verbose mode " ) ;
2015-01-28 20:11:57 +00:00
System . exit ( 2 ) ;
}
gui = new GuiTextOneLine ( ) ;
break ;
2015-04-08 20:37:53 +01:00
case GuiText . type :
gui = new GuiText ( ) ;
break ;
default :
2015-04-08 20:32:24 +01:00
case GuiSwing . type :
2015-01-28 20:11:57 +00:00
if ( java . awt . GraphicsEnvironment . isHeadless ( ) ) {
2020-05-28 23:05:19 +10:00
System . err . println ( " ERROR: Your current configuration doesn't support graphical UI. " ) ;
System . err . println ( " Please use one of the text-based UIs provided (using -ui " + GuiTextOneLine . type + " or -ui " + GuiText . type + " ) " ) ;
2015-01-28 20:11:57 +00:00
System . exit ( 3 ) ;
}
2020-06-09 14:47:39 +10:00
gui = new GuiSwing ( config . isUseSysTray ( ) , title ) ;
2021-07-16 23:37:24 +00:00
( ( GuiSwing ) gui ) . setSettingsLoader ( settingsLoader ) ;
2015-01-28 20:11:57 +00:00
break ;
2014-11-20 17:50:23 +00:00
}
2014-11-20 13:21:19 +00:00
Client cli = new Client ( gui , config , server ) ;
2015-01-15 23:20:17 +01:00
gui . setClient ( cli ) ;
2014-11-20 13:21:19 +00:00
ShutdownHook hook = new ShutdownHook ( cli ) ;
hook . attachShutDownHook ( ) ;
2015-01-15 23:20:17 +01:00
gui . start ( ) ;
2014-11-20 13:21:19 +00:00
}
2020-05-28 23:05:19 +10:00
private void showGPUList ( CmdLineParser parser ) {
try {
parser . parseArgument ( " --show-gpu " ) ;
}
catch ( CmdLineException e ) {
System . err . println ( String . format ( " ERROR: Unable to parse the provided parameter [%s] " , e . getMessage ( ) ) ) ;
}
}
2020-07-28 00:49:36 +10:00
private LocalDateTime shutdownTimeParse ( String shutdownTime ) {
try {
DateTimeFormatter df = DateTimeFormatter . ofPattern ( " yyyy-MM-dd'T'HH:mm:ss " ) ;
return LocalDateTime . parse ( shutdownTime , df ) ;
}
catch ( DateTimeParseException e ) {
e . printStackTrace ( ) ;
return null ;
}
}
2014-11-20 13:21:19 +00:00
}