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