minecraftctl 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -13,7 +13,7 @@ group :development do
13
13
  gem "reek", "~> 1.2.8"
14
14
  gem "roodi", "~> 2.1.0"
15
15
  gem "sinatra", ">= 1.2.6"
16
- gem "main", ">= 4.7.3"
16
+ gem "micro-optparse", "~> 1"
17
17
  gem "httpclient", ">= 2.2.1"
18
18
  gem "mongrel", ">= 1.1.5"
19
19
  gem "mongrel", ">= 1.1.5"
data/Gemfile.lock CHANGED
@@ -1,14 +1,11 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
- arrayfields (4.7.4)
5
4
  cgi_multipart_eof_fix (2.5.0)
6
- chronic (0.6.4)
7
5
  daemon (1.1.0)
8
6
  daemons (1.1.4)
9
7
  diff-lcs (1.1.3)
10
8
  fastthread (1.0.7)
11
- fattr (2.2.0)
12
9
  gem_plugin (0.2.3)
13
10
  git (1.2.5)
14
11
  httpclient (2.2.1)
@@ -16,12 +13,7 @@ GEM
16
13
  bundler (~> 1.0)
17
14
  git (>= 1.2.5)
18
15
  rake
19
- main (4.7.7)
20
- arrayfields (~> 4.7.4)
21
- chronic (~> 0.6.2)
22
- fattr (~> 2.2.0)
23
- map (~> 4.3.0)
24
- map (4.3.0)
16
+ micro-optparse (1.1.5)
25
17
  mongrel (1.1.5)
26
18
  cgi_multipart_eof_fix (>= 2.4)
27
19
  daemons (>= 1.0.3)
@@ -63,7 +55,7 @@ DEPENDENCIES
63
55
  daemon (~> 1)
64
56
  httpclient (>= 2.2.1)
65
57
  jeweler (~> 1.6.4)
66
- main (>= 4.7.3)
58
+ micro-optparse (~> 1)
67
59
  mongrel (>= 1.1.5)
68
60
  rcov
69
61
  reek (~> 1.2.8)
data/README.rdoc CHANGED
@@ -46,6 +46,9 @@ Some cURL examples:
46
46
 
47
47
  == Changelog
48
48
 
49
+ === v2.0.2
50
+ * better help message and switch related error handling
51
+
49
52
  === v2.0.1
50
53
  * increased startup and command timeout
51
54
  * startup and command timeout values are now configurable via -t and -s switches
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.0.1
1
+ 2.0.2
data/bin/minecraftctl CHANGED
@@ -1,54 +1,50 @@
1
1
  #!/usr/bin/ruby
2
2
  require 'rubygems'
3
- require 'main'
3
+ require 'micro-optparse'
4
4
  require 'httpclient'
5
5
  require 'thread'
6
6
  Thread.abort_on_exception = true
7
7
 
8
- Main do
9
- description 'controls minecraft server via minecraftctlserver'
8
+ options = Parser.new do |p|
9
+ p.banner = <<EOF
10
+ Controls minecraft server started via minecraftctlserver
10
11
 
11
- option 'server', 's' do
12
- default 'localhost'
13
- description 'minecraft control server address'
14
- argument_required
15
- end
12
+ Usage: #{$0} [options] command [arguments]
16
13
 
17
- option 'port', 'p' do
18
- default 25560
19
- description 'minecraft control server port'
20
- argument_required
21
- end
14
+ Command has to start with '/'
15
+ For available server commands type '#{$0} /'
22
16
 
