sbcp 0.1.4 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,66 +1,56 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
1
17
  require 'yaml'
2
- require 'logger'
3
18
  require 'tempfile'
4
- require 'rufus-scheduler'
5
19
  require 'celluloid/current'
6
20
  require_relative 'starbound'
7
21
  require_relative 'backup'
8
- require_relative 'parser'
9
22
  require_relative 'logs'
23
+
10
24
  module SBCP
11
25
  class Daemon
26
+ include Celluloid
27
+
12
28
  def initialize
13
- # Loads the config into an instance variable for use in GUI mode
14
29
  @config = YAML.load_file(File.expand_path('../../../config.yml', __FILE__))
15
30
  end
16
31
 
17
- def self.run
18
- # This method is used when starting SBCP from the sbcp executable.
19
- # It's primary purpose is to enable running SBCP in CLI mode.
20
- # The Sinatra web server is completely bypassed in this case.
21
-
22
- # Check to see if the daemon is already running.
23
- # This can happen after a server shut down and it's taking a backup or handling log files.
24
- abort("SBCP is currently running. If you recently shut down Starbound, please allow a few minutes.\nSBCP is likely handling backup or log file operations. Interruption could lead to data loss.") if not Dir.glob('/tmp/sbcp_daemon-pid*').empty?
25
-
26
- # Create a file containing the pid of this process so it can be aborted if neccesary.
27
- pid_file = Tempfile.new('sbcp_daemon-pid')
28
- pid = Process.pid.to_s
29
- pid_file.write(pid)
30
-
31
- # Next, we perform a check to ensure that the server isn't already running.
32
- abort('Starbound is already running.') if not `pidof starbound_server`.empty?
33
-
34
- # We should load the config values into a local variable.
35
- # Since CLI mode does not create an instance of the daemon method,
36
- # we have to set things up separately so they can be used.
37
- config = YAML.load_file(File.expand_path('../../../config.yml', __FILE__))
38
-
32
+ def start
39
33
  # Quick check for invalid config values.
40
- abort('Please run sbcp --setup first.') if config['starbound_directory'].nil?
41
- abort('Error - Invalid starbound directory') if not Dir.exist?(config['starbound_directory']) && Dir.exist?(config['starbound_directory'] + '/giraffe_storage')
42
- abort('Error - Invalid backup directory') if not Dir.exist?(config['backup_directory'])
43
- abort('Error - Invalid backup schedule') if not ['hourly', 2, 3, 4, 6, 8, 12, 'daily', 'restart'].include? config['backup_schedule']
44
- abort('Error - Invalid backup history') if not config['backup_history'] >= 1 || config['backup_history'] == 'none'
45
- abort('Error - Invalid log directory') if not Dir.exist?(config['log_directory'])
46
- abort('Error - Invalid log history') if not config['log_history'].is_a?(Integer) && config['log_history'] >= 1
47
- abort('Error - Invalid log style') if not ['daily', 'restart'].include? config['log_style']
48
- abort('Error - Invalid restart schedule') if not ['none', 'hourly', 2, 3, 4, 6, 8, 12, 'daily'].include? config['restart_schedule']
34
+ raise('Please run setup first.') if @config['starbound_directory'].nil?
35
+ raise('Error - Invalid starbound directory') if not Dir.exist?(@config['starbound_directory']) && Dir.exist?(@config['starbound_directory'] + '/giraffe_storage')
36
+ raise('Error - Invalid backup directory') if not Dir.exist?(@config['backup_directory'])
37
+ raise('Error - Invalid backup schedule') if not ['hourly', 2, 3, 4, 6, 8, 12, 'daily', 'restart'].include? @config['backup_schedule']
38
+ raise('Error - Invalid backup history') if not @config['backup_history'] == 'none' || @config['backup_history'] >= 1
39
+ raise('Error - Invalid log directory') if not Dir.exist?(@config['log_directory'])
40
+ raise('Error - Invalid log history') if not @config['log_history'].is_a?(Integer) && @config['log_history'] >= 1
41
+ raise('Error - Invalid log style') if not ['daily', 'restart'].include? @config['log_style']
42
+ raise('Error - Invalid restart schedule') if not ['none', 'hourly', 2, 3, 4, 6, 8, 12, 'daily'].include? @config['restart_schedule']
49
43
 
50
44
  # Require any present plugins
51
- plugins_directory = "#{config['starbound_directory']}/sbcp/plugins"
45
+ plugins_directory = "#{@config['starbound_directory']}/sbcp/plugins"
52
46
  $LOAD_PATH.unshift(plugins_directory)
