minecraft 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -3,3 +3,6 @@ gem.sh
3
3
  .bundle
4
4
  Gemfile.lock
5
5
  pkg/*
6
+ mc
7
+ doc
8
+ .yardoc
@@ -0,0 +1,5 @@
1
+ --hide-void-return
2
+ --title "Minecraft server wrapper and deployment solution."
3
+ --readme README.md
4
+
5
+ lib/**/*.rb
@@ -0,0 +1,54 @@
1
+ 0.0.5 (2011-08-17)
2
+ ------------------
3
+
4
+ * Added `savefreq` and `welcome_message` CLI options.
5
+ * Refactored `Minecraft` module, added a `Runtime` class.
6
+ * Added `!printtime`, `!printtimer`, `!s`, `!shortcuts` commands.
7
+ * Clarified console information/error messages.
8
+ * Fixed kits.
9
+ * Saves timers and shortcuts.
10
+ * Displays welcome message to connecting users.
11
+ * Major refactoring to server process handling.
12
+ * `save_all` on exit works now, proper exit handling. No longer used `Thread#join`
13
+ * Toggling mob state (`--tempmob`) prints new state.
14
+
15
+ 0.0.4 (2011-08-14)
16
+ ------------------
17
+
18
+ * Added the `!rules` command and associated `rules` CLI option.
19
+ * Refactored `give` logic.
20
+ * `!property`, `!uptime` commands added.
21
+ * More intelligent quantifiers.
22
+ * Monitors kicks and bans.
23
+
24
+ 0.0.3 (2011-08-13)
25
+ ------------------
26
+
27
+ * Split command methods into the `Minecraft::Commands` module.
28
+ * Validates `!kit` command.
29
+ * Added `!list`, `!addtimer`, `!deltimer`, `!printtimer`, `!kitlist` commands.
30
+ * Approximate item names implemented.
31
+ * Fixed item naming bugs.
32
+ * Major refactoring done to commands.
33
+ * Logs user uptime.
34
+ * Catches processing exceptions.
35
+
36
+ 0.0.2 (2011-08-13)
37
+ ------------------
38
+
39
+ * Command line options added.
40
+ * Trapped the interrupt signal for an exit hook.
41
+ * `no_run`, `update`, `min_memory`, `max_memory`, `no_auto_save`, `tempmobs` CLI options implemented.
42
+ * Fixed indentation.
43
+ * `!giveall`, `!tpall`, `!help` commands implemented.
44
+ * Meta checks in place (ops, join/part).
45
+ * Privilege errors.
46
+ * Refactored `Minecraft::Server`, added `Minecraft::Tools`.
47
+
48
+ 0.0.1 (2011-08-12)
49
+ ------------------
50
+
51
+ * `:diamond`, `:garmour`, `:armour`, `:ranged`, `:nether`, `:portal` kits.
52
+ * Data values hash in place.
53
+ * Simple console processing in place.
54
+ * `!give`, `!kit`, `!tp`, `!nom` commands implemented.
data/README.md CHANGED
@@ -25,30 +25,55 @@ Usage
25
25
  Current Features
26
26
  ----------------
27
27
 
28
- - !give <item> <quantity>
29
- - Give the user an item with a quantity of up to 2560 (by default Minecraft
30
- will cap at 64).
31
- - Use the item ID or item name. http://minecraftwiki.net/wiki/Data_values
32
- - !giveall <item> <quantity>
33
- - !tp <target_user>
34
- - !tpall
35
- - !nom
36
- - !nomall
37
- - !kit <group>
38
- - Diamond
39
- - Gold armour
40
- - Armour
41
- - Ranged
42
- - Nether
43
- - Portal
44
- - !kitall <group>
45
- - !addtimer <item> <frequency>
46
- - !deltimer <item>
47
- - !printtimer
48
- - !list
49
-
50
- TODO
51
- ----
28
+ Please refer to `Minecraft::Extensions#initialize`.
29
+
30
+ !give <item> <quantity> # Gives the user an <item>, the <quantity>
31
+ # upper bound is 2560 unlike the Minecraft
32
+ # default of 64. Items can be specified by
33
+ # approximate name.
34
+ !giveall <item> <quantity> # Gives all connected users an <item>.
35
+ !tp <target_user> # Teleports the user to the <target_user>.
36
+ !tpall # Teleports all connected user to the user.
37
+ !nom # Give a golden apple the user.
38
+ !nomall # Give a golden apple to every connected user.
39
+ !kit <group> # Gives a kit to the user.
40
+ !kitall <group> # Give a kit to every connected user.
41
+ !kitlist # Lists the available kits.
42
+ !addtimer <item> <frequency> # Adds a timer to give <item> to the user ever <frequency> seconds.
43
+ !deltimer <item> # Delete a timer associated with <item>.
44
+ !printtimer # Print timers of the user.
45
+ !printtime # Print the current counter time.
46
+ !list # List the connected users, notes ops.
47
+ !property # Check a server property (!property spawn-monsters).
48
+ !s <label> <command> # Associates a command to a shortcut label for the user.
49
+ !s <label> # Runs the command with the associated label.
50
+ !shortcuts # Lists the users shortcuts.
51
+ !rules # Prints the server rules.
52
+ !uptime <user> # Prints the uptime of the given user.
53
+ !uptime # Prints the uptime of the current user.
54
+ !help # Outputs the help contents.
55
+
56
+ Development Path
57
+ ----------------
58
+
59
+ ### Major Release Milestone
60
+
61
+ - Complete test coverage
62
+ - Complete documentation coverage
63
+ - All feature requests closed
64
+ - All bugs closed
65
+ - Thorough testing done across multiple environments
66
+
67
+
68
+ ### Semantic Versioning
69
+
70
+ - http://semver.org
71
+ - Currently patch level releases are being made, nothing is stable yet.
72
+ - Once tests and documentation has been written a stable minor release will be
73
+ committed.
74
+ - Once the repository is completely stable and I am satisified with the feature
75
+ set, a major version will be released. Any chances afterwards are backwards
76
+ compatible until the major version is incremented.
52
77
 
