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.
@@ -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
@@ -0,0 +1,3 @@
1
+ helper 'bar' do
2
+ server.say 'bar'
3
+ end
@@ -0,0 +1,3 @@
1
+ helper 'foo', 'bar' do |args, user, command|
2
+ server.say "#{command}, #{user}, #{args}"
3
+ end
data/exe/mcblocky ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require "thor"
3
+ require "yaml"
4
+ require "mcblocky"
5
+
6
+ McBlocky::Cli.start(ARGV)
@@ -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,13 @@
1
+ module McBlocky::DSL
2
+ class Block
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
+ end
12
+ end
13
+ 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,9 @@
1
+ module McBlocky::DSL
2
+ class RepeatChain < Commands
3
+ attr_reader :rect
4
+ def initialize(*args)
5
+ super(:repeat)
6
+ @rect = McBlocky::Rect.new(*args)
7
+ end
8
+ end
9
+ 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