53
47
  Dir[File.join(plugins_directory, '*.rb')].each {|file| require File.basename(file) }
54
48
 
55
- # We detach and daemonize this process to prevent a block in the calling executable.
56
- Process.daemon(nochdir=true)
57
-
58
49
  # We create an infinite loop so we can easily restart the server.
59
50
  loop do
60
51
  # Next we invoke the Starbound class to create an instance of the server.
61
52
  # This class will spawn a sub-process containing the server.
62
- server = SBCP::Starbound.new
63
- server.start
53
+ Starbound.new.start
64
54
 
65
55
  # We wait for the server process to conclude before moving on.
66
56
  # This normally occurs after a shutdown, crash, or restart.
@@ -69,47 +59,21 @@ module SBCP
69
59
  # Once the server has finished running, we'll want to age our logfiles.
70
60
  # We'll also take backups here if they've been set to behave that way.
71
61
  # There's probably a better way than sending config values as arguements, but...
72
- SBCP::Logs.age(config['log_directory'], config['log_history']) if config['log_style'] == 'restart'
73
- SBCP::Backup.create_backup if config['backup_schedule'] == 'restart'
62
+ Logs.age(@config['log_directory'], @config['log_history']) if @config['log_style'] == 'restart'
63
+ Backup.create_backup if @config['backup_schedule'] == 'restart'
74
64
 
75
65
  # Now we must determine if the server was closed intentionally.
76
66
  # If the server was shut down on purpose, we don't want to automatically restart it.
77
67
  # If the shutdown file exists, it was an intentional shutdown.
78
68
  # We break the loop which ends the method and closes the Daemon process.
79
- break if not Dir.glob('/tmp/sb-shutdown*').empty?
69
+ if not Dir.glob('/tmp/sb-shutdown*').empty?
70
+ $daemon = nil
71
+ break
72
+ end
80
73
 
81
74
  # This delay is needed or some commands don't report back correctly
82
75
  sleep 5
83
76
  end
84
- ensure
85
- unless pid_file.nil?
86
- pid_file.close
87
- pid_file.unlink
88
- end
89
- end
90
-
91
- private
92
-
93
- # These methods are used only in GUI mode for managing the server.
94
- # In CLI mode, server management is handled via CLI options.
95
-
96
- def start_server
97
- # TODO: Create code that spawns a sub-process containing the server
98
- end
99
-
100
- def restart_server
101
- end
102
-
103
- def force_restart_server
104
- end
105
-
106
- def stop_server
107
- end
108
-
109
- def force_stop_server
110
- end
111
-
112
- def server_status
113
77
  end
114
78
  end
115
79
  end
@@ -0,0 +1,27 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'highline'
18
+
19
+ module SBCP
20
+ class Help
21
+ def self.menu(type)
22
+ case type
23
+ when :main
24
+ end
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,19 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
1
17
  module SBCP
2
18
  class Logs
3
19
  def self.age(directory, days)
@@ -0,0 +1,44 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'sinatra/base'
18
+ require 'sinatra/contrib'
19
+ require 'sinatra/flash'
20
+ require 'securerandom'
21
+ require 'fileutils'
22
+ require 'logger'
23
+ require 'yaml'
24
+
25
+ require_relative 'daemon'
26
+
27
+ module SBCP
28
+ class Panel < Sinatra::Base
29
+ register Sinatra::Contrib
30
+ register Sinatra::Flash
31
+ config = YAML.load_file(File.expand_path('../../config.yml', __FILE__))
32
+ configure do
33
+ set :environment, :development
34
+ set :server, 'thin'
35
+ set :threaded, true
36
+ set :bind, '0.0.0.0'
37
+ use Rack::Session::Cookie,
38
+ :key => 'rack.session',
39
+ :path => '/',
40
+ :expire_after => 3600 # In seconds
41
+ end
42
+ run! if app_file == $0
43
+ end
44
+ end
@@ -1,10 +1,118 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'celluloid/current'
18
+ require 'tempfile'
19
+ require 'yaml'
20
+
21
+ require_relative 'rcon'
22
+
1
23
  module SBCP
2
24
  class Parser
3
25
  include Celluloid
4
26
 