53
78
  Contributors
54
79
  ------------
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :test do
4
+ $:.unshift "./test"
5
+ Dir.glob("test/*_test.rb").each { |test| require "./#{test}" }
6
+ end
7
+
8
+ task :default => :test
@@ -1,7 +1,19 @@
1
1
  module Minecraft
2
+ # Contains all the methods for console commands executed by a connected
3
+ # player.
2
4
  module Commands
3
5
  include Data
4
6
 
7
+ # Give command takes an item name or numeric id and a quantifier. If a
8
+ # quantifier is not specified then the quantity defaults to 1. Items will
9
+ # try to resolved if they are not an exact match.
10
+ #
11
+ # @param [String] user Target user of the command.
12
+ # @example
13
+ # give("basicxman", "cobblestone")
14
+ # give("basicxman", "cobblestone", "9m")
15
+ # give("basicxman", "flint", "and", "steel", "1")
16
+ # give("basicxman", "4")
5
17
  def give(user, *args)
6
18
  item, quantity = items_arg(1, args)
7
19
  item = resolve_item(item)
@@ -9,12 +21,21 @@ module Minecraft
9
21
  construct_give(user, item, quantity)
10
22
  end
11
23
 
24
+ # Validates a kit group, if the kit cannot be found it executes the
25
+ # !kitlist command.
12
26
  def validate_kit(group = "")
13
27
  return true if KITS.include? group.to_sym
14
28
  @server.puts "say #{group} is not a valid kit."
15
29
  kitlist
16
30
  end
17
31
 
32
+ # Kit command takes a group name and gives the contents of the kit to the
33
+ # target user.
34
+ #
35
+ # @param [String] user Target user of the command.
36
+ # @param [String] group Label of the kit.
37
+ # @example
38
+ # give("basicxman", "diamond")
18
39
  def kit(user, group)
19
40
  KITS[group.to_sym].each do |item|
20
41
  if item.is_a? Array
@@ -25,22 +46,53 @@ module Minecraft
25
46
  end
26
47
  end
27
48
 
49
+ # Teleports the current user to the target user.
50
+ #
51
+ # @param [String] user Current user.
52
+ # @param [String] target User to teleport to.
53
+ # @example
54
+ # tp("basicxman", "mike_n_7")
28
55
  def tp(user, target)
29
56
  @server.puts "tp #{user} #{target}"
30
57
  end
31
58
 
59
+ # Teleports all users to the target user. Overrides !tpall.
60
+ #
61
+ # @param [String] user Current (target) user.
62
+ # @example
63
+ # tp("basicxman")
32
64
  def tpall(user, *args)
33
65
  @users.each { |u| tp(u, user) }
34
66
  end
35
67
 
68
+ # Gives a golden apple to the specified user.
69
+ #
70
+ # @param [String] user Target user.
71
+ # @example
72
+ # nom("basicxman")
36
73
  def nom(user)
37
74
  @server.puts "give #{user} 322 1"
38
75
  end
39
76
 
77
+ # Outputs the current value of a server property.
78
+ #
79
+ # @param [String] user The requesting user.
80
+ # @param [String] key The server property requested.
81
+ # @example
82
+ # property("basicxman", "spawn-monsters")
40
83
  def property(user, key)
41
84
  @server.puts "say #{key} is currently #{@server_properties[key]}" if @server_properties.include? key
42
85
  end
43
86
 
87
+ # Checks the current uptime of the current or target user. Prints their
88
+ # connected uptime and their total uptime. If no target user is specified
89
+ # it will check the requesting user.
90
+ #
91
+ # @param [String] user The requesting user.
92
+ # @param [String] target_user The user to check.
93
+ # @example
94
+ # uptime("basicxman")
95
+ # uptime("basicxman", "mike_n_7")
44
96
  def uptime(user, target_user = nil)
45
97
  target_user ||= user
46
98
  time_spent = calculate_uptime(target_user)
