mcblocky 0.1.0.pre.alpha.pre.6

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.
@@ -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