27
+ def initialize
28
+ Starbound::SESSION[:players] = {} if not Starbound::SESSION[:players].empty?
29
+ @config = YAML.load_file(File.expand_path('../../../config.yml', __FILE__))
30
+ sb_config_raw = File.read("#{@config['starbound_directory']}/giraffe_storage/starbound.config")
31
+ @sb_config_parsed = JSON.parse(sb_config_raw)
32
+ @tmp = {}
33
+ if @config['log_style'] == 'daily' then
34
+ @log = Logger.new("#{@config['log_directory']}/starbound.log", 'daily', @config['log_history'])
35
+ elsif @config['log_style'] == 'restart' then
36
+ stamp = "#{Time.now.strftime("%m-%d-%Y-%H-%M-%S")}-starbound"
37
+ @log = Logger.new("#{@config['log_directory']}/#{stamp}.log")
38
+ end
39
+ @log.formatter = proc { |severity, datetime, progname, msg| date_format = datetime.strftime("%H:%M:%S.%N")[0...-6]; "[#{date_format}] #{msg}" }
40
+ @log.level = Logger::INFO
41
+ @log.info("---------- SBCP is starting a new Starbound instance ----------\n")
42
+ end
43
+
44
+ def log(string)
45
+ @log.info(string)
46
+ end
47
+
5
48
  def parse(line)
6
- # This method will eventually be responsible for parsing server output and running other methods based on that output.
7
- # One example would be to emulate in-game server commands.
49
+ if line.include? "Chat:"
50
+ parse_chat(line)
51
+ else
52
+ case line
53
+ when /Starting UniverseServer with UUID:/
54
+ if @sb_config_parsed["runRconServer"] == true
55
+ $rcon = RCON.new(@sb_config_parsed["rconServerPort"], @sb_config_parsed["rconServerPassword"])
56
+ else
57
+ puts "RCON is not enabled. Please check your starbound.config file."
58
+ puts "Some of SBCP's features may not work correctly without RCON."
59
+ end
60
+ when /Logged in account '(.+)' as player '(.+)' from address (.+)/
61
+ id = Starbound::SESSION[:players].count + 1
62
+ @tmp[id] = {
63
+ :account => $1,
64
+ :name => $2,
65
+ :ip => $3,
66
+ :nick => nil
67
+ }
68
+ when /Client '(.+)' <(\d+)> \((.+)\) connected/
69
+ if get_id_from_name($1)
70
+ unless $rcon.nil?
71
+ $rcon.execute("kick $#{$2} \"#{@config['duplicate_kick_msg']}\"")
72
+ id = get_tempid_from_name($1)
73
+ @tmp.delete(id)
74
+ else
75
+ log('DUPLICATE NAME DETECTED BUT RCON DISABLED - CANNOT HANDLE')
76
+ end
77
+ elsif id = get_tempid_from_name($1)
78
+ Starbound::SESSION[:players][$2] = @tmp[id]
79
+ @tmp.delete(id)
80
+ end
81
+ when /Client '(.+)' <(\d+)> \((.+)\) disconnected/
82
+ Starbound::SESSION[:players].delete($2) unless Starbound::SESSION[:players][$2].nil?
83
+ end
84
+ log(line)
85
+ end
86
+ end
87
+
88
+ def close
89
+ @log.close
8
90
  end
91
+
92
+ private
93
+
94
+ def parse_chat(line)
95
+ case line
96
+ when /<(.+)> \/nick (.+)/
97
+ id = get_id_from_name($1)
98
+ Starbound::SESSION[:players][id][:nick] = $2
99
+ end
100
+ log(line)
101
+ end
102
+
103
+ def get_tempid_from_name(name)
104
+ @tmp.each_pair { |k,v|
105
+ return k if v[:name] == name
106
+ }
107
+ return nil
108
+ end
109
+
110
+ def get_id_from_name(name)
111
+ Starbound::SESSION[:players].each_pair { |k,v|
112
+ return k if v[:name] == name
113
+ }
114
+ return nil
115
+ end
116
+
9
117
  end
10
118
  end