@@ -50,10 +102,20 @@ module Minecraft
50
102
  @server.puts "say #{target_user} has been online for #{format_uptime(time_spent)} minutes.#{total}"
51
103
  end
52
104
 
105
+ # Will print the server rules to all connected players.
106
+ #
107
+ # @example
108
+ # rules()
53
109
  def rules(*args)
54
110
  @server.puts "say #{@rules}"
55
111
  end
56
112
 
113
+ # Lists the currently connecting players, noting which is the requesting
114
+ # user and which users are ops.
115
+ #
116
+ # @param [String] user The requesting user.
117
+ # @example
118
+ # list("basicxman")
57
119
  def list(user)
58
120
  l = @users.inject("") do |s, u|
59
121
  if u == user
@@ -66,6 +128,16 @@ module Minecraft
66
128
  @server.puts "say #{l}"
67
129
  end
68
130
 
131
+ # Adds a timer to the requesting users timers, the item and frequency in
132
+ # seconds of the timer should be specified. If the timer already exists
133
+ # for that item, the frequency is re-assigned. If the frequency is
134
+ # unspecified, it will default to 30.
135
+ #
136
+ # @param [String] user The requesting user.
137
+ # @param args item, frequency
138
+ # @example
139
+ # addtimer("basicxman", "cobblestone")
140
+ # addtimer("basicxman", "arrow", "10")
69
141
  def addtimer(user, *args)
70
142
  item, duration = items_arg(30, args)
71
143
  item = resolve_item(item)
@@ -74,12 +146,23 @@ module Minecraft
74
146
  @server.puts "say Timer added for #{user}. Giving item id #{item} every #{duration} seconds."
75
147
  end
76
148
 
149
+ # Deletes a timer from the requesting user.
150
+ #
151
+ # @param [String] user The requesting user.
152
+ # @param args item
153
+ # @example
154
+ # deltimer("basicxman", "cobblestone")
77
155
  def deltimer(user, *args)
78
156
  item = args.join(" ")
79
157
  item = resolve_item(item)
80
158
  @timers[user][item] = nil if @timers.has_key? user
81
159
  end
82
160
 
161
+ # Prints the requesting users current timers.
162
+ #
163
+ # @param [String] user The requesting user.
164
+ # @example
165
+ # printtimer("basicxman")
83
166
  def printtimer(user)
84
167
  unless @timers.has_key? user || @timers[user].length == 0
85
168
  @server.puts "say No timers have been added for #{user}."
@@ -90,10 +173,24 @@ module Minecraft
90
173
  end
91
174
  end
92
175
 
176
+ # Prints the current value of the counter (seconds since server
177
+ # initialized).
178
+ #
179
+ # @example
180
+ # printtimer("basicxman")
93
181
  def printtime(user)
94
182
  @server.puts "say Timer is at #{@counter}."
95
183
  end
96
184
 
185
+ # Adds a shortcut for the user with a given label. Shortcuts can only be
186
+ # given for custom commands. If only a label is given, the shortcut is
187
+ # executed.
188
+ #
189
+ # @param [String] user The requesting user.
190
+ # @param args label, command array
191
+ # @example
192
+ # s("basicxman", "cobble", "give", "cobblestone", "64")
193
+ # s("basicxman", "mike", "tp", "mike_n_7")
97
194
  def s(user, *args)
98
195
  return @server.puts "say You need to specify a shortcut silly!" if args.length == 0
99
196
 
@@ -109,11 +206,20 @@ module Minecraft
109
206
  @server.puts "say Shortcut labelled #{shortcut_name} for #{user} has been added."
110
207
  end
111
208
 
209
+ # Prints the requested users shortcuts.
210
+ #
211
+ # @param [String] user The requesting user.
212
+ # @example
213
+ # shortcuts("basicxman")
112
214
  def shortcuts(user, *args)
113
215
  labels = @shortcuts[user].keys.join(", ") if @shortcuts.has_key? user
114
216
  @server.puts "say Shortcuts for #{user}: #{labels}."
115
217
  end
116
218
 
219
+ # Prints the help contents.
220
+ #
221
+ # @example
222
+ # help()
117
223
  def help(*args)
118
224
  @server.puts <<-eof
119
225
  say !tp target_user
@@ -126,10 +232,24 @@ say !deltimer item
126
232
  eof
127
233
  end
128
234
 
235
+ # Prints the list of available kits to the connected players.
236
+ #
237
+ # @example
238
+ # kitlist()
129
239
  def kitlist(*args)
130
240
  @server.puts "say Kits: #{KITS.keys.join(", ")}"
131
241
  end
132
242
 
243
+ # Helper method for printing the final give statements to the server. If a
244
+ # quantity is above the default upper bound (64) it will print multiple
245
+ # statements.
246
+ #
247
+ # @param [String] user The requesting user.
248
+ # @param [Integer] item The item id.
249
+ # @param [Integer] quantity The quantity of the item to give.
250
+ # @example
251
+ # construct_give("basicxman", 4, 64)
252
+ # construct_give("basicxman", 4, 2560)
133
253
  def construct_give(user, item, quantity)
