mcblocky 0.1.0.pre.alpha.pre.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +11 -0
- data/.travis.yml +16 -0
- data/Gemfile +4 -0
- data/README.md +34 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bin/workon.cmd +1 -0
- data/doc/Introduction.md +144 -0
- data/examples/ctf/arena.rb +14 -0
- data/examples/ctf/config.example.yml +15 -0
- data/examples/ctf/ctf.rb +316 -0
- data/examples/ctf/lobby.rb +20 -0
- data/examples/hello/config.example.yml +14 -0
- data/examples/hello/hello.rb +81 -0
- data/examples/hello/helpers/bar.rb +3 -0
- data/examples/hello/helpers/foo.rb +3 -0
- data/exe/mcblocky +6 -0
- data/lib/mcblocky/cli.rb +108 -0
- data/lib/mcblocky/config.rb +63 -0
- data/lib/mcblocky/context.rb +87 -0
- data/lib/mcblocky/dsl/block.rb +13 -0
- data/lib/mcblocky/dsl/command_block.rb +23 -0
- data/lib/mcblocky/dsl/commands.rb +152 -0
- data/lib/mcblocky/dsl/container.rb +24 -0
- data/lib/mcblocky/dsl/repeat_chain.rb +9 -0
- data/lib/mcblocky/dsl/selector.rb +29 -0
- data/lib/mcblocky/dsl.rb +146 -0
- data/lib/mcblocky/executor.rb +149 -0
- data/lib/mcblocky/listener.rb +69 -0
- data/lib/mcblocky/location.rb +95 -0
- data/lib/mcblocky/logging.rb +45 -0
- data/lib/mcblocky/server.rb +143 -0
- data/lib/mcblocky/version.rb +3 -0
- data/lib/mcblocky.rb +23 -0
- data/mcblocky.gemspec +28 -0
- metadata +151 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'helpers/foo'
|
2
|
+
|
3
|
+
helper 'hello' do |args, user|
|
4
|
+
server.say "Hello, #{user}!"
|
5
|
+
end
|
6
|
+
|
7
|
+
helper 'clean' do |args, user|
|
8
|
+
Executor.to_commands($context).each{|c| server.command c}
|
9
|
+
end
|
10
|
+
|
11
|
+
initial do
|
12
|
+
gamerule 'doDaylightCycle', false
|
13
|
+
gamerule 'commandBlockOutput', false
|
14
|
+
gamerule 'logAdminCommands', false
|
15
|
+
time :set, 'day'
|
16
|
+
|
17
|
+
['red', 'blue', 'yellow', 'green'].each do |color|
|
18
|
+
scoreboard :teams do
|
19
|
+
add color.capitalize
|
20
|
+
option color.capitalize, :color, color
|
21
|
+
end
|
22
|
+
end
|
23
|
+
scoreboard :teams do
|
24
|
+
add 'Spectators'
|
25
|
+
option 'Spectators', :color, 'gray'
|
26
|
+
end
|
27
|
+
scoreboard :objectives do
|
28
|
+
add 'SwitchingTeam', 'dummy'
|
29
|
+
end
|
30
|
+
|
31
|
+
tellraw @a[team: 'Red'], {text: "Hello world", color: "red"}
|
32
|
+
end
|
33
|
+
|
34
|
+
repeat 171, 82, 242, 175, 84, 247 do
|
35
|
+
scoreboard :players, :set, @a[x: 172, y: 81, z: 236, r: 1, team: '!Red'], 'SwitchingTeam', 1
|
36
|
+
scoreboard :players, :set, @a[x: 170, y: 81, z: 236, r: 1, team: '!Blue'], 'SwitchingTeam', 1
|
37
|
+
execute @a[x: 170, y: 81, z: 236, r: 1, score_SwitchingTeam_min: 1], '~ ~ ~', 'scoreboard teams join Blue @p'
|
38
|
+
execute @a[x: 170, y: 81, z: 236, r: 1, score_SwitchingTeam_min: 1], '~ ~ ~', :tellraw, @a, JSON.dump([
|
39
|
+
{selector: @p},
|
40
|
+
{text: ' joined the ', color: 'reset'},
|
41
|
+
{text: 'Blue', color: 'blue'},
|
42
|
+
{text: ' team', color: 'reset'}
|
43
|
+
])
|
44
|
+
execute @a[x: 172, y: 81, z: 236, r: 1, score_SwitchingTeam_min: 1], '~ ~ ~', 'scoreboard teams join Red @p'
|
45
|
+
execute @a[x: 172, y: 81, z: 236, r: 1, score_SwitchingTeam_min: 1], '~ ~ ~', :tellraw, @a, JSON.dump([
|
46
|
+
{selector: @p},
|
47
|
+
{text: ' joined the ', color: 'reset'},
|
48
|
+
{text: 'Red', color: 'red'},
|
49
|
+
{text: ' team', color: 'reset'}
|
50
|
+
])
|
51
|
+
scoreboard :players, :reset, @a[score_SwitchingTeam_min: 1], 'SwitchingTeam'
|
52
|
+
#tellraw @a[team: 'Red'], {text: "Hello", color: "red"}
|
53
|
+
end
|
54
|
+
|
55
|
+
fill 171, 78, 242, 181, 78, 252, 'minecraft:stained_glass', Color::BLUE
|
56
|
+
|
57
|
+
cleanup do
|
58
|
+
fill 172, 80, 243, 174, 80, 243, 'minecraft:air', 0, 'replace'
|
59
|
+
end
|
60
|
+
|
61
|
+
at 172, 79, 243 do
|
62
|
+
fill '~', '~1', '~', '~2', '~1', '~', 'minecraft:stone'
|
63
|
+
end
|
64
|
+
|
65
|
+
at 172, 81, 243 do
|
66
|
+
fill '~ ~-1 ~ ~2 ~-1 ~', 'minecraft:redstone_block'
|
67
|
+
end
|
68
|
+
|
69
|
+
setblock 175, 79, 248, 'minecraft:standing_sign', 0, 'replace', {'Text1'=>'{"text":"hello friends"}'}
|
70
|
+
|
71
|
+
furnace 176, 79, 248 do
|
72
|
+
item 'minecraft:diamond', 64
|
73
|
+
item 'minecraft:spawn_egg', 1, 0, {'EntityTag'=>{'id'=>'Chicken', 'CustomName'=>'Chickfila'}, 'display'=>{'Name'=>'Chicken Egg'}}
|
74
|
+
item 'minecraft:gold_ingot'
|
75
|
+
end
|
76
|
+
|
77
|
+
after do
|
78
|
+
# setblock 175, 79, 248, 'minecraft:standing_sign', 0, 'replace'
|
79
|
+
# blockdata 175, 79, 248, {'Text1'=>'{"text":"hola mundo"}'}
|
80
|
+
setblock 172, 80, 243, 'minecraft:redstone_block', 0, 'replace'
|
81
|
+
end
|
data/exe/mcblocky
ADDED
data/lib/mcblocky/cli.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "thor"
|
3
|
+
|
4
|
+
module McBlocky
|
5
|
+
class Cli < Thor
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
class_option :config,
|
9
|
+
desc: 'Path to config file',
|
10
|
+
default: 'config.yml',
|
11
|
+
type: :string,
|
12
|
+
aliases: '-f'
|
13
|
+
|
14
|
+
desc "list", "List commands that would be sent to the server"
|
15
|
+
option :watch, aliases: '-w'
|
16
|
+
option :diff
|
17
|
+
def list
|
18
|
+
begin
|
19
|
+
Config.load(options[:config])
|
20
|
+
rescue ArgumentError => e
|
21
|
+
log_error "Error in #{File.basename Config.config_path}:"
|
22
|
+
log_error e.message
|
23
|
+
exit 1
|
24
|
+
end
|
25
|
+
if options[:watch]
|
26
|
+
$old_context = nil
|
27
|
+
listener = Listener.from_config do |context|
|
28
|
+
Executor.to_commands(context, options[:diff] ? $old_context : nil).each{|c| puts c}
|
29
|
+
$old_context = context
|
30
|
+
end
|
31
|
+
listener.start
|
32
|
+
while true; end
|
33
|
+
else
|
34
|
+
context = Context.run_file(Config.config['code']['main'], File.dirname(Config.config_path))
|
35
|
+
Executor.to_commands(context).each{|c| puts c}
|
36
|
+
end
|
37
|
+
rescue Interrupt
|
38
|
+
end
|
39
|
+
|
40
|
+
desc "start", "Start the server"
|
41
|
+
def start
|
42
|
+
begin
|
43
|
+
Config.load(options[:config])
|
44
|
+
rescue ArgumentError => e
|
45
|
+
log_error "Error in #{File.basename Config.config_path}:"
|
46
|
+
log_error e.message
|
47
|
+
exit 1
|
48
|
+
end
|
49
|
+
$server = Server.from_config
|
50
|
+
log_status "Starting server..."
|
51
|
+
$server.start
|
52
|
+
log_status "Server is ready! Connect to 127.0.0.1:25565"
|
53
|
+
reader = Thread.new do
|
54
|
+
until $stdin.closed?
|
55
|
+
line = $stdin.gets.chomp
|
56
|
+
$server.command line unless line.empty?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
$server.say("McBlocky is ready")
|
60
|
+
$server.on_message '!stop' do
|
61
|
+
log_status "Stopping server..."
|
62
|
+
$server.stop
|
63
|
+
end
|
64
|
+
$server.on_message /^!/ do |message, user|
|
65
|
+
next unless $context
|
66
|
+
command, _, args = message.partition(/\s+/)
|
67
|
+
$context.helpers.each do |aliases, block|
|
68
|
+
aliases = [aliases] if String === aliases
|
69
|
+
aliases.each do |a|
|
70
|
+
if command == "!#{a}"
|
71
|
+
block.call(args, user, a)
|
72
|
+
break
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
listener = Listener.from_config do |context|
|
78
|
+
old_context = $context
|
79
|
+
$context = context
|
80
|
+
$context.server = $server # needed by helpers
|
81
|
+
Executor.to_commands(context, old_context).each{|c| $server.command c}
|
82
|
+
end
|
83
|
+
listener.start
|
84
|
+
$server.loop!
|
85
|
+
rescue SystemExit
|
86
|
+
if $server
|
87
|
+
log_status "Stopping server..."
|
88
|
+
$server.stop
|
89
|
+
end
|
90
|
+
reader.kill if reader
|
91
|
+
rescue Interrupt
|
92
|
+
if $server
|
93
|
+
log_status "Stopping server..."
|
94
|
+
$server.stop
|
95
|
+
end
|
96
|
+
reader.kill if reader
|
97
|
+
rescue Exception
|
98
|
+
log_error "Caught error, stopping server..."
|
99
|
+
begin
|
100
|
+
$server.stop if $server
|
101
|
+
reader.kill if reader
|
102
|
+
rescue
|
103
|
+
end
|
104
|
+
log_error "Error trace:"
|
105
|
+
raise
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module McBlocky
|
4
|
+
class Config
|
5
|
+
class << self
|
6
|
+
attr_reader :config
|
7
|
+
attr_reader :config_path
|
8
|
+
def load(filename)
|
9
|
+
@valid = false
|
10
|
+
filename = File.expand_path('config.yml', filename) if File.directory? filename
|
11
|
+
@config_path = filename
|
12
|
+
open(filename) do |f|
|
13
|
+
@config = YAML.safe_load(f)
|
14
|
+
end
|
15
|
+
validate
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate
|
19
|
+
return if @valid
|
20
|
+
raise ArgumentError, "No config loaded" unless config
|
21
|
+
raise ArgumentError, "No server section" unless config['server']
|
22
|
+
config['code'] ||= {}
|
23
|
+
|
24
|
+
if config['server']['ops']
|
25
|
+
raise ArgumentError, "server.ops must be an array" unless Array === config['server']['ops']
|
26
|
+
end
|
27
|
+
|
28
|
+
config['server']['properties'] = {'enable-command-block' => 'true'}.merge(config['server']['properties'] || {})
|
29
|
+
|
30
|
+
Dir.chdir File.dirname(config_path) do
|
31
|
+
unless which 'java'
|
32
|
+
java = config['server']['java']
|
33
|
+
raise ArgumentError, "Java not found. Specify the full path in server.java" if !java or java.empty?
|
34
|
+
raise ArgumentError, "Java specified in server.java is not executable" unless File.executable? java
|
35
|
+
end
|
36
|
+
|
37
|
+
jar = config['server']['jar']
|
38
|
+
raise ArgumentError, "No server.jar specified" if !jar or jar.empty?
|
39
|
+
raise ArgumentError, "Jar specified in server.jar does not exist" unless File.exist? jar
|
40
|
+
|
41
|
+
config['code']['main'] ||= "#{File.basename File.dirname(config_path)}.rb"
|
42
|
+
main = config['code']['main']
|
43
|
+
raise ArgumentError, "No code.main specified" if !main or main.empty?
|
44
|
+
raise ArgumentError, "#{main} does not exist" unless File.exist? main or File.exist? "#{main}.rb"
|
45
|
+
end
|
46
|
+
|
47
|
+
@valid = true
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
def which(cmd)
|
52
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
53
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
54
|
+
exts.each { |ext|
|
55
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
56
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
57
|
+
}
|
58
|
+
end
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'mcblocky/dsl'
|
2
|
+
|
3
|
+
module McBlocky
|
4
|
+
class Context
|
5
|
+
attr_accessor :server
|
6
|
+
|
7
|
+
def self.run_file(file, dir=nil)
|
8
|
+
dir = File.dirname(file) unless dir
|
9
|
+
Dir.chdir dir do
|
10
|
+
begin
|
11
|
+
ctx = Context.new
|
12
|
+
f = open(file)
|
13
|
+
ctx.instance_eval(f.read, file)
|
14
|
+
return ctx
|
15
|
+
ensure
|
16
|
+
f.close if f
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.run_block(&block)
|
22
|
+
ctx = Context.new
|
23
|
+
ctx.instance_exec &block
|
24
|
+
return ctx
|
25
|
+
end
|
26
|
+
|
27
|
+
def helpers
|
28
|
+
@helpers ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def required_files
|
32
|
+
@required_files ||= Set.new
|
33
|
+
end
|
34
|
+
|
35
|
+
def chains
|
36
|
+
@chains ||= []
|
37
|
+
end
|
38
|
+
|
39
|
+
def blocks
|
40
|
+
@blocks ||= {}
|
41
|
+
end
|
42
|
+
|
43
|
+
def rects
|
44
|
+
@rects ||= {}
|
45
|
+
end
|
46
|
+
|
47
|
+
def areas
|
48
|
+
@areas ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
def context
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def require(file)
|
56
|
+
if file.start_with? './'
|
57
|
+
file = "#{file.sub('./','')}.rb" unless file.end_with? '.rb'
|
58
|
+
required_files << file
|
59
|
+
begin
|
60
|
+
f = open(file)
|
61
|
+
instance_eval(f.read, file)
|
62
|
+
true
|
63
|
+
ensure
|
64
|
+
f.close if f
|
65
|
+
end
|
66
|
+
else
|
67
|
+
Kernel.require(file)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def require_relative(file)
|
72
|
+
path = File.dirname caller[0].split('.rb')[0]
|
73
|
+
file = "#{file}.rb" unless file.end_with? '.rb'
|
74
|
+
file = File.expand_path(file, path)
|
75
|
+
required_files << file
|
76
|
+
begin
|
77
|
+
f = open(file)
|
78
|
+
instance_eval(f.read, file)
|
79
|
+
true
|
80
|
+
ensure
|
81
|
+
f.close if f
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
include McBlocky::DSL
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module McBlocky::DSL
|
2
|
+
class CommandBlock < Commands
|
3
|
+
attr_reader :x, :y, :z, :block_data, :block_kind
|
4
|
+
def initialize(x, y, z, facing, kind, nbt={})
|
5
|
+
super(:at)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@z = z
|
9
|
+
@block_data = facing
|
10
|
+
@block_kind = kind
|
11
|
+
@nbt = nbt
|
12
|
+
end
|
13
|
+
|
14
|
+
def command(*args)
|
15
|
+
raise ArgumentError, "Only one command is allowed per block" unless commands.empty?
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def nbt
|
20
|
+
return @nbt.merge({'Command'=>commands[0] || ''})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module McBlocky::DSL
|
2
|
+
class Commands
|
3
|
+
attr_reader :kind
|
4
|
+
attr_accessor :commands
|
5
|
+
|
6
|
+
def initialize(kind, *args)
|
7
|
+
@kind = kind
|
8
|
+
@args = args
|
9
|
+
@commands = []
|
10
|
+
@a = Selector.new '@a'
|
11
|
+
@p = Selector.new '@p'
|
12
|
+
@r = Selector.new '@r'
|
13
|
+
@e = Selector.new '@e'
|
14
|
+
end
|
15
|
+
|
16
|
+
def command(*args)
|
17
|
+
commands << args.map(&:to_s).join(' ')
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_nbt(obj)
|
21
|
+
McBlocky::DSL.to_nbt(obj)
|
22
|
+
end
|
23
|
+
|
24
|
+
COMMANDS = [:achievement, :ban, :ban_ip, :banlist, :blockdata, :clear, :clone, :debug, :defaultgamemode, :deop, :difficulty, :effect, :enchant, :entitydata, :execute, :fill, :gamemode, :gamerule, :give, :help, :kick, :kill, :list, :me, :op, :pardon, :pardon_ip, :particle, :playsound, :replaceitem, :save_all, :save_off, :save_on, :say, :scoreboard, :seed, :setblock, :setidletimeout, :setworldspawn, :spawnpoint, :spreadplayers, :stats, :stop, :summon, :tell, :tellraw, :testfor, :testforblock, :testforblocks, :time, :title, :toggledownfall, :tp, :trigger, :weather, :whitelist, :worldborder, :xp]
|
25
|
+
|
26
|
+
def blockdata(*args)
|
27
|
+
args[-1] = to_nbt(args[-1]) if Hash === args[-1]
|
28
|
+
command :blockdata, *args
|
29
|
+
end
|
30
|
+
|
31
|
+
def detect(selector, *args, &block)
|
32
|
+
if block
|
33
|
+
chain = Commands.new(:detect)
|
34
|
+
chain.instance_exec &block
|
35
|
+
chain.commands.each do |c|
|
36
|
+
command :execute, selector, '~ ~ ~', :detect, *args, c
|
37
|
+
end
|
38
|
+
else
|
39
|
+
command :execute, selector, '~ ~ ~', :detect, *args
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute(selector, *args, &block)
|
44
|
+
if args.empty?
|
45
|
+
args = ['~ ~ ~']
|
46
|
+
end
|
47
|
+
if block
|
48
|
+
chain = Commands.new(:execute)
|
49
|
+
chain.instance_exec &block
|
50
|
+
chain.commands.each do |c|
|
51
|
+
command :execute, selector, *args, c
|
52
|
+
end
|
53
|
+
else
|
54
|
+
command :execute, selector, *args
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def gamerule(rule=nil, value=nil, &block)
|
59
|
+
if (rule and block) or (rule and value.nil?)
|
60
|
+
raise ArgumentError
|
61
|
+
end
|
62
|
+
unless block
|
63
|
+
command :gamerule, rule, value
|
64
|
+
else
|
65
|
+
o = PartialCommand.new(self, :gamerule)
|
66
|
+
o.instance_exec &block
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def replaceitem(*args)
|
71
|
+
args[-1] = to_nbt(args[-1]) if Hash === args[-1]
|
72
|
+
command :replaceitem, *args
|
73
|
+
end
|
74
|
+
|
75
|
+
def scoreboard(*args, &block)
|
76
|
+
if block
|
77
|
+
d = SimpleDelegator.new(self)
|
78
|
+
d.instance_variable_set :@a, @a
|
79
|
+
d.instance_variable_set :@p, @p
|
80
|
+
d.instance_variable_set :@r, @r
|
81
|
+
d.instance_variable_set :@e, @e
|
82
|
+
d.instance_variable_set :@args, args
|
83
|
+
def d.method_missing(m, *a)
|
84
|
+
super
|
85
|
+
rescue NoMethodError
|
86
|
+
command :scoreboard, *@args, m, *a
|
87
|
+
end
|
88
|
+
d.instance_exec(&block)
|
89
|
+
else
|
90
|
+
command :scoreboard, *args
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def setblock(*args)
|
95
|
+
args[-1] = to_nbt(args[-1]) if Hash === args[-1]
|
96
|
+
command :setblock, *args
|
97
|
+
end
|
98
|
+
|
99
|
+
def tellraw(player, *args)
|
100
|
+
if args.length < 1
|
101
|
+
raise ArgumentError, "No message given in tellraw"
|
102
|
+
end
|
103
|
+
obj = []
|
104
|
+
args.each do |arg|
|
105
|
+
if Array === arg
|
106
|
+
obj += arg
|
107
|
+
else
|
108
|
+
obj << arg
|
109
|
+
end
|
110
|
+
end
|
111
|
+
command :tellraw, player, JSON.dump(obj)
|
112
|
+
end
|
113
|
+
|
114
|
+
def title(selector, subcommand, *args)
|
115
|
+
if args.length < 1
|
116
|
+
raise ArgumentError, "No message given in title"
|
117
|
+
end
|
118
|
+
obj = []
|
119
|
+
args.each do |arg|
|
120
|
+
if Array === arg
|
121
|
+
obj += arg
|
122
|
+
else
|
123
|
+
obj << arg
|
124
|
+
end
|
125
|
+
end
|
126
|
+
command :title, selector, subcommand, JSON.dump(obj)
|
127
|
+
end
|
128
|
+
|
129
|
+
COMMANDS.each do |c|
|
130
|
+
unless method_defined? c
|
131
|
+
define_method c do |*args|
|
132
|
+
command c.to_s.gsub('_', '-'), *args
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class PartialCommand
|
139
|
+
def initialize(context, *args)
|
140
|
+
@context = context
|
141
|
+
@args = args
|
142
|
+
@a = Selector.new '@a'
|
143
|
+
@p = Selector.new '@p'
|
144
|
+
@r = Selector.new '@r'
|
145
|
+
@e = Selector.new '@e'
|
146
|
+
end
|
147
|
+
|
148
|
+
def method_missing(m, *args)
|
149
|
+
@context.command *(@args + [m] + args)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module McBlocky::DSL
|
2
|
+
class Container
|
3
|
+
attr_reader :x, :y, :z, :block_data, :block_kind, :nbt
|
4
|
+
def initialize(x, y, z, kind, data=0, nbt={})
|
5
|
+
@x = x
|
6
|
+
@y = y
|
7
|
+
@z = z
|
8
|
+
@block_kind = kind
|
9
|
+
@block_data = data
|
10
|
+
@nbt = nbt
|
11
|
+
@last_slot = -1
|
12
|
+
end
|
13
|
+
|
14
|
+
def item_in_slot(slot, kind, count=1, damage=0, tag={})
|
15
|
+
nbt['Items'] ||= []
|
16
|
+
nbt['Items'] << {'Slot'=>slot, 'id'=>kind, 'Count'=>count, 'Damage'=>damage, 'tag'=>tag}
|
17
|
+
@last_slot = slot if slot > @last_slot
|
18
|
+
end
|
19
|
+
|
20
|
+
def item(kind, count=1, damage=0, tag={})
|
21
|
+
item_in_slot(@last_slot+1, kind, count, damage, tag)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module McBlocky::DSL
|
2
|
+
class Selector
|
3
|
+
def initialize(name, **kwargs)
|
4
|
+
@name = name
|
5
|
+
if kwargs[:loc]
|
6
|
+
loc = kwargs[:loc]
|
7
|
+
raise ArgumentError, "Relative locations are not allowed in selectors" if loc.is_relative?
|
8
|
+
kwargs[:x] = loc.x
|
9
|
+
kwargs[:y] = loc.y
|
10
|
+
kwargs[:z] = loc.z
|
11
|
+
kwargs.delete :loc
|
12
|
+
end
|
13
|
+
@args = kwargs
|
14
|
+
end
|
15
|
+
|
16
|
+
def [](**args)
|
17
|
+
Selector.new(@name, @args.merge(args))
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
if @args.empty?
|
22
|
+
@name
|
23
|
+
else
|
24
|
+
pairs = @args.map{|k,v| "#{k}=#{v}"}
|
25
|
+
"#{@name}[#{pairs.join(',')}]"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|