@@ -0,0 +1,41 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'steam-condenser'
18
+ require 'json'
19
+ require 'yaml'
20
+
21
+ module SBCP
22
+ class RCON
23
+ def initialize(port, pass)
24
+ original_verbosity = $VERBOSE
25
+ $VERBOSE = nil
26
+ SteamSocket.timeout = 100
27
+ @rcon = SourceServer.new("127.0.0.1:#{port}")
28
+ @rcon.rcon_auth(pass)
29
+ $VERBOSE = original_verbosity
30
+ end
31
+
32
+ def execute(command)
33
+ # We swallow the time out exception here because Steam Condenser expects a reply
34
+ # Starbound doesn't seem to always give a reply, even though the commands work
35
+ begin
36
+ @rcon.rcon_exec(command)
37
+ rescue Exception
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,193 @@
1
+ # SBCP - Starbound Server Management Solution for Linux Servers
2
+ # Copyright (C) 2016 Kazyyk
3
+
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU Affero General Public License as
6
+ # published by the Free Software Foundation, either version 3 of the
7
+ # License, or (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU Affero General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU Affero General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'yaml'
18
+ require 'fileutils'
19
+ require 'highline'
20
+
21
+ module SBCP
22
+ class Setup
23
+ def initialize
24
+ @cli = HighLine.new
25
+ end
26
+
27
+ def run
28
+ config_file = File.expand_path('../../../config.yml', __FILE__)
29
+ config = YAML.load_file(config_file)
30
+ response = @cli.agree('Are you sure you want to run the first-time setup? (y/n)')
31
+ if response # If response is true, continue
32
+ # First, we must attempt to locate where Starbound is installed.
33
+ # This performs a recursive search on the OS for a folder named 'giraffe_storage'
34
+ @cli.newline
35
+ @cli.say('SBCP is starting up...')
36
+ @cli.say('SBCP is attempting to automatically locate Starbound...')
37
+ result = Dir.glob('/**/*/giraffe_storage')
38
+ if result.empty?
39
+ @cli.say('Unable to locate the Starbound installation directory.')
40
+ a = ''
41
+ until Dir.exist?(a) && Dir.exist?(a + '/giraffe_storage') && File.writable?(a)
42
+ a = @cli.ask("Please locate the directory manually and enter it below.\n> ")
43
+ if not Dir.exist?(a) && Dir.exist?(a + '/giraffe_storage')
44
+ @cli.say('Error - This dirctory does not exist or is not a Starbound installation. Try again.')
45
+ elsif not File.writable?(a)
46
+ @cli.say('Error - This dirctory cannot be written to. Check permissions and try again.')
47
+ end
48
+ end
49
+ config['starbound_directory'] = a
50
+ else
51
+ if result.count > 1
52
+ @cli.say('SBCP encountered multiple possible directories.')
53
+ answer = @cli.choose do |menu|
54
+ menu.prompt = "Please select a directory\n> "
55
+ result.each do |dir|
56
+ dir = dir.split("/")[0..3].join("/")
57
+ menu.choice(dir) { abort('Error - This directory cannot be written to. Check permissions and try again.') if not File.writable?(dir); config['starbound_directory'] = dir }
58
+ end
59
+ end
60
+ @cli.say("Starbound installation directory set to \"#{config['starbound_directory']}\"")
61
+ else
62
+ r = result.first.split("/giraffe_storage").first
63
+ @cli.say('SBCP successfully located the Starbound installation directory at:')
64
+ @cli.say("\"#{r}\"")
65
+ abort('Error - This directory cannot be written to. Check permissions and try again.') if not File.writable?(r)
66
+ config['starbound_directory'] = r
67
+ end
68
+ end
69
+ root = config['starbound_directory']
70
+
71
+ @cli.newline
72
+ @cli.say('Welcome to SBCP.')
73
+ @cli.say('You can change any options later by running the config command. (config)')
74
+ if @cli.agree("Would you like to skip setup and just use SBCP's default settings? (See README) (y/n)")
75
+ # So we're running with the defaults. Let's get 'em setup!
76
+ FileUtils.mkdir "#{root}/sbcp" if not Dir.exist?("#{root}/sbcp")
77
+ FileUtils.mkdir "#{root}/sbcp/backups" if not Dir.exist?("#{root}/sbcp/backups")
78
+ FileUtils.mkdir "#{root}/sbcp/logs" if not Dir.exist?("#{root}/sbcp/logs")
79
+ config['backup_directory'] = "#{root}/sbcp/backups"
80
+ config['backup_history'] = 90
81
+ config['backup_schedule'] = "hourly"
82
+ config['log_directory'] = "#{root}/sbcp/logs"
83
+ config['log_history'] = 90
84
+ config['log_style'] = "daily"
85
+ config['duplicate_names'] = false
86
+ config['duplicate_kick_msg'] = "Another player is currently online with your character's name."
87
+ config['restart_schedule'] = 4
88
+ else
89
+ # Backup Settings
90
+ @cli.newline
91
+ @cli.say('--- Automatic Backups ---')
92
+ if @cli.agree('Would you like to enable automatic backups?')
93
+ @cli.newline
94
+ @cli.say('--- Backup Directory Location ---')
95
+ answer = ''
96
+ until Dir.exist?(answer) && File.writable?(answer) || answer == 'default'
97
+ answer = @cli.ask('Where would you like backups to be stored? Type "default" to use default.')
98
+ if not Dir.exist?(answer)
99
+ @cli.say('Error - This dirctory does not exist. Try again.') unless answer == 'default'
100
+ elsif not File.writable?(answer)
101
+ @cli.say('Error - This dirctory cannot be written to. Check permissions and try again.')
102
+ end
103
+ end
104
+ if answer == 'default'
105
+ config['backup_directory'] = "#{root}/sbcp/backups"
106
+ FileUtils.mkdir_p "#{root}/sbcp/backups" if not Dir.exist?("#{root}/sbcp/backups")
107
+ else
108
+ config['backup_directory'] = answer
109
+ end
110
+
111
+ @cli.newline
112
+ @cli.say('--- Backup Schedule ---')
113
+ answer = @cli.ask('How frequently would you like to take backups (in hours)? Type 0 for on restart.', Integer) { |q| q.in = [0, 1, 2, 3, 4, 6, 8, 12, 24] }
114
+ answer = 'restart' if answer == 0
115
+ answer = 'hourly' if answer == 1
116
+ answer = 'daily' if answer == 24
117
+ config['backup_schedule'] = answer
118
+
119
+ @cli.newline
120
+ @cli.say('--- Backup History ---')
121
+ answer = 0
122
+ until answer > 0
123
+ answer = @cli.ask('How long would like to keep the backups (in # of days)?', Integer)
124
+ @cli.say('Value must be greater than zero.') if not answer > 0
125
+ end
126
+ config['backup_history'] = answer
127
+ else
128
+ config['backup_history'] = 'none'
129
+ end
130
+ File.write(config_file, config.to_yaml) # Periodic save
131
+
132
+ # Log Settings
133
+ @cli.newline
134
+ @cli.say('--- Log Directory Location ---')
135
+ answer = ''
136
+ until Dir.exist?(answer) && File.writable?(answer) || answer == 'default'
137
+ answer = @cli.ask('Where would you like log files to be stored? Type "default" to use default.')
138
+ if not Dir.exist?(answer)
139
+ @cli.say('Error - This dirctory does not exist. Try again.') unless answer == 'default'
140
+ elsif not File.writable?(answer)
141
+ @cli.say('Error - This dirctory cannot be written to. Check permissions and try again.')
142
+ end
143
+ end
144
+ if answer == 'default'
145
+ config['log_directory'] = "#{root}/sbcp/logs"
146
+ FileUtils.mkdir_p "#{root}/sbcp/logs" if not Dir.exist?("#{root}/sbcp/logs")
147
+ else
148
+ config['log_directory'] = answer
149
+ end
150
+
151
+ @cli.newline
152
+ @cli.say('--- Log History ---')
153
+ answer = 0
154
+ until answer > 0
155
+ answer = @cli.ask('How long would you like log files to be kept (in days)?', Integer)
156
+ @cli.say('Value must be greater than zero.') if not answer > 0
157
+ end
158
+ config['log_history'] = answer
159
+
160
+ @cli.newline
161
+ @cli.say('--- Log Style ---')
162
+ @cli.say('There are two types of log styles available.')
163
+ @cli.say('Daily: One log file per day. Bigger files, but less of them.')
164
+ @cli.say('Restart: One log file per restart. Smaller files, but more of them.')
165
+ answer = @cli.ask("What kind of log style do you prefer?\n> ") { |q| q.in = ['daily', 'restart'] }
166
+ config['log_style'] = answer
167
+ File.write(config_file, config.to_yaml)
168
+
169
+ # Restart Settings
170
+ @cli.newline
171
+ @cli.say('--- Restart Schedule ---')
172
+ answer = @cli.ask('How frequently would you like the Starbound server to restart (in hours)? Type 0 to disable.', Integer) { |q| q.in = [0, 1, 2, 3, 4, 6, 8, 12, 24] }
173
+ answer = 'none' if response == 0
174
+ answer = 'hourly' if response == 1
175
+ answer = 'daily' if response == 24
176
+ config['restart_schedule'] = answer
177
+ File.write(config_file, config.to_yaml)
178
+ end
179
+
180
+ # Create the plugins directory and readme
181
+ FileUtils.mkdir_p "#{root}/sbcp/plugins" if not Dir.exist?("#{root}/sbcp/plugins")
182
+ File.write("#{root}/sbcp/plugins/README.txt", "You can override SBCP's behavior by writing your own Ruby plugins and placing them here.\nCheck the README on GitHub for more information.")
183
+
184
+ # We save everything back to the config file at the end.
185
+ File.write(config_file, config.to_yaml)
186
+
187
+ @cli.newline
188
+ @cli.say('SBCP has been configured successfully.')
189
+ @cli.say('Type help for a list of commands.')
190
+ end
191
+ end
192
+ end
193
+ end