134
254
  if quantity <= 64
135
255
  @server.puts "give #{user} #{item} #{quantity}"
@@ -143,6 +263,16 @@ say !deltimer item
143
263
  @server.puts "give #{user} #{item} #{sub_quantity}" if sub_quantity > 0
144
264
  end
145
265
 
266
+ # Takes an array and decides if a quantifier is specified or not, returns
267
+ # the quantity and full item name.
268
+ #
269
+ # @param [Integer] default The default quantity to give if none is given.
270
+ # @return [Array] full_item_name, quantity
271
+ # @example
272
+ # items_arg(1, ["flint", "and", "steel"])
273
+ # items_arg(1, ["4"])
274
+ # items_arg(1, ["4", "1"])
275
+ # items_arg(1, ["flint", "and", "steel", "32"])
146
276
  def items_arg(default, args)
147
277
  if args.length == 1
148
278
  second = default
@@ -159,10 +289,29 @@ say !deltimer item
159
289
  return [first, second]
160
290
  end
161
291
 
292
+ # Resolves a full item name, checks if it is a numeric value and if not
293
+ # attempts an approximate match on the key and gets the value from the
294
+ # data value hash.
295
+ #
296
+ # @param [String] item The full item name.
297
+ # @return [String] The resolved item id.
298
+ # @example
299
+ # resolve_item("4")
300
+ # resolve_item("cobb")
301
+ # resolve_item("cobblestone")
162
302
  def resolve_item(item)
163
- item.to_i.to_s == item ? item.to_i : DATA_VALUE_HASH[resolve_key(item.downcase)]
303
+ item.to_i.to_s == item ? item : DATA_VALUE_HASH[resolve_key(item.downcase)]
164
304
  end
165
305
 
306
+ # Looks for an approximate match of a key, prints an error message to
307
+ # the player if the key does not exist.
308
+ #
309
+ # @param [String] key The approximate item name.
310
+ # @return [String] The proper item id.
311
+ # @example
312
+ # resolve_key("cobb")
313
+ # resolve_key("cobblestone")
314
+ # resolve_key("torches")
166
315
  def resolve_key(key)
167
316
  bucket = key[0]
168
317
  return no_key(key) unless ITEM_BUCKETS.include? bucket
@@ -188,27 +337,50 @@ say !deltimer item
188
337
  return shortest_key
189
338
  end
190
339
 
340
+ # Prints a no key error the server.
191
341
  def no_key(key)
192
342
  @server.puts "say No item #{key} found."
193
343
  end
194
344
 
345
+ # Checks if a value is a valid quantifier or not.
346
+ #
347
+ # @param [String] quantity The potential quantifier.
348
+ # @return [Boolean]
349
+ # @example
350
+ # is_quantifier? "x"
351
+ # is_quantifier? "1m"
352
+ # is_quantifier? "2d"
353
+ # is_quantifier? "1s"
354
+ # is_quantifier? "1"
355
+ # is_quantifier? "steel"
195
356
  def is_quantifier?(quantity)
196
357
  quantity.index(/[0-9]+[a-z]?/) == 0
197
358
  end
198
359
 
360
+ # Compute a final quantity from a quantifier value.
361
+ #
362
+ # @param [String] value The quantifier.
363
+ # @return [Integer] The resulting quantity, upper bounded to 2560.
364
+ # @example
365
+ # quantify("1m")
366
+ # quantify("2d")
367
+ # quantify("34s")
368
+ # quantify("8m2d")
369
+ # quantify("64")
199
370
  def quantify(value)
200
- return value.scan(/[0-9]+[a-z]?/).inject(0) do |total, term|
371
+ quantity = value.scan(/[0-9]+[a-z]?/).inject(0) do |total, term|
201
372
  quantity, flag = term.match(/([0-9]+)([a-z]?)/)[1..2]
202
373
  quantity = quantity.to_i
203
374
  return total + quantity if flag.nil?
204
375
 
205
376
  total + case flag
206
- when 'm' then [2560, quantity * 64].min
377
+ when 'm' then quantity * 64
207
378
  when 'd' then (64.0 / [1, quantity].max).round
208
379
  when 's' then [1, 64 - quantity].max
209
380
  else quantity
210
381
  end
211
382
  end
383
+ return [2560, quantity].min
212
384
  end
213
385
  end
214
386
  end
@@ -1,5 +1,7 @@
1
1
  module Minecraft
2
+ # Contains kit and item hashes takes care of buckets for items.
2
3
  module Data
4
+ # Item kits.
3
5
  KITS = {
4
6
  :diamond => [276, 277, 278, 279, 293],
5
7
  :goldarmour => [314, 315, 316, 317],
@@ -10,6 +12,7 @@ module Minecraft
10
12
  :redstone => [[331, 256], [356, 64], [69, 64], [77, 64], [70, 64]]
11
13
  }
12
14
 
15
+ # All item keys.
13
16
  DATA_VALUE_HASH = {
14
17
  "glass" => "20",
15
18
  "purple wool" => "35",
@@ -248,6 +251,7 @@ module Minecraft
248
251
  "cocoa beans" => "351"
249
252
  }