23
- argument 'command' do
24
- description 'command to send to control server: try "/" for available commands'
25
- validate{|cmd| cmd =~ /^\//}
26
- end
17
+ Options:
18
+ EOF
19
+ p.option :server, 'minecraft control server address', :default => 'localhost'
20
+ p.option :port, 'minecraft control server port', :default => 25560
21
+ end.process!
27
22
 
28
- argument 'arguments' do
29
- required false
30
- arity -1
31
- end
23
+ command = ARGV.shift
24
+ unless command
25
+ puts "command not specified, see '#{$0} -h' for help"
26
+ exit 1
27
+ end
28
+ unless command =~ /^\//
29
+ puts "invalid command '#{command}', command has to start with '/'. Type '#{$0} /' to see available server commands"
30
+ exit 1
31
+ end
32
+
33
+ args = ARGV
32
34
 
33
- run do
34
- c = HTTPClient.new
35
-
36
- begin
37
- command = params['command'].value
38
- args = params['arguments'].values
39
-
40
- if args.empty?
41
- c.get_async("http://#{params['server'].value}:#{params['port'].value}#{command}").pop.content.each do |line|
42
- puts line
43
- end
44
- else
45
- c.post_async("http://#{params['server'].value}:#{params['port'].value}#{command}", args.join("\n")).pop.content.each do |line|
46
- puts line
47
- end
48
- end
49
- rescue Errno::ECONNREFUSED => e
50
- puts "Falied to connect to minecraftctlserver; please run minecraftctlserver first: #{e}"
35
+ c = HTTPClient.new
36
+
37
+ begin
38
+ if args.empty?
39
+ c.get_async("http://#{options[:server]}:#{options[:port]}#{command}").pop.content.each do |line|
40
+ puts line
41
+ end
42
+ else
43
+ c.post_async("http://#{options[:server]}:#{options[:port]}#{command}", args.join("\n")).pop.content.each do |line|
44
+ puts line
51
45
  end
52
46
  end
47
+ rescue SocketError, Errno::ECONNREFUSED => e
48
+ puts "Falied to connect to minecraft control server (to start it run 'minecraftctlserver .' in your minecraft install directory): #{e}"
53
49
  end
54
50
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/ruby
2
2
  require 'rubygems'
3
- require 'main'
3
+ require 'micro-optparse'
4
4
  require 'open3'
5
5
  require 'thread'
6
6
  require 'pathname'
@@ -124,180 +124,168 @@ class APIBuilder
124
124
  end
125
125
  end
126
126
 
127
- Main do
128
- description 'lounches Minecraft server that can be controlled via HTTP'
127
+ options = Parser.new do |p|
128
+ p.banner = <<EOF
129
+ Lounches minecraft server that can be controlled via HTTP
130
+
131
+ Usage: #{$0} [options] directory
132
+
133
+ Directory has to be a path to the minecraft server install directory (where you have 'minecraft_server.jar'); use '.' for current directory
134
+
135
+ Options:
136
+ EOF
137
+ p.option :command, 'command to be used to span server', :default => 'java -Xms256M -Xmx512M -Djava.net.preferIPv4Stack=true -jar minecraft_server.jar nogui'
138
+ p.option :port, 'TCP port number on which the HTTP control server will be listening', :default => 25560
139
+ p.option :bind, 'IP address of interface on which the HTTP control server will be listening on; use 0.0.0.0 to bind to all interfaces', :default => '127.0.0.1'
140
+ p.option :foreground, 'don\'t daemonize, stay in foreground'
141
+ p.option :pid_file, 'pid file relative to minecraft server direcotry', :default => 'minecraftctlserver.pid', :short => 'P'
142
+ p.option :log_file, 'log file relative to minecraft server directory where daemon messages will be written', :default => 'minecraftctlserver.log'
143
+ p.option :startup_timeout, 'time in seconds after which the control server will exit with error while waiting for minecraft to start up', :default => 120
144
+ p.option :command_timeout, 'time in seconds after which control server will exit with error while waiting for minecraft console command to finish', :default => 40
145
+ end.process!
146
+
147
+ directory = ARGV.shift
148
+ unless directory
149
+ puts "directory not specified, see '#{$0} -h' for help"
150
+ exit 1
151
+ end
129
152
 
130
- option 'command', 'c' do
131
- default 'java -Xms256M -Xmx512M -Djava.net.preferIPv4Stack=true -jar minecraft_server.jar nogui'
132
- description 'command to be used to span server'
133
- argument_required
134
- end
153
+ pid_file = Pathname.new(options[:pid_file])
154
+ log_file = Pathname.new(options[:log_file])
135
155
 
136
- option 'port', 'p' do
137
- default 25560
138
- description 'port on which the control HTTP server should be running'
139
- argument_required
140
- end
156
+ Dir.chdir(directory)
141
157
 
142
- option 'bind', 'b' do
143
- description 'IP address of interface to listen for connections on; use 0.0.0.0 to bind to all interfaces'
144
- default '127.0.0.1'
145
- argument_required
158
+ begin
159
+ if options[:foreground]
160
+ Daemon.lock(pid_file)
161
+ else
162
+ Daemon.daemonize(pid_file, log_file)
146
163
  end
164
+ rescue => e
165
+ puts "Error: failed to start daemon/lock pid: #{e}"
166
+ exit 2
167
+ end
147
168
 
148
- option 'foreground', 'f' do
149
- description 'don\'t daemonize'
150
- end
169
+ minecraft = nil
151
170
 
152
- option 'pid-file', 'P' do
153
- description 'pid file relative to minecraft-dir'
154
- default 'minecraftctlserver.pid'
155
- argument_required
156
- end
171
+ begin
172
+ minecraft = Minecraft.new(options[:command], :startup_timeout => options[:startup_timeout], :command_timeout => options[:command_timeout])
157
173
 
158
- option 'log-file', 'l' do
159
- description 'log file relative to minecraft-dir when in not in foreground'
160
- default 'minecraftctlserver.log'
161
- argument_required
174
+ TextCollector.for(minecraft) do
175
+ start
176
+ end.each do |msg|
177
+ puts msg
162
178
  end
179
+ rescue Minecraft::StartupFailedError => e
180
+ puts "Error: failed to start minecraft with '#{options[:command]}' in '#{directory}': #{e}"
181
+ exit 3
182
+ rescue => e
183
+ puts "Error: #{e}"
184
+ exit 4
185
+ end
163
186
 
164
- option 'startup-timeout', 's' do
165
- description 'time in seconds after which control server will exit with error while waiting for minecraft to start up'
166
- default 120
167
- cast :float
168
- argument_required
169
- end
187
+ sinatra = Sinatra.new
188
+ sinatra.set :port, options[:port]
189
+ sinatra.set :bind, options[:bind]
190
+ sinatra.set :environment, 'production'
191
+ sinatra.set :server, ['mongrel']
192
+ sinatra.set :lock, true
193
+
194
+ APIBuilder.new(sinatra, 'minecraft control server API') do
195
+ #get 'inspect', 'internal representation of output data' do
196
+ #minecraft.history.map{|m| m.inspect}.join("\n") + "\n"
197
+ #end
170
198
 
171
- option 'command-timeout', 't' do
172
- description 'time in seconds after which control server will exit with error while waiting for minecraft console command to finish'
173
- default 40
174
- cast :float
175
- argument_required
199
+ get 'dir', 'minecraft directory' do
200
+ Dir.pwd + "\n"
176
201
  end
177
202
 
178
- argument 'minecraft-dir' do
179
- description 'directory path to mincraft server installation directory'
180
- argument_required
203
+ get 'pid', 'minecraft control server PID' do
204
+ Process.pid.to_s + "\n"
181
205
  end
182
206
 
183
- run do
184
- pid_file = Pathname.new(params['pid-file'].value)
185
- log_file = Pathname.new(params['log-file'].value)
207
+ get 'pid_file', 'minecraft control server PID file' do
208
+ pid_file.realpath.to_s + "\n"
209
+ end
186
210
 
187
- Dir.chdir(params['minecraft-dir'].value)
211
+ get 'log_file', 'minecraft control server log file' do
212
+ log_file.realpath.to_s + "\n"
213
+ end
188
214
 
189
- if params['foreground'].given?
190
- Daemon.lock(pid_file)
191
- else
192
- Daemon.daemonize(pid_file, log_file)
193
- end
215
+ get 'out', 'output generated by control and minecraft server' do
216
+ minecraft.history.join("\n") + "\n"
217
+ end
194
218
 
195
- minecraft = Minecraft.new(params['command'].value, :startup_timeout => params['startup-timeout'].value, :command_timeout => params['command-timeout'].value)
219
+ post 'shutdown', 'shutdown minecraft and control server' do
196
220
  TextCollector.for(minecraft) do
197
- start
198
- end.each do |msg|
199
- puts msg
200
- end
221
+ minecraft.stop if minecraft.running?
222
+ log "Shutting down minecraftctlserver"
201
223
 
202
- sinatra = Sinatra.new
203
- sinatra.set :port, params['port'].value
204
- sinatra.set :bind, params['bind'].value
205
- sinatra.set :environment, 'production'
206
- sinatra.set :server, ['mongrel']
207
- sinatra.set :lock, true
208
-
209
- APIBuilder.new(sinatra, 'minecraft control server API') do
210
- #get 'inspect', 'internal representation of output data' do
211
- #minecraft.history.map{|m| m.inspect}.join("\n") + "\n"
212
- #end
213
-
214
- get 'dir', 'minecraft directory' do
215
- Dir.pwd + "\n"
216
- end
217
-
218
- get 'pid', 'minecraft control server PID' do
219
- Process.pid.to_s + "\n"
220
- end
224
+ pid = Process.pid
225
+ Thread.new{Process.kill(15, pid)}
226
+ end
227
+ end
221
228
 
222
- get 'pid_file', 'minecraft control server PID file' do
223
- pid_file.realpath.to_s + "\n"
229
+ path 'server', 'minecraft server API' do
230
+ get 'status', 'server status' do
231
+ if minecraft.running?
232
+ "running\n"
233
+ else
234
+ "stopped\n"
224
235
  end
236
+ end
225
237
 
226
- get 'log_file', 'minecraft control server log file' do
227
- log_file.realpath.to_s + "\n"
238
+ get 'pid', 'minecraft server PID' do
239
+ if minecraft.server_pid
240
+ minecraft.server_pid.to_s + "\n"
241
+ else
242
+ "Server not running\n"
228
243
  end
244
+ end
229
245
 
230
- get 'out', 'output generated by control and minecraft server' do
231
- minecraft.history.join("\n") + "\n"
246
+ post 'start', 'starts minecraft server' do
247
+ TextCollector.for(minecraft) do
248
+ start
232
249
  end
250
+ end
233
251
 
234
- post 'shutdown', 'shutdown minecraft and control server' do
235
- TextCollector.for(minecraft) do
236
- minecraft.stop if minecraft.running?
237
- log "Shutting down minecraftctlserver"
238
-
239
- pid = Process.pid
240
- Thread.new{Process.kill(15, pid)}
241
- end
252
+ post 'stop', 'stops minecraft server' do
253
+ TextCollector.for(minecraft) do
254
+ stop
242
255
  end
256
+ end
243
257
 
244
- path 'server', 'minecraft server API' do
245
- get 'status', 'server status' do
246
- if minecraft.running?
247
- "running\n"
248
- else
249
- "stopped\n"
250
- end
251
- end
252
-
253
- get 'pid', 'minecraft server PID' do
254
- if minecraft.server_pid
255
- minecraft.server_pid.to_s + "\n"
256
- else
257
- "Server not running\n"
258
- end
259
- end
260
-
261
- post 'start', 'starts minecraft server' do
262
- TextCollector.for(minecraft) do
263
- start
264
- end
265
- end
266
-
267
- post 'stop', 'stops minecraft server' do
268
- TextCollector.for(minecraft) do
269
- stop
270
- end
271
- end
272
-
273
- post 'console', 'send console command' do |args|
274
- next "Console command not specified; try 'console help'\n" if args.empty?
275
- TextCollector.for(minecraft) do
276
- begin
277
- send(args.shift.tr('-', '_').to_sym, *args)
278
- rescue Minecraft::ServerNotRunningError
279
- log "Server not running"
280
- end
281
- end
258
+ post 'console', 'send console command' do |args|
259
+ next "Console command not specified; try 'console help'\n" if args.empty?
260
+ TextCollector.for(minecraft) do
261
+ begin
262
+ send(args.shift.tr('-', '_').to_sym, *args)
263
+ rescue Minecraft::ServerNotRunningError
264
+ log "Server not running"
282
265
  end
283
266
  end
284
267
  end
268
+ end
269
+ end
285
270
 
286
- sinatra.not_found do
287
- "Unknown request: #{env['REQUEST_PATH']}\n"
288
- end
289
-
290
- sinatra.error do
291
- "Error in minecraftctlserver while processing request '#{env['REQUEST_PATH']}': #{env['sinatra.error']}\n"
292
- end
271
+ sinatra.not_found do
272
+ "Unknown request: #{env['REQUEST_PATH']}\n"
273
+ end
293
274
 
294
- sinatra.run!
275
+ sinatra.error do
276
+ "Error in minecraftctlserver while processing request '#{env['REQUEST_PATH']}': #{env['sinatra.error']}\n"
277
+ end
295
278
 
296
- # make sure we stop the server on exit
297
- if minecraft.running?
298
- puts 'stopping minecraft'
299
- minecraft.stop
300
- end
279
+ begin
280
+ sinatra.run!
281
+ rescue Errno::EACCES, Errno::EADDRNOTAVAIL, SocketError => e
282
+ puts "Error: failed to bind HTTP server socket (#{options[:bind]}): #{e}"
283
+ exit 5
284
+ ensure
285
+ # make sure we stop the server on exit
286
+ if minecraft.running?
287
+ puts 'stopping minecraft'
288
+ minecraft.stop
301
289
  end
302
290
  end
303
291
 
data/lib/minecraft.rb CHANGED
@@ -102,7 +102,7 @@ class Minecraft
102
102
  end
103
103
  end
104
104
 
105
- def initialize(cmd, options)
105
+ def initialize(cmd, options = {})
106
106
  @cmd = cmd
107
107
  @in_queue = Queue.new
108
108
  @message_queue = MessageQueue.new
data/minecraftctl.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "minecraftctl"
8
- s.version = "2.0.1"
8
+ s.version = "2.0.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jakub Pastuszek"]
@@ -55,7 +55,7 @@ Gem::Specification.new do |s|
55
55
  s.add_development_dependency(%q<reek>, ["~> 1.2.8"])
56
56
  s.add_development_dependency(%q<roodi>, ["~> 2.1.0"])
57
57
  s.add_development_dependency(%q<sinatra>, [">= 1.2.6"])
58
- s.add_development_dependency(%q<main>, [">= 4.7.3"])
58
+ s.add_development_dependency(%q<micro-optparse>, ["~> 1"])
59
59
  s.add_development_dependency(%q<httpclient>, [">= 2.2.1"])
60
60
  s.add_development_dependency(%q<mongrel>, [">= 1.1.5"])
61
61
  s.add_development_dependency(%q<mongrel>, [">= 1.1.5"])
@@ -68,7 +68,7 @@ Gem::Specification.new do |s|
68
68
  s.add_dependency(%q<reek>, ["~> 1.2.8"])
69
69
  s.add_dependency(%q<roodi>, ["~> 2.1.0"])
70
70
  s.add_dependency(%q<sinatra>, [">= 1.2.6"])
71
- s.add_dependency(%q<main>, [">= 4.7.3"])
71
+ s.add_dependency(%q<micro-optparse>, ["~> 1"])
72
72
  s.add_dependency(%q<httpclient>, [">= 2.2.1"])
73
73
  s.add_dependency(%q<mongrel>, [">= 1.1.5"])
74
74
  s.add_dependency(%q<mongrel>, [">= 1.1.5"])
