minecraft 0.0.5 → 0.1.0

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 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: []