250
253
 
254
+ # Alphabetical buckets for quicker approximate matching. Filled on init.
251
255
  ITEM_BUCKETS = {}
252
256
  DATA_VALUE_HASH.each_key do |key|
253
257
  bucket = key[0]
@@ -1,7 +1,14 @@
1
1
  module Minecraft
2
+ # An Extensions instance is meant to process pipes from a Server instance and
3
+ # manage custom functionality additional to default Notchian Minecraft
4
+ # behaviour.
2
5
  class Extensions
3
6
  include Commands
4
7
 
8
+ # New Extensions instance.
9
+ #
10
+ # @param [IO] server The standard input pipe of the server process.
11
+ # @param [Slop] opts Command line options from Slop.
5
12
  def initialize(server, opts)
6
13
  @ops = File.readlines("ops.txt").map { |s| s.chomp }
7
14
  @userlog = get_json("user.log")
@@ -38,6 +45,7 @@ module Minecraft
38
45
  add_command(:property, :ops => true, :all => false)
39
46
  end
40
47
 
48
+ # Gets a hash from a JSON file (or a blank one).
41
49
  def get_json(file)
42
50
  if File.exists? file
43
51
  JSON.parse(File.read(file))
@@ -46,23 +54,46 @@ module Minecraft
46
54
  end
47
55
  end
48
56
 
57
+ # Save the user timers and shortcuts hash to a data file.
49
58
  def save
50
59
  save_file :timers
51
60
  save_file :shortcuts
52
61
  end
53
62
 
63
+ # Save an instance hash to it's associated data file.
64
+ #
65
+ # @param [Symbol] var
66
+ # @example
67
+ # save_file :timers
54
68
  def save_file(var)
55
69
  File.open("user_#{var}.json", "w") { |f| f.print instance_variable_get("@#{var}").to_json }
56
70
  end
57
71
 
72
+ # Writes the user uptime log to disk.
58
73
  def write_log
59
74
  File.open("user.log", "w") { |f| f.print @userlog.to_json }
60
75
  end
61
76
 
77
+ # Complicated method to decide the logic of calling a command. Checks
78
+ # if the command requires op privileges and whether an `all` version is
79
+ # available and has been requested.
80
+ #
81
+ # Figures out the root portion of the command, checks if a validation
82
+ # method is available, if so it will be executed. Then checks if op
83
+ # privileges are required, any `all` version of the command requires
84
+ # ops as it affects all users. The corresponding method will be called,
85
+ # if an `all` command is used it will call the corresponding method if
86
+ # available or loop through the base command for each connected user.
87
+ #
88
+ # @param [String] user The requesting user.
89
+ # @param [String] command The requested command.
90
+ # @param args Arguments for the command.
91
+ # @example
92
+ # call_command("basicxman", "give", "cobblestone", "64")
62
93
  def call_command(user, command, *args)
63
94
  is_all = command.to_s.end_with? "all"
64
95
  root = command.to_s.chomp("all").to_sym
65
- return invalid_command(command) unless @commands.include? root
96
+ return send(root, user, *args) unless @commands.include? root
66
97
 
67
98
  # Any `all` suffixed command requires ops.
68
99
  if @commands[root][:ops] or (is_all and @commands[root][:all])
@@ -85,10 +116,20 @@ module Minecraft
85
116
  end
86
117
  end
87
118
 
119
+ # Add a command to the commands instance hash
120
+ #
121
+ # @param [Symbol] command The command to add.
122
+ # @option opts [Boolean] :all
123
+ # Whether or not an all version of the command should be made available.
124
+ # @option opts [Boolean] :ops
125
+ # Whether or not the base command requires ops.
126
+ # @option opts [String] :all_message
127
+ # The message to print when the all version is used.
88
128
  def add_command(command, opts)
89
129
  @commands[command] = opts
90
130
  end
91
131
 
132
+ # Processes a line from the console.
92
133
  def process(line)
93
134
  puts line
94
135
  return info_command(line) if line.index "INFO"
@@ -98,6 +139,8 @@ module Minecraft
98
139
  puts e.backtrace
99
140
  end
100
141
 
142
+ # Checks if the server needs to be saved and prints the save-all command if
143
+ # so.
101
144
  def check_save
102
145
  if @savefreq.nil?
103
146
  freq = 30
@@ -109,6 +152,8 @@ module Minecraft
109
152
  @server.puts "save-all" if @counter % freq == 0
110
153
  end
111
154
 
155
+ # Increments the counter and checks if any timers are needed to be
156
+ # executed.
112
157
  def periodic
113
158
  @counter += 1
114
159
  check_save
@@ -121,6 +166,11 @@ module Minecraft
121
166
  end
122
167
  end
123
168
 
169
+ # Removes the meta data (timestamp, INFO) from the line and then executes a
170
+ # series of checks on the line. Grabs the user and command from the line
171
+ # and then calls the call_command method.
172
+ #
173
+ # @param [String] line The line from the console.
124
174
  def info_command(line)