@@ -82,7 +82,7 @@ Gem::Specification.new do |s|
82
82
  s.add_dependency(%q<reek>, ["~> 1.2.8"])
83
83
  s.add_dependency(%q<roodi>, ["~> 2.1.0"])
84
84
  s.add_dependency(%q<sinatra>, [">= 1.2.6"])
85
- s.add_dependency(%q<main>, [">= 4.7.3"])
85
+ s.add_dependency(%q<micro-optparse>, ["~> 1"])
86
86
  s.add_dependency(%q<httpclient>, [">= 2.2.1"])
87
87
  s.add_dependency(%q<mongrel>, [">= 1.1.5"])
88
88
  s.add_dependency(%q<mongrel>, [">= 1.1.5"])
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minecraftctl
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
4
+ hash: 11
5
5
  prerelease:
6
6
  segments:
7
7
  - 2
8
8
  - 0
9
- - 1
10
- version: 2.0.1
9
+ - 2
10
+ version: 2.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Jakub Pastuszek
@@ -131,16 +131,14 @@ dependencies:
131
131
  requirement: &id008 !ruby/object:Gem::Requirement
132
132
  none: false
133
133
  requirements:
134
- - - ">="
134
+ - - ~>
135
135
  - !ruby/object:Gem::Version
136
- hash: 37
136
+ hash: 1
137
137
  segments:
138
- - 4
139
- - 7
140
- - 3
141
- version: 4.7.3
138
+ - 1
139
+ version: "1"
142
140
  version_requirements: *id008
143
- name: main
141
+ name: micro-optparse
144
142
  prerelease: false
145
143
  type: :development
146
144
  - !ruby/object:Gem::Dependency