mclaunch 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ lib/tags
19
+ tags
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ #Required Gems
4
+ gem "progressbar"
5
+ gem "logrotate"
6
+
7
+ # Specify your gem's dependencies in mclaunch.gemspec
8
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 odie
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # Mclaunch
2
+
3
+ mclaunch is a launcher for a craftbukkit server, that runs the
4
+ craftbukkit server in either a tmux or a GNU screen session.
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ gem 'mclaunch'
11
+
12
+ And then execute:
13
+
14
+ $ bundle
15
+
16
+ Or install it yourself as:
17
+
18
+ $ gem install mclaunch
19
+
20
+ ## Usage
21
+
22
+ type "mclaunch --help" in a terminal for help
23
+
24
+ ## Contributing
25
+
26
+ 1. Fork it
27
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
28
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
29
+ 4. Push to the branch (`git push origin my-new-feature`)
30
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/mclaunch ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mclaunch'
@@ -0,0 +1,155 @@
1
+ module Mclaunch
2
+ # Commands to issue on minecraft server
3
+
4
+ class Commands
5
+ def initialize(runtime=nil, options=nil, tools=nil)
6
+ @runtime = runtime
7
+ @options = options
8
+ @tools = tools
9
+ @quiet = @options[:quiet]
10
+ @debug = @options[:debug]
11
+ end #end initialize
12
+
13
+ #setter for @runtime
14
+ def set_runtime(runtime)
15
+ @runtime = runtime
16
+ end #end set_runtime(runtime)
17
+
18
+ #setter for @options
19
+ def set_options(options)
20
+ @options = options
21
+ end #end set_options(options)
22
+
23
+ #Reload command
24
+ def reload
25
+ puts "sending reload command to server" unless @quiet
26
+ @runtime.send_command("reload")
27
+ end #end reload
28
+
29
+ #save-on command
30
+ def save_on
31
+ puts "sending save-on command to server" unless @quiet
32
+ @runtime.send_command("save-on")
33
+ end #end save_on
34
+
35
+ #save-off command
36
+ def save_off
37
+ puts "sending save-off command to server" unless @quiet
38
+ @runtime.send_command("save-off")
39
+ end #end save_off
40
+
41
+ #save-all command
42
+ def save_all
43
+ puts "sending save-all command to server" unless @quiet
44
+ @runtime.send_command("save-all")
45
+ end #end save_all
46
+
47
+ #defaultgamemode command
48
+ def change_gamemode(mode)
49
+ raise "\n False command: #{mode}\n\n Use:\n 0 or survival\n 1 or creative\n 2 or adventure" unless mode =~ /^([0-2]|(survival|creative|adventure))$/
50
+ puts "changing gamemode to \"#{mode}\"" unless @quiet
51
+ @runtime.send_command("defaultgamemode #{mode.to_i}") if mode =~ /^[0-2]$/
52
+ @runtime.send_command("defaultgamemode #{mode.to_s}") if mode =~ /^(survival|creative|adventure)$/
53
+ end #end change_gamemode(mode)
54
+
55
+ #difficulty command
56
+ def change_difficulty(difficulty)
57
+ raise "\n Command only available with minecraft-server.jar" unless @options[:server_executable].eql? "minecraft-server.jar"
58
+ raise "\n False command: #{difficulty}\n\n Use:\n 0 or peaceful\n 1 or easy\n 2 or normal\n 3 or hard" unless difficulty =~ /^([0-3]|(peaceful|easy|normal|hard))$/
59
+ puts "changing difficulty to \"#{difficulty}\"" unless @quiet
60
+ @runtime.send_command("difficulty #{difficulty.to_i}") if difficulty =~ /^[0-3]$/
61
+ @runtime.send_command("difficulty #{difficulty.to_s}") if difficulty =~ /^(peaceful|easy|normal|hard)i$/
62
+ end #end change_difficulty(difficulty)
63
+
64
+ #op command
65
+ def op(player)
66
+ cmd = "op #{player}"
67
+ online_mode = @tools.to_boolean(@runtime.get_server_properties['online-mode'])
68
+ is_player = @tools.is_minecraft_player?(player)
69
+ puts "adding \"#{player}\" to #{@options[:server_runtime_directory]}opts.txt" unless @quiet
70
+ if online_mode
71
+ @runtime.send_command(cmd) if is_player && @tools.is_op?(player)
72
+ else
73
+ @runtime.send_command(cmd)
74
+ end
75
+ end #end op(player)
76
+
77
+ #deop command
78
+ def deop(player)
79
+ puts "removing #{player} from #{@options[:server_runtime_directory]}opts.txt" unless @quiet
80
+ @runtime.send_command("deop #{player}") if @tools.is_op?(player)
81
+ end #end deop(player)
82
+
83
+ #kick player command
84
+ def kick_player(player)
85
+ puts "kicking #{player} from server" unless @quiet
86
+ @runtime.send_command("kick #{player}")
87
+ end #end kick_player(player, reason=nil)
88
+
89
+ #ban player command
90
+ def ban_player(player, reason=nil)
91
+ puts "banning #{player} from server" unless @quiet
92
+ reason_string = reason if reason.is_a?(String)
93
+ reason_string = @tools.array_to_string(reason) if reason.is_a?(Array)
94
+ @runtime.send_command("ban #{player} #{reason_string}")
95
+ end #end ban_player(player, reason=nil)
96
+
97
+ #ban ip command
98
+ def ban_ip(ip, reason=nil)
99
+ raise "This is not an IP address!" unless ip =~/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/
100
+ puts "banning \"#{ip}\ from server" unless @quiet
101
+ reason_string = reason if reason.is_a?(String)
102
+ reason_string = @tools.array_to_string(reason) if reason.is_a?(Array)
103
+ @runtime.send_command("ban-ip #{ip} #{reason_string}")
104
+ end #end ban_ip(ip, reason=nil)
105
+
106
+ #pardon player command
107
+ def pardon_player(player)
108
+ puts "removing ban for \"#{player}\"." unless @quiet
109
+ @runtime.send_command("pardon #{player}")
110
+ end #end pardon_player(player, reason=nil)
111
+
112
+ #pardon ip command
113
+ def pardon_ip(ip)
114
+ raise "This is not an IP address!" unless ip =~/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/
115
+ puts "removing ban for \"#{ip}\"." unless @quiet
116
+ @runtime.send_command("pardon-ip #{ip}")
117
+ end #end pardon_ip(ip, reason=nil)
118
+
119
+ #whitelist <on|off|list> command
120
+ def whitelist(cmd)
121
+ raise "Available commands are: on, off, list" unless cmd =~ /^(on|off|list)$/
122
+ unless @quiet
123
+ puts "turning whitelist on." if cmd.eql?("on")
124
+ puts "turning whitelist off." if cmd.eql?("off")
125
+ end
126
+ @runtime.send_command("whitelist #{cmd}")
127
+ end #end whitelist(cmd)
128
+
129
+ #whitelist add command
130
+ def whitelist_add(player)
131
+ cmd = "whitelist add #{player}"
132
+ online_mode = @tools.to_boolean(@runtime.get_server_properties['online-mode'])
133
+ is_player = @tools.is_minecraft_player?(player)
134
+ puts "adding \"#{player}\" to whitelist" unless @quiet
135
+ if online_mode
136
+ @runtime.send_command(cmd) if is_player
137
+ else
138
+ @runtime.send_command(cmd)
139
+ end
140
+ end #end whitelist_add(player)
141
+
142
+ #whitelist remove command
143
+ def whitelist_remove(player)
144
+ cmd = "whitelist remove #{player}"
145
+ online_mode = @tools.to_boolean(@runtime.get_server_properties['online-mode'])
146
+ is_player = @tools.is_minecraft_player?(player)
147
+ puts "removing \"#{player}\" to whitelist" unless @quiet
148
+ if online_mode
149
+ @runtime.send_command(cmd) if is_player && @tools.is_op?(player)
150
+ else
151
+ @runtime.send_command(cmd)
152
+ end
153
+ end #end whitelist_remove(player)
154
+ end #end Commands
155
+ end
@@ -0,0 +1,143 @@
1
+ module Mclaunch
2
+ # Runtime Class for Minecraft server
3
+
4
+ extend self
5
+ require 'logrotate'
6
+ require 'mclaunch'
7
+
8
+ class Runtime
9
+ def initialize(options=nil)
10
+ @options = options
11
+ @start_cmd = "java -Xms#{@options[:server_min_heap_memory]} -Xmx#{@options[:server_max_heap_memory]} #{@options[:server_additional_options]} -jar #{@options[:server_executable]}"
12
+ @path = options[:server_runtime_directory]
13
+ @quiet = @options[:quiet]
14
+ @debug = @options[:debug]
15
+ @multiplexer = @options[:server_terminal_multiplexer]
16
+ @multiplexer_session_name = @options[:server_terminal_multiplexer_name]
17
+ @multiplexer_session = @options[:server_terminal_multiplexer_session]
18
+ end #end initialize
19
+
20
+ #setter for @options
21
+ def set_options(options)
22
+ @options = options
23
+ end #end set_options(options)
24
+
25
+ #Run server
26
+ def run
27
+ puts "starting server now." unless @quiet
28
+ if @debug
29
+ puts "Command: #{@start_cmd}"
30
+ puts "Multiplexer: #{@multiplexer}"
31
+ end
32
+ Dir.chdir(@path)
33
+ create_tmux_session(@multiplexer_session_name)
34
+ start
35
+ sleep 1
36
+ if is_running?
37
+ puts "server is running now." unless @quiet
38
+ else
39
+ run
40
+ end
41
+ end #end run
42
+
43
+ #start server
44
+ def start
45
+ send_command(@start_cmd)
46
+ end #end start
47
+
48
+ #stop server
49
+ def stop
50
+ send_command("stop")
51
+ sleep 5
52
+ end #end stop
53
+
54
+ #halt
55
+ def halt
56
+ puts "halting server now." unless @quiet
57
+ stop
58
+ log_rotate
59
+ kill_tmux_session(@multiplexer_session_name)
60
+ puts "server haltet." unless @quiet
61
+ end #end halt
62
+
63
+ def restart
64
+ puts "restarting server now." unless @quiet
65
+ stop
66
+ start
67
+ if is_running?
68
+ puts "server restart complete" unless @quiet
69
+ else
70
+ run
71
+ end
72
+ end
73
+
74
+ #check is server is running
75
+ def is_running?
76
+ puts "check if server is running" if @debug
77
+ return true if `ps ax | grep -v grep | egrep "(minecraft|craftbukkit).jar" | wc -l`.to_i > 0
78
+ end #end is_running?
79
+
80
+ #Send a command to a running server
81
+ def send_command(cmd)
82
+ puts "sending \"#{cmd}\" to #{@multiplexer} session \"#{@multiplexer_session_name}\"" if @debug
83
+ `tmux -S #{@multiplexer_session} send -t #{@multiplexer_session_name} "#{cmd}" ENTER` if @multiplexer.eql?("tmux")
84
+ `screen -p 0 -S #{@multiplexer_session_name} -X eval 'stuff \"#{cmd}\"'\15` if @multiplexer.eql?("screen")
85
+ end #end send_command(cmd)
86
+
87
+ #Send a chat message as a server say command
88
+ def send_message(msg)
89
+ msg = "say #{msg}"
90
+ puts "sending \"#{msg}\" as server chat message" if @debug
91
+ send_command(msg)
92
+ end #end send_message(msg)
93
+
94
+ #Create a tmux session for the server to run in
95
+ def create_tmux_session(name)
96
+ puts "connecting to or creating a new #{@multiplexer} session now" if @debug
97
+ `tmux -S #{@multiplexer_session} has-session -t #{name} > /dev/null 2>&1 || tmux -S #{@multiplexer_session} new-session -s #{name} -d` if @multiplexer.eql?("tmux")
98
+ `ps ax | grep -v grep | grep SCREEN | grep craftbukkit 2>&1 || screen -dmS #{name}` if @multiplexer.eql?("screen")
99
+ puts "multiplexer session should be started now" if @debug
100
+ sleep 0.1
101
+ File.chmod(01000, @multiplexer_session) if @Multiplexer.eql?("tmux")
102
+ end #end create_tmux_session(name)
103
+
104
+ #Kill a tmux session
105
+ def kill_tmux_session(name)
106
+ puts "killing #{@multiplexer} sessinon now" if @debug
107
+ case @multiplexer
108
+ when "screen"
109
+ send_command("exit") unless is_running?
110
+ when "tmux"
111
+ `tmux -S #{@multiplexer_session} kill-session -t #{@multiplexer_session_name}`
112
+ end
113
+ halt if is_running?
114
+ end #end kill_tmux_session(name)
115
+
116
+ #Rotate the server log file
117
+ def log_rotate
118
+ puts "Rotating logfile now" if @debug
119
+ options = {
120
+ :gzip => true,
121
+ :count => 5,
122
+ }
123
+ LogRotate.rotate_file("/home/odie/bukkit/server/server.log", options)
124
+ end #end log_rotate
125
+
126
+ #Get server.properties file from server runtime directory
127
+ def get_server_properties
128
+ props = Hash[File.read("#{@options[:server_runtime_directory]}server.properties").split("\n").map{|i|i.split('=') unless i =~ /^#.*$/}]
129
+ if @debug
130
+ puts "---"
131
+ puts "Server properties: "
132
+ puts
133
+ props.each do |k,v|
134
+ puts "\t#{k}=#{v}"
135
+ end
136
+ puts "---"
137
+ puts
138
+ end
139
+ props
140
+ end #end get_server_properties
141
+
142
+ end #end Runtime
143
+ end
@@ -0,0 +1,323 @@
1
+ module Mclaunch
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'rexml/document'
5
+ require 'open-uri'
6
+ require 'progressbar'
7
+ require 'date'
8
+
9
+ class Tools
10
+ def initialize(runtime, options)
11
+ @runtime = runtime
12
+ @options = options
13
+ @server_properties = @runtime.get_server_properties
14
+ @versions = {}
15
+ @debug = @options[:debug]
16
+ @quiet = @options[:quiet]
17
+ @xml_document = nil
18
+ @craftbukkit_version_regex = /^[1-9]\.[0-9]\.[0-9]-R[0-9]\.[0-9]$/
19
+ @commands = nil
20
+ end #end initialize
21
+
22
+ #Method desctiption
23
+ def commands(commands=nil)
24
+ @commands = commands
25
+ @commands unless @commands
26
+ end #end commands(commands=nil)
27
+
28
+ #Check online if Player name is a valid minecraft username if server is in online mode
29
+ def is_minecraft_player?(player)
30
+ if to_boolean(@server_properties['online-mode']) == true
31
+ uri = URI.parse("https://minecraft.net/haspaid.jsp?user=#{player}")
32
+ http = Net::HTTP.new(uri.host, uri.port)
33
+ http.use_ssl = true
34
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
35
+ request = Net::HTTP::Get.new(uri.request_uri)
36
+ response = http.request(request)
37
+ to_boolean response.body
38
+ end
39
+ end #end is_minecraft_player?(player, cmd)
40
+
41
+ #check if player is in ops.txt
42
+ def is_op?(player)
43
+ puts "checking if \"#{player}\" is op." if @debug
44
+ ops = File.new("#{@options[:server_runtime_directory]}ops.txt", "r")
45
+ op = false
46
+ ops.each_line do |l|
47
+ op = true if l.gsub("\n", "").eql?(player)
48
+ end
49
+ op
50
+ end #end is_op?
51
+
52
+ def is_symlink_or_file(path)
53
+ File.exist?(path) || File.symlink?(path)
54
+ end #end is_symlink_or_file(path)
55
+
56
+ #Convert String to Boolean
57
+ def to_boolean(s)
58
+ s.eql?"true"
59
+ end #end to_boolean(s)
60
+
61
+ #Convert a array into a String.
62
+ def array_to_string(array, delimiter=" ")
63
+ tmp = ""
64
+ array.each do |s|
65
+ tmp += s+delimiter
66
+ end
67
+ tmp
68
+ end #end array_to_string(array, delimiter=" ")
69
+
70
+ def get_worlds
71
+ worlds = {}
72
+ dir = Dir.new(@options[:server_world_directory])
73
+ dir.each do |world|
74
+ unless world =~ /^(\.|\.\.)$/
75
+ ramdisk = false
76
+ ramdisk = true if File.exists?("#{@options[:server_world_directory]}#{world}/ramdisk")
77
+ worlds[world] = ramdisk
78
+ if @debug
79
+ puts " world: #{world} runs from ramdisk" if ramdisk
80
+ puts " world: #{world} runs from disk" unless ramdisk
81
+ end
82
+ end
83
+ end #end dir.each
84
+ return worlds
85
+ end #end get_worlds
86
+
87
+ #Create world Symlinks
88
+ def worlds_create_symlinks
89
+ worlds = get_worlds
90
+ worlds.each do |world,ramdisk|
91
+ src = "#{@options[:server_world_directory]}#{world}"
92
+ src = "#{@options[:server_ramdisk_directory]}#{world}" if ramdisk
93
+ dst = "#{@options[:server_runtime_directory]}#{world}"
94
+ File.unlink(dst) if File.symlink?(dst)
95
+ File.symlink(src,dst)
96
+ puts "Symlink created for: #{world}" unless @quiet
97
+ puts "Source: #{src}\nDestination: #{dst}" if @debug
98
+ end
99
+ end #end worlds_create_symlinks
100
+
101
+ def world_ramdisk_state(world_name)
102
+ worlds = get_worlds
103
+ world_exists = false
104
+ world_already_on_ramdisk = false
105
+ worlds.each do |world,ramdisk|
106
+ if world.eql? world_name
107
+ world_exists = true
108
+ world_already_on_ramdisk = ramdisk
109
+ end
110
+ end
111
+ if world_name
112
+ if world_exists
113
+ if world_already_on_ramdisk
114
+ puts "\"#{world_name}\" will run from harddisk on next server start" unless @quiet
115
+ File.delete("#{@options[:server_world_directory]}#{world_name}/ramdisk")
116
+ else
117
+ puts "\"#{world_name}\" will run from ramdisk on next server start" unless @quiet
118
+ File.open("#{@options[:server_world_directory]}#{world_name}/ramdisk", "w")
119
+ end #end if world_already_on_ramdisk
120
+ else
121
+ puts "\"#{world_name}\" does not exist in world folder!"
122
+ end #end if world_exists
123
+ end #end if world_name
124
+ end #end world_ramdisk_state(world_name)
125
+
126
+ def worlds_to_ramdisk
127
+ worlds = get_worlds
128
+ worlds.each do |world,ramdisk|
129
+ if ramdisk
130
+ print "copy #{world} to ramdisk... " unless @quiet
131
+ `rsync -rt --exclude 'ramdisk' #{@options[:server_world_directory]}#{world}/ #{@options[:server_ramdisk_directory]}#{world}`
132
+ puts "ok" unless @quiet
133
+ end #end if ramdisk
134
+ end #worlds.each
135
+ end #end to_ramdisk
136
+
137
+ def worlds_to_disk
138
+ worlds = get_worlds()
139
+ ramdisk_worlds = Dir.open("#{@options[:server_ramdisk_directory]}")
140
+ ramdisk_worlds.each do |world|
141
+ if worlds.key?(world)
142
+ if worlds[world]
143
+ print "copy #{world} to harddisk... " unless @quiet
144
+ `rsync -rt --exclude 'ramdisk' #{@options[:server_ramdisk_directory]}#{world}/ #{@options[:server_world_directory]}#{world}`
145
+ puts "ok" unless @quiet
146
+ end
147
+ end #end if ramdisk
148
+ end #worlds.each
149
+ end #end to_ramdisk
150
+
151
+ #Get a hash of the locally available server versions
152
+ def get_local_server_versions
153
+ dir = Dir.new(@options[:server_jar_directory])
154
+ dir.sort.each do |f|
155
+ @versions[f.gsub(/^craftbukkit-|\.jar$/, "")] = "#{@options[:server_jar_directory]}#{f}" unless f =~ /^(\.\.|\.)$/
156
+ end
157
+ @versions
158
+ end #end get_local_server_versions
159
+
160
+ #Print locally available server versions to stdout
161
+ def print_local_server_versions
162
+ get_local_server_versions.each do |k,v|
163
+ puts "#{k}, #{v}"
164
+ end
165
+ end #end print_local_server_versions
166
+
167
+ #Check if given version is available locally
168
+ def local_version_available?(channel, version, build_number)
169
+ true if get_local_server_versions.has_key?("#{channel}-#{version}-#{build_number}")
170
+ end #end local_version_available?(version)
171
+
172
+ #Get an xml Document from the given url
173
+ def get_xml_document(url)
174
+ xml_data = Net::HTTP.get_response(URI.parse(url))
175
+ raise "Search for xml document data returned HTTP Error 404\n\nMaybe the version you entered does not exist." if xml_data.is_a?(Net::HTTPNotFound)
176
+ @xml_document = REXML::Document.new(xml_data.body)
177
+ raise "xml document empty" unless @xml_document
178
+ end #end get_xml_document(url)
179
+
180
+ #Get xml data from craftbukkit site as hash
181
+ def get_xml_first_path(url, path)
182
+ get_xml_document(url)
183
+ REXML::XPath.first(@xml_document, path).text
184
+ end #end get_xml_data(url)
185
+
186
+ #Get the latest build number for given version that was not broken
187
+ def get_latest_build_number(version, page=1)
188
+ url = "http://dl.bukkit.org/api/1.0/downloads/projects/craftbukkit/artifacts/?_accept=application%2Fxml&amp;limit=20&amp;page=#{page}"
189
+ build_number = nil
190
+ get_xml_document(url)
191
+ max_page = REXML::XPath.first(@xml_document, "//pages").text
192
+ REXML::XPath.each(@xml_document, "/root/results/list-item") do |element|
193
+ vers = element.text "version"
194
+ build_nr = element.text "build_number"
195
+ next if element.text("is_broken") =~ /^[Tt][Rr][Uu][Ee]$/
196
+ build_number = build_nr if version.eql? vers
197
+ end
198
+ if build_number.to_i > 0
199
+ return build_number
200
+ else
201
+ get_latest_build_number(version, page + 1) if page <= max_page.to_i
202
+ end
203
+ end #end get_latest_build_number(version)
204
+
205
+ #Return the link for the latest version of craftbukkit
206
+ def get_download_link(version)
207
+ prefix = "http://dl.bukkit.org"
208
+ url = "#{prefix}/api/1.0/downloads/projects/craftbukkit/artifacts/#{version}/?_accept=application%2Fxml" if version =~ /^(rb|beta|dev)$/
209
+ url = "#{prefix}/api/1.0/downloads/projects/craftbukkit/view/build-#{version}/?_accept=application%2Fxml" if version =~ /^[0-9][0-9][0-9][0-9]$/
210
+ url = "#{prefix}/api/1.0/downloads/projects/craftbukkit/view/build-#{get_latest_build_number(version)}/?_accept=application%2Fxml" if version =~ @craftbukkit_version_regex
211
+ raise "Thist is no Craftbukkit Version." unless url
212
+ {
213
+ :link => "#{prefix}#{get_xml_first_path(url, '//file/url')}",
214
+ :checksum => get_xml_first_path(url, "//file/checksum_md5"),
215
+ :version => get_xml_first_path(url, "//version"),
216
+ :build_number => get_xml_first_path(url, "//build_number"),
217
+ :channel => get_xml_first_path(url, "//channel/slug"),
218
+ }
219
+ end #end get_latest_download_link
220
+
221
+ #Download the latest server executable
222
+ def download_server_executable(version)
223
+ link = get_download_link(version)
224
+ url = link[:link]
225
+ md5 = link[:checksum]
226
+ version = link[:version]
227
+ build_number = link[:build_number]
228
+ channel = link[:channel]
229
+ target = "#{@options[:server_jar_directory]}craftbukkit-#{channel}-#{version}-#{build_number}.jar"
230
+
231
+ #Debug output
232
+ if @debug
233
+ puts "[#{version}]"
234
+ puts " link:\t\t#{url}"
235
+ puts " md5:\t\t#{md5}"
236
+ puts " taret:\t#{target}"
237
+ puts " build_nr:\t#{build_number}"
238
+ puts " channel:\t#{channel}"
239
+ end
240
+
241
+ if local_version_available? channel, version, build_number
242
+ puts "local version available. Will symlink local version to server runtime directory." if @debug
243
+ else
244
+ puts "local version not available. Will download new version and put symlink to it in server runtime directory." if @debug
245
+ download_file(url, target)
246
+ end
247
+ set_server_exe_symlink(target)
248
+ end #end download_latest_server_executable
249
+
250
+ #Change server version manually
251
+ def server_change_version(version)
252
+ link = get_download_link(version)
253
+ raise "This is no Craftbukkit Version!" unless version =~ /^(#{@craftbukkit_version_regex}|[0-9]{4})$/
254
+ if local_version_available?(link[:channel], link[:version], link[:build_number])
255
+ filename = "craftbukkit-#{version}.jar"
256
+ set_server_exe_symlink filename
257
+ else
258
+ download_server_executable(version)
259
+ end
260
+ end #end server_change_version(version)
261
+
262
+ #Download url to path
263
+ def download_file(url, path)
264
+ puts "downloading file from: \"#{url}\"" unless @quiet
265
+ url = URI(url)
266
+ location = URI(Net::HTTP.get_response(url)['location'])
267
+ Net::HTTP.start(location.host, location.port) do |http|
268
+ unless File.exist?(path)
269
+ f = open(path, 'wb')
270
+ begin
271
+ downloadedSize = 0
272
+ http.request_get(location.request_uri) do |resp|
273
+ progressBar = ProgressBar.new("Downloading", 100)
274
+ size = resp.content_length
275
+ resp.read_body do |segment|
276
+ downloadedSize += segment.length
277
+ if(downloadedSize != 0)
278
+ percent = (downloadedSize * 100 / size)
279
+ progressBar.set(percent)
280
+ end
281
+ f.write(segment)
282
+ end #end resp.read_body
283
+ progressBar.finish
284
+ end #end http.request_get
285
+ ensure
286
+ f.close()
287
+ end #end begin
288
+ end #end Net::HTTP.start
289
+ end
290
+ end #end download_file(url, path)
291
+
292
+ def set_server_exe_symlink(file)
293
+ current_file = "#{@options[:server_runtime_directory]}#{@options[:server_executable]}"
294
+ was_running = false
295
+ if @runtime.is_running?
296
+ was_running = true
297
+ @runtime.stop
298
+ end
299
+ puts "symlinking #{file} to #{current_file}"
300
+ File.unlink(current_file) if is_symlink_or_file(current_file)
301
+ File.symlink(file,current_file)
302
+ @runtime.start if was_running
303
+ end #end set_server_exe_symlink(file)
304
+
305
+ #Create a world backup with rsync
306
+ def world_backup_create
307
+ puts "Creating world folder backup now." unless @quiet
308
+ @commands.save_off
309
+ @commands.save_all
310
+ sleep 1
311
+ `rsync -a --delete-after #{@options[:server_world_directory]} #{@options[:server_backup_directory]}worlds/latest > #{@options[:server_backup_directory]}worlds/latest/rsync.log`
312
+ @commands.save_on
313
+ now = DateTime.now.strftime("%Y-%m-%d-%H:%M:%S")
314
+ `cp -al #{@options[:server_backup_directory]}worlds/latest #{@options[:server_backup_directory]}worlds/#{now}`
315
+ end #end world_backup create
316
+
317
+ #Delete world backups which are older than a month
318
+ def world_backup_delete
319
+ puts "Deleting old backups" unless @quiet
320
+ `find #{@options[:server_backup_directory]} -mindepth 1 -maxdepth 1 -not -newermt '1 month ago' -exec rm -rf {} \\;`
321
+ end #end world_backup_delete
322
+ end #end class Tools
323
+ end #end module Mclaunch
@@ -0,0 +1,3 @@
1
+ module Mclaunch
2
+ VERSION = "0.0.1"
3
+ end
data/lib/mclaunch.rb ADDED
@@ -0,0 +1,347 @@
1
+ module Mclaunch
2
+ require "mclaunch/version"
3
+ require "mclaunch/runtime"
4
+ require "mclaunch/commands"
5
+ require "mclaunch/tools"
6
+ require 'optparse'
7
+ require 'yaml'
8
+
9
+ default_options = {
10
+ :server_executable => "craftbukkit.jar",
11
+ :server_runtime_directory => "#{ENV["HOME"]}/bukkit/server/",
12
+ :server_world_directory => "#{ENV["HOME"]}/bukkit/worlds/",
13
+ :server_jar_directory => "#{ENV["HOME"]}/bukkit/jars/",
14
+ :server_backup_directory => "#{ENV["HOME"]}/bukkit/backup/",
15
+ :server_ramdisk_directory => "#{ENV["HOME"]}/bukkit/ramdisk/",
16
+ :server_min_heap_memory => "512M",
17
+ :server_max_heap_memory => "1024M",
18
+ :server_additional_options => nil,
19
+ :server_terminal_multiplexer => "tmux",
20
+ :server_terminal_multiplexer_name => "craftbukkit",
21
+ :server_terminal_multiplexer_session => "/tmp/craftbukkit",
22
+ }
23
+ @options = {
24
+ :debug => false,
25
+ :quiet => false,
26
+ :config_file => nil,
27
+ :server_management => {
28
+ :list_versions => false,
29
+ :update_to_latest => nil,
30
+ :server_change_version => nil,
31
+ :world_to_ramdisk => nil,
32
+ :world_to_disk => nil,
33
+ :worlds_create_symlinks => nil,
34
+ },
35
+ :server_commands => {
36
+ :reload => false,
37
+ :save_on => false,
38
+ :save_off => false,
39
+ :save_all => false,
40
+ :gamemode => nil,
41
+ :difficulty => nil,
42
+ :op => nil,
43
+ :deop => nil,
44
+ :kick => nil,
45
+ :kick_player => nil,
46
+ :kick_ip => nil,
47
+ :pardon_player => nil,
48
+ :pardon_ip => nil,
49
+ :whitelist_on => false,
50
+ :whitelist_off => false,
51
+ :whitelist_add => nil,
52
+ :whitelist_remove => nil,
53
+ }
54
+ }
55
+ @options.merge! default_options
56
+
57
+
58
+ opt_parser = OptionParser.new do |opts|
59
+
60
+ #Write Standard config file if it does't exist
61
+
62
+ @config_file = "#{@options[:server_runtime_directory]}mclaunch.properties" unless @options[:config_file]
63
+ @config_file = @options[:config_file] if @options[:config_file]
64
+ if File.exists?(@config_file) == false
65
+ File.open(@config_file, "w") {|f| f.write(default_options.to_yaml)}
66
+ end
67
+
68
+
69
+ opts.banner = " Usage:\t#{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} <options> start/stop/restart\n"
70
+ opts.separator ""
71
+ opts.separator " Avalable options are:\n"
72
+ opts.separator ""
73
+
74
+ opts.separator " Game management:"
75
+
76
+ opts.on('--reload', "reload the minecraft server") do
77
+ @options[:server_commands][:reload] = true
78
+ end #end opts.on
79
+
80
+ opts.on('--save-on', "turn the save mode on") do
81
+ @options[:server_commands][:save_on] = true
82
+ end #end opts.on
83
+
84
+ opts.on('--save-off', "turn the save mode off") do
85
+ @options[:server_commands][:save_off] = true
86
+ end #end opts.on
87
+
88
+ opts.on('--save-all', "save the current world state") do
89
+ @options[:server_commands][:save_all] = true
90
+ end #end opts.on
91
+
92
+ opts.on('--gamemode GAMEMODE', String, "change the current gamemode to GAMEMODE") do |arg|
93
+ @options[:server_commands][:gamemode] = arg
94
+ end #end opts.on
95
+
96
+ opts.on('--difficulty DIFFICULTY', String, "change the current difficulty to DIFFICULTY") do |arg|
97
+ @options[:server_commands][:difficulty] = arg
98
+ end #end opts.on
99
+
100
+ opts.on('--op-player PLAYER', String, "grant op status on PLAYER") do |arg|
101
+ @options[:server_commands][:op] = arg
102
+ end #end opts.on
103
+
104
+ opts.on('--deop-player PLAYER', String, "revoke op status on Player") do |arg|
105
+ @options[:server_commands][:deop] = arg
106
+ end #end opts.on
107
+
108
+ opts.on('--ban-player PLAYER [REASON]', String, "ban PLAYER") do |arg|
109
+ @options[:server_commands][:ban_player] = arg
110
+ end #end opts.on
111
+
112
+ opts.on('--ban-ip 11.22.33.44 [REASON]', String, "ban IP") do |arg|
113
+ @options[:server_commands][:ban_ip] = arg
114
+ end #end opts.on
115
+
116
+ opts.on('--pardon-player PLAYER', String, "pardon PLAYER") do |arg|
117
+ @options[:server_commands][:pardon_player] = arg
118
+ end #end opts.on
119
+
120
+ opts.on('--pardon-ip 11.22.33.44', String, "pardon IP") do |arg|
121
+ @options[:server_commands][:pardon_ip] = arg
122
+ end #end opts.on
123
+
124
+ opts.on('--whitelist-add PLAYER', String, "add PLAYER to whitelist") do |arg|
125
+ @options[:server_commands][:whitelist_add] = arg
126
+ end #end opts.on
127
+
128
+ opts.on('--whitelist-remove PLAYER', String, "remove PLAYER from whitelist") do |arg|
129
+ @options[:server_commands][:whitelist_remove] = arg
130
+ end #end opts.on
131
+
132
+ opts.on('--whitelist-on', "turn the whitelist on") do
133
+ @options[:server_commands][:whitelist_on] = true
134
+ end #end opts.on
135
+
136
+ opts.on('--whitelist-off', "turn the whitelist off") do
137
+ @options[:server_commands][:whitelist_off] = true
138
+ end #end opts.on
139
+
140
+ opts.separator ""
141
+ opts.separator " Server Settings:"
142
+
143
+ opts.on('-m', '--minimal-mem 512M', "set the minimal memory to use in format \"512M\"") do |arg|
144
+ @options[:server_min_heap_memory] = arg
145
+ end
146
+
147
+ opts.on('-M', '--maximal-mem 1G', "set the maximal memory to use in format \"1024M\"") do |arg|
148
+ @options[:server_max_heap_memory] = arg
149
+ end
150
+
151
+ opts.on('--server-executable JAR', String, "set the JAR to use for current session") do |arg|
152
+ @options[:server_executable] = arg
153
+ end
154
+
155
+ opts.on('--server-directory DIR', String, "set the directory to user to DIR") do |arg|
156
+ @options[:server_runtime_directory] = arg
157
+ end
158
+
159
+ opts.on('--jar-directory DIR', String, "set the jar directory to use to DIR") do |arg|
160
+ @options[:server_jar_directory] = arg
161
+ end
162
+
163
+ opts.on('--world-directory DIR', String, "set the world directory to use to DIR") do |arg|
164
+ @options[:server_world_directory] = arg
165
+ end
166
+
167
+ opts.on('--backup-directory DIR', String, "set the backup directory to use to DIR") do |arg|
168
+ @options[:server_backup_directory] = arg
169
+ end
170
+
171
+ opts.separator ""
172
+ opts.separator " Server Magagement:"
173
+
174
+ opts.on('-l', '--list-versions', "list locally available Server versions") do
175
+ @options[:server_management][:list_versions] = true
176
+ end
177
+
178
+ opts.on('--update VERSION', "update to latest craftbukkit version, where version may be \"rb, beta or dev\"") do |arg|
179
+ @options[:server_management][:update_to_latest] = arg
180
+ end
181
+
182
+ opts.on('--server-version VERSION', "Set server version to Version") do |arg|
183
+ @options[:server_management][:server_change_version] = arg
184
+ end
185
+
186
+ opts.separator ""
187
+ opts.separator " World Management:"
188
+
189
+ opts.on('--world-to-ramdisk WORLD', "Set ramdisk flag for WORLD") do |arg|
190
+ @options[:server_management][:world_to_disk] = arg
191
+ end
192
+
193
+ opts.on('--world-to-disk WORLD', "Remove ramdisk flag from WORLD") do |arg|
194
+ @options[:server_management][:world_to_ramdisk] = arg
195
+ end
196
+
197
+ opts.on('--worlds-symlink', "Symlink all worlds in the worlds directory to runtime directory")do |arg|
198
+ @options[:server_management][:worlds_create_symlinks] = arg
199
+ end
200
+
201
+ opts.on('--world-backup-create', "Create a Backup from world directory to backup directory") do
202
+ @options[:server_management][:world_backup_create] = true
203
+ end
204
+
205
+ opts.on('--world-backup-delete', "Delete 1 month old backupa from backup directory") do
206
+ @options[:server_management][:world_backup_delete] = true
207
+ end
208
+
209
+ opts.separator ""
210
+ opts.separator " Misc Options:"
211
+
212
+ opts.on('-q', '--quiet', "turn off all output") do
213
+ @options[:quiet] = true
214
+ end #end opts.on
215
+
216
+ opts.on('-d', '--debug', "turn debug output on") do
217
+ @options[:debug] = true
218
+ end #end opts.on
219
+
220
+ opts.on("-h", "--help", "Show this message") do
221
+ puts opts
222
+ end # end opts.on_tail
223
+
224
+ opts.separator ""
225
+ opts.separator ""
226
+ opts.separator " Usage Examples:"
227
+ opts.separator ""
228
+ opts.separator " Start, Stop, Restart"
229
+ opts.separator " To start the craftbukkit server use:"
230
+ opts.separator " #{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} <options> start"
231
+ opts.separator " To stop the craftbukkit server use:"
232
+ opts.separator " #{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} stop"
233
+ opts.separator " To restart the craftbukkit server use:"
234
+ opts.separator " #{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} <options> restart"
235
+ opts.separator ""
236
+ opts.separator " Server Management:"
237
+ opts.separator " kick player:"
238
+ opts.separator " #{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} --kick-player notch"
239
+ opts.separator " change gamemode:"
240
+ opts.separator " #{__FILE__.gsub(/(^\/.*\/|\.rb)/, "")} --gamemode creative"
241
+ opts.separator ""
242
+
243
+
244
+ end
245
+
246
+ begin
247
+ @options.merge!(Hash[YAML::load(File.new(@config_file)).map { |k, v| @options[k] = v}])
248
+
249
+ opt_parser.parse!
250
+ rescue => exc
251
+ STDERR.puts "an error with the command line options occured:\n\t#{exc}"
252
+ STDERR.puts
253
+ STDERR.puts "For usage options use #{__FILE__.gsub(/^\/.*\//, "")} --help"
254
+ exit 1
255
+ end
256
+
257
+ begin
258
+ @runtime = Runtime.new(@options)
259
+ @tools = Tools.new(@runtime, @options)
260
+ @commands = Commands.new(@runtime, @options, @tools)
261
+ @tools.commands(@commands)
262
+ @craftbukkit_version_regex = /^[1-9]\.[0-9]\.[0-9]-R[0-9]\.[0-9]$/
263
+
264
+ @options[:server_management].each do |k,v|
265
+ case k
266
+ when :list_versions
267
+ @tools.print_local_server_versions if v
268
+ when :update_to_latest
269
+ @tools.download_server_executable(v) if v =~ /^(rb|beta|dev)$/
270
+ when :server_change_version
271
+ @tools.server_change_version(v) if v =~ /^(#{@craftbukkit_version_regex}|[0-9]{4})$/
272
+ when :world_to_ramdisk
273
+ @tools.world_ramdisk_state(v) if v
274
+ when :world_to_disk
275
+ @tools.world_ramdisk_state(v) if v
276
+ when :worlds_create_symlinks
277
+ @tools.worlds_create_symlinks if v
278
+ when :world_backup_create
279
+ @tools.world_backup_create if v
280
+ when :world_backup_delete
281
+ @tools.world_backup_delete if v
282
+ end
283
+ end
284
+ @options[:server_commands].each do |k,v|
285
+ case k
286
+ when :reload
287
+ @commands.reload if v
288
+ when :save_on
289
+ @commands.save_on if v
290
+ when :save_off
291
+ @commands.save_off if v
292
+ when :save_all
293
+ @commands.save_all if v
294
+ when :gamemode
295
+ @commands.change_gamemode(v) if v
296
+ when :difficulty
297
+ @commands.change_difficulty(v) if v
298
+ when :op
299
+ @commands.op(v) if v
300
+ when :deop
301
+ @commands.deop(v) if v
302
+ when :kick_player
303
+ @commands.kick_player(v) if v
304
+ when :kick_ip
305
+ @commands.kick_ip(v) if v
306
+ when :ban_player
307
+ @commands.ban_player(v, ARGV) if v
308
+ when :ban_ip
309
+ @commands.ban_ip(v, ARGV) if v
310
+ when :pardon_player
311
+ @commands.pardon_player(v) if v
312
+ when :pardon_ip
313
+ @commands.pardon_ip(v) if v
314
+ when :whitelist_on
315
+ @commands.whitelist("on") if v
316
+ when :whitelist_off
317
+ @commands.whitelist("off") if v
318
+ when :whitelist_add
319
+ @commands.whitelist_add(v) if v
320
+ when :whitelist_remove
321
+ @commands.whitelist_remove(v) if v
322
+ end
323
+ end
324
+ rescue => exc
325
+ STDERR.puts "An runtime error occured:\n\t#{exc.message}"
326
+ STDERR.puts
327
+ STDERR.puts "For usage options use #{__FILE__.gsub(/^\/.*\//, "")} --help"
328
+ end
329
+
330
+ begin
331
+ case ARGV[0]
332
+ when "start"
333
+ @tools.worlds_to_ramdisk
334
+ @runtime.run
335
+ when "stop"
336
+ @runtime.halt
337
+ @tools.worlds_to_disk
338
+ when "restart"
339
+ @tools.worlds_to_disk
340
+ @tools.worlds_to_ramdisk
341
+ @runtime.restart
342
+ end
343
+ rescue => exc
344
+ STDERR.puts exc.message
345
+ end
346
+ end
347
+
data/mclaunch.gemspec ADDED
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mclaunch/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "mclaunch"
8
+ gem.version = Mclaunch::VERSION
9
+ gem.authors = ["odie"]
10
+ gem.email = ["masterodie@gmail.com"]
11
+ gem.description = "A craftbukkit server launcher"
12
+ gem.summary = "A craftbukkit server launcher"
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mclaunch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - odie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: A craftbukkit server launcher
15
+ email:
16
+ - masterodie@gmail.com
17
+ executables:
18
+ - mclaunch
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - .gitignore
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - bin/mclaunch
28
+ - lib/mclaunch.rb
29
+ - lib/mclaunch/commands.rb
30
+ - lib/mclaunch/runtime.rb
31
+ - lib/mclaunch/tools.rb
32
+ - lib/mclaunch/version.rb
33
+ - mclaunch.gemspec
34
+ homepage: ''
35
+ licenses: []
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ required_rubygems_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubyforge_project:
54
+ rubygems_version: 1.8.24
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: A craftbukkit server launcher
58
+ test_files: []