125
175
  line.gsub! /^.*?\[INFO\]\s+/, ''
126
176
  return if meta_check(line)
@@ -132,17 +182,26 @@ module Minecraft
132
182
  call_command(user, args.slice!(0).to_sym, *args)
133
183
  end
134
184
 
185
+ # Executes the meta checks (kick/ban, ops, join/disconnect) and returns true
186
+ # if any of them were used (and thus no further processing required).
187
+ #
188
+ # @param [String] line The passed line from the console.
135
189
  def meta_check(line)
136
190
  return true if check_kick_ban(line)
137
191
  return true if check_ops(line)
138
192
  return true if check_join_part(line)
139
193
  end
140
194
 
195
+ # Removes the specified user as from the connected players array.
196
+ #
197
+ # @param [String] user The specified user.
198
+ # @return [Boolean] Always returns true.
141
199
  def remove_user(user)
142
200
  @users.reject! { |u| u.downcase == user.downcase }
143
201
  return true
144
202
  end
145
203
 
204
+ # Check if a console line has informed us about a ban or kick.
146
205
  def check_kick_ban(line)
147
206
  user = line.split(" ").last
148
207
  if line.index "Banning"
@@ -152,6 +211,7 @@ module Minecraft
152
211
  end
153
212
  end
154
213
 
214
+ # Check if a console line has informed us about a [de-]op privilege change.
155
215
  def check_ops(line)
156
216
  user = line.split(" ").last
157
217
  if line.index "De-opping"
@@ -163,6 +223,7 @@ module Minecraft
163
223
  end
164
224
  end
165
225
 
226
+ # Check if a console line has informed us about a player [dis]connecting.
166
227
  def check_join_part(line)
167
228
  user = line.split(" ").first
168
229
  if line.index "lost connection"
@@ -176,14 +237,20 @@ module Minecraft
176
237
  end
177
238
  end
178
239
 
240
+ # If a command method is called and is not specified, take in the arguments
241
+ # here and attempt to !give the player the item. Otherwise print an error.
179
242
  def method_missing(sym, *args)
180
- if DATA_VALUE_HASH.has_key? sym.downcase and is_op? args.first
181
- give(args.first, sym, args.last)
243
+ item, quantity = items_arg(1, [sym.to_s.downcase, args.last])
244
+ item = resolve_item(item)
245
+ if item and is_op? args.first
246
+ give(args.first, item, quantity.to_s)
182
247
  else
183
- puts "Invalid command given."
248
+ puts "#{item} is invalid."
184
249
  end
185
250
  end
186
251
 
252
+ # Calculate and print the time spent by a recently disconnected user. Save
253
+ # the user uptime log.
187
254
  def log_time(user)
188
255
  time_spent = calculate_uptime(user)
189
256
  @userlog[user] ||= 0
@@ -192,27 +259,46 @@ module Minecraft
192
259
  write_log
193
260
  end
194
261
 
262
+ # Format an uptime for printing. Should not be used for logging.
263
+ #
264
+ # @param [Integer] time Time difference in seconds.
265
+ # @return [Float] Returns the number of minutes rounded to two precision.
195
266
  def format_uptime(time)
196
267
  (time / 60.0).round(2)
197
268
  end
198
269
 
270
+ # Calculate a users current uptime.
271
+ #
272
+ # @param [String] user The specified user.
273
+ # @return [Integer] The uptime in seconds.
199
274
  def calculate_uptime(user)
200
275
  Time.now - @logon_time[user]
201
276
  end
202
277
 
278
+ # Check if a user has op privileges.
279
+ #
280
+ # @param [String] user The specified user.
281
+ # @return [Boolean]
203
282
  def is_op?(user)
204
283
  @ops.include? user.downcase
205
284
  end
206
285
 
286
+ # Check if a user is ops and print a privilege error if not.
287
+ #
288
+ # @param [String] user The specified user.
289
+ # @param [String] command The command they tried to use.
290
+ # @return [Boolean] Returns true if the user is an op.
207
291
  def validate_ops(user, command)
208
292
  return true if is_op? user
209
293
  @server.puts "say #{user} is not an op, cannot use !#{command}."
210
294
  end
211
295
 
296
+ # An error message for invalid commands.
212
297
  def invalid_command(command)
213
298
  @server.puts "say #{command} is invalid."
214
299
  end
215
300
 
301
+ # Load the server.properties file into a Ruby hash.
216
302
  def load_server_properties
217
303
  @server_properties = {}
218
304
  File.readlines("server.properties").each do |line|
@@ -222,6 +308,7 @@ module Minecraft
222
308
  end
223
309
  end
224
310
 
311
+ # Display a welcome message to a recently connected user.
225
312
  def display_welcome_message(user)
226
313
  @server.puts "say #{@welcome_message.gsub('%', user)}" unless @welcome_message.nil?
227
314
  end
@@ -1,5 +1,15 @@
1
1
  module Minecraft
