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 +3 -0
- data/.yardopts +5 -0
- data/CHANGES.md +54 -0
- data/README.md +49 -24
- data/Rakefile +7 -0
- data/lib/minecraft/commands.rb +175 -3
- data/lib/minecraft/data.rb +4 -0
- data/lib/minecraft/extensions.rb +91 -4
- data/lib/minecraft/runtime.rb +12 -0
- data/lib/minecraft/server.rb +14 -0
- data/lib/minecraft/tools.rb +8 -0
- data/lib/minecraft/version.rb +3 -1
- data/test/commands_test.rb +80 -0
- data/test/helper.rb +14 -0
- data/test/tools_test.rb +30 -0
- metadata +7 -2
data/.gitignore
CHANGED
data/.yardopts
ADDED
data/CHANGES.md
ADDED
@@ -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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
data/lib/minecraft/commands.rb
CHANGED
@@ -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
|
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
|
-
|
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
|
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
|
data/lib/minecraft/data.rb
CHANGED
@@ -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]
|
data/lib/minecraft/extensions.rb
CHANGED
@@ -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
|
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
|
-
|
181
|
-
|
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 "
|
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
|
data/lib/minecraft/runtime.rb
CHANGED
@@ -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?
|
data/lib/minecraft/server.rb
CHANGED
@@ -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..."
|
data/lib/minecraft/tools.rb
CHANGED
@@ -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]
|
data/lib/minecraft/version.rb
CHANGED
@@ -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
|
data/test/helper.rb
ADDED
@@ -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
|
data/test/tools_test.rb
ADDED
@@ -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
|
+
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-
|
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: []
|