2
+ # An instance of the Runtime class will spool up the server and do any
3
+ # preliminary work.
2
4
  class Runtime
5
+ # New Runtime instance.
6
+ #
7
+ # @param [Slop] opts Command line options from slop.
8
+ # @example
9
+ # opts = Slop.parse do
10
+ # ...
11
+ # end
12
+ # runtime = Runtime.new(opts)
3
13
  def initialize(opts)
4
14
  @opts = opts
5
15
  pre_checks
@@ -13,6 +23,8 @@ module Minecraft
13
23
  end
14
24
  end
15
25
 
26
+ # Checks if Minecraft needs to be updated, checks if mobs are to be
27
+ # toggled.
16
28
  def pre_checks
17
29
  Minecraft::Tools.download_minecraft if @opts.update?
18
30
  puts "[+] Temporarily toggling mobs. Setting to #{Minecraft::Tools.toggle_mobs}." if @opts.tempmobs?
@@ -1,9 +1,21 @@
1
1
  require "open3"
2
2
 
3
3
  module Minecraft
4
+ # An instance of the server class will start a Java subprocess running the
5
+ # Minecraft server jarfile. The interrupt signal will be trapped to run an
6
+ # exit hook and threads will be created to process all pipes.
4
7
  class Server
5
8
  attr_accessor :sin
6
9
 
10
+ # New Server instance.
11
+ #
12
+ # @param [String] command The command for the subprocess.
13
+ # @param [Slop] opts Command line options from Slop.
14
+ # @example
15
+ # opts = Slop.new do
16
+ # ...
17
+ # end
18
+ # server = Server.new("java -jar minecraft_server.jar", opts)
7
19
  def initialize(command, opts)
8
20
  @sin, @sout, @serr, @thr = Open3.popen3(command)
9
21
  @extensions = Extensions.new(@sin, opts)
@@ -20,6 +32,8 @@ module Minecraft
20
32
  exit!
21
33
  end
22
34
 
35
+ # An exit hook, checks if mobs need to be untoggled and saves the server.
36
+ # Server is stopped gracefully.
23
37
  def minecraft_exit
24
38
  puts "[+] Restoring previous mob state to #{Minecraft::Tools.toggle_mobs}." if @opts.tempmobs?
25
39
  puts "\n[+] Saving..."
@@ -1,27 +1,35 @@
1
1
  require "net/http"
2
2
 
3
3
  module Minecraft
4
+ # Methods for external manipulation of the current deployment.
4
5
  module Tools
6
+ # Checks if minecraft_server.jar and calls the download method if not.
5
7
  def self.check_jarfile
6
8
  download_minecraft unless File.exists? "minecraft_server.jar"
7
9
  end
8
10
 
11
+ # Downloads the minecraft server jarfile into the current directory.
9
12
  def self.download_minecraft
10
13
  url = get_minecraft_page
11
14
  puts "[+] Downloading Minecraft server..."
12
15
  `wget http://minecraft.net/#{url} -O minecraft_server.jar -q`
13
16
  end
14
17
 
18
+ # Parses the miencraft.net download page for the current jarfile URL.
15
19
  def self.get_minecraft_page
16
20
  page = Net::HTTP.get("www.minecraft.net", "/download.jsp")
17
21
  data = page.match /\"([0-9a-zA-Z_\/]*minecraft_server\.jar\?v=[0-9]+)/
18
22
  data[1]
19
23
  end
20
24
 
25
+ # Generates a command for running the server with default settings including memory.
26
+ #
27
+ # @param [Slop] opts Command line options from Slop, used for memory specification.
21
28
  def self.command(opts)
22
29
  "java -Xmx#{opts[:max_memory] || "1024M"} -Xms#{opts[:min_memory] || "1024M"} -jar minecraft_server.jar nogui"
23
30
  end
24
31
 
32
+ # Toggles mobs in server.properties and returns the new state.
25
33
  def self.toggle_mobs
26
34
  content = File.read("server.properties")
27
35
  state = content.match(/spawn\-monsters=(true|false)/)[1]
@@ -1,3 +1,5 @@
1
+ # Global Minecraft module.
1
2
  module Minecraft
2
- VERSION = "0.0.5"
3
+ # Yay, keep these versions coming!
4
+ VERSION = "0.1.0"
3
5
  end
@@ -0,0 +1,80 @@
1
+ require "helper"
2
+ require "minecraft"
3
+
4
+ class CommandsTest < Test
5
+ include Minecraft::Commands
6
+ test "should properly check quantifiers" do
7
+ assert is_quantifier? "1"
8
+ assert is_quantifier? "10"
9
+ assert is_quantifier? "9m"
10
+ assert is_quantifier? "9mm"
11
+ refute is_quantifier? "m"
12
+ refute is_quantifier? "steel"
13
+ refute is_quantifier? "!"
14
+ end
15
+
16
+ test "should propery quantify values" do
17
+ assert_equal 1, quantify("1")
18
+ assert_equal 64, quantify("1m")
19
+ assert_equal 63, quantify("1s")
20
+ assert_equal 64, quantify("1d")
21
+ assert_equal 1, quantify("1i")
22
+ assert_equal 32, quantify("2d")
23
+ assert_equal 160, quantify("2d2m")
24
+ assert_equal 2560, quantify("41m")
25
+ assert_equal 2560, quantify("100000")
26
+ assert_equal 64, quantify("0d")
27
+ end
28
+
29
+ test "should resolve keys" do
30
+ @server = StringIO.new
31
+ assert_equal "glass", resolve_key("glass")
32
+ assert_equal "redstone", resolve_key("redstone")
33
+ assert_equal "redstone", resolve_key("reds")
34
+ assert_equal "cobblestone", resolve_key("cobb")
35
+ assert_equal "cobweb", resolve_key("cob")
36
+ assert_nil resolve_key("asdf")
37
+ end
38
+
39
+ test "should properly distinguish items and quantities" do
40
+ assert_equal ["flint", 1], items_arg(1, ["flint"])
41
+ assert_equal ["flint and steel", 1], items_arg(1, ["flint", "and", "steel"])
42
+ assert_equal ["flint", 64], items_arg(1, ["flint", "64"])
43
+ assert_equal ["flint and steel", 64], items_arg(1, ["flint", "and", "steel", "64"])
44
+ end
45
+
46
+ test "should properly resolve an item id" do
47
+ @server = StringIO.new
48
+ assert_equal "5", resolve_item("5")
49
+ assert_equal "81", resolve_item("cactus")
50
+ assert_nil resolve_item("asdf")
51
+ end
52
+
53
+ test "give command should give a single slot worth" do
54
+ @server = StringIO.new
55
+ give("foo", "cobblestone", "64")
56
+ result = <<eof
57
+ give foo 4 64
58
+ eof
59
+ assert_equal result, @server.string
60
+ end
61
+
62
+ test "give command should work for multiple slot spans" do
63
+ @server = StringIO.new
64
+ give("foo", "cobblestone", "2m")
65
+ result = <<eof
66
+ give foo 4 64
67
+ give foo 4 64
68
+ eof
69
+ assert_equal result, @server.string
70
+ end
71
+
72
+ test "give command should give less than a single slot span" do
73
+ @server = StringIO.new
74
+ give("foo", "cobblestone", "32")
75
+ result = <<eof
76
+ give foo 4 32
77
+ eof
78
+ assert_equal result, @server.string
79
+ end
80
+ end
@@ -0,0 +1,14 @@
1
+ unless Object.const_defined? 'Minecraft'
2
+ $:.unshift File.expand_path("../../lib", __FILE__)
3
+ require "minecraft"
4
+ end
5
+
6
+ require "minitest/autorun"
7
+ require "stringio"
8
+ require "turn"
9
+
10
+ class Test < MiniTest::Unit::TestCase
11
+ def self.test(name, &block)
12
+ define_method("test_#{name.gsub(/\W/, '_')}", block)
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ require "helper"
2
+ require "minecraft/tools"
3
+
4
+ class ToolsTest < Test
5
+ test "should get minecraft download url" do
6
+ url = Minecraft::Tools.get_minecraft_page
7
+ refute_nil url.index "minecraft_server.jar"
8
+ end
9
+
10
+ test "should generate a proper default command" do
11
+ command = Minecraft::Tools.command({})
12
+ assert_equal "java -Xmx1024M -Xms1024M -jar minecraft_server.jar nogui", command
13
+ end
14
+
15
+ test "should properly modify server properties when toggling mobs" do
16
+ File.open("server.properties", "w") do |f| f.print <<-eof
17
+ #Minecraft server properties
18
+ #Thu May 26 15:19:31 EDT 2011
19
+ view-distance=10
20
+ spawn-monsters=false
21
+ online-mode=true
22
+ eof
23
+ end
24
+ assert_equal "true", Minecraft::Tools.toggle_mobs
25
+ refute_nil File.read("server.properties").index("spawn-monsters=true")
26
+ assert_equal "false", Minecraft::Tools.toggle_mobs
27
+ refute_nil File.read("server.properties").index("spawn-monsters=false")
28
+ FileUtils.rm_f("server.properties")
29
+ end
30
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: minecraft
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.5
5
+ version: 0.1.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Andrew Horsman
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-08-18 00:00:00 -04:00
13
+ date: 2011-08-19 00:00:00 -04:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -35,6 +35,8 @@ extra_rdoc_files: []
35
35
 
36
36
  files:
37
37
  - .gitignore
38
+ - .yardopts
39
+ - CHANGES.md
38
40
  - Gemfile
39
41
  - README.md
40
42
  - Rakefile
@@ -48,6 +50,9 @@ files:
48
50
  - lib/minecraft/tools.rb
49
51
  - lib/minecraft/version.rb
50
52
  - minecraft.gemspec
53
+ - test/commands_test.rb
54
+ - test/helper.rb
55
+ - test/tools_test.rb
51
56
  has_rdoc: true
52
57
  homepage: http://github.com/basicxman/minecraft
53
58
  licenses: []