class McBlocky::Executor

Public Class Methods

to_commands(context, old_context=nil) click to toggle source
# File lib/mcblocky/executor.rb, line 3
def self.to_commands(context, old_context=nil)
  executor = Executor.new(nil, :final)
  executor.do_context(context, old_context)
  executor.commands
end

Public Instance Methods

do_block(block, old_block=nil) click to toggle source
# File lib/mcblocky/executor.rb, line 74
def do_block(block, old_block=nil)
  if old_block and !block
    setblock old_block.x, old_block.y, old_block.z, 'minecraft:air'
    return
  end

  if old_block and old_block.block_kind == block.block_kind and old_block.block_data == block.block_data
    return if old_block.nbt == block.nbt
    blockdata block.x, block.y, block.z, block.nbt unless block.nbt == {}
  else
    setblock block.x, block.y, block.z, block.block_kind, block.block_data, 'replace'
    blockdata block.x, block.y, block.z, block.nbt unless block.nbt == {}
  end
end
do_context(context, old_context) click to toggle source
# File lib/mcblocky/executor.rb, line 9
def do_context(context, old_context)
  # do cleanup first
  if old_context
    old_context.chains.select{|x|x.kind == :cleanup}.each do |c|
      self.commands += c.commands
    end
  end

  # do initial blocks
  old_initials = old_context ? old_context.chains.select{|x|x.kind == :initial} : []
  initials = context.chains.select{|x|x.kind == :initial}
  initials.each_with_index do |chain, i|
    old_chain = old_initials[i]
    if old_chain
      matches = true
      chain.commands.each_with_index do |cmd, j|
        if matches
          old_cmd = old_chain.commands[j]
          next if old_cmd == cmd
          matches = false
          command cmd
        else
          command cmd
        end
      end
    else
      chain.commands.each {|cmd| command cmd}
    end
  end

  rects = (old_context ? old_context.rects.keys : []) + context.rects.keys
  rects.uniq.each do |rect|
    old_block = old_context ? old_context.rects[rect] : nil
    block = context.rects[rect]
    if old_block and !block
      fill rect.x1, rect.y1, rect.z1, rect.x2, rect.y2, rect.z2, 'minecraft:air'
    elsif old_block and old_block != block
      fill rect.x1, rect.y1, rect.z1, rect.x2, rect.y2, rect.z2, block.block_kind, block.block_data, 'replace', old_block.block_kind, old_block.block_data
    else
      fill rect.x1, rect.y1, rect.z1, rect.x2, rect.y2, rect.z2, block.block_kind, block.block_data
    end
  end

  context.chains.select{|x|[:repeat, :impulse_chain].include? x.kind}.each do |c|
    case c.kind
    when :repeat
      do_repeat context, c
    when :impulse_chain
      do_impulse context, c
    end
  end

  locations = (old_context ? old_context.blocks.keys : []) + context.blocks.keys
  locations.uniq.each do |loc|
    old = old_context ? old_context.blocks[loc] : nil
    new = context.blocks[loc]
    do_block(new, old)
  end

  # after blocks are set
  context.chains.select{|x|x.kind == :after}.each do |c|
    self.commands += c.commands
  end
end
do_impulse(context, chain) click to toggle source
# File lib/mcblocky/executor.rb, line 126
def do_impulse(context, chain)
  if chain.rect != @last_area
    @last_area = chain.rect
    @offset = 0
  end
  sequence = fill_space(chain.rect)
  chain.commands << "blockdata #{sequence[@offset].x} #{sequence[@offset].y} #{sequence[@offset].z} {auto:0}"
  if chain.commands.length + @offset > sequence.length
    raise ArgumentError, "Chain is too long for the provided space"
  end
  kind = 'minecraft:command_block'
  nbt = {auto: 0}
  chain.commands.each_with_index do |c,i|
    cursor = sequence[i+@offset]
    next_cursor = if i+1 < sequence.length
                    sequence[i+1]
                  else
                    Location.new(cursor.x, cursor.y+1, cursor.z)
                  end
    facing = if next_cursor.x - cursor.x == 1
               DSL::Facing::EAST
             elsif next_cursor.x - cursor.x == -1
               DSL::Facing::WEST
             elsif next_cursor.y - cursor.y == 1
               DSL::Facing::UP
             elsif next_cursor.y - cursor.y == -1
               DSL::Facing::DOWN
             elsif next_cursor.z - cursor.z == 1
               DSL::Facing::SOUTH
             elsif next_cursor.z - cursor.z == -1
               DSL::Facing::NORTH
             end
    context.blocks[cursor] = DSL::CommandBlock.new(context, cursor.x, cursor.y, cursor.z, facing, kind, nbt)
    context.blocks[cursor].command c
    kind = 'minecraft:chain_command_block'
    nbt = {auto: 1}
  end
  @offset += self.next_power_of_two(chain.commands.length)
end
do_repeat(context, chain) click to toggle source
# File lib/mcblocky/executor.rb, line 89
def do_repeat(context, chain)
  if chain.rect != @last_area
    @last_area = chain.rect
    @offset = 0
  end
  sequence = fill_space(chain.rect)
  if chain.commands.length + @offset > sequence.length
    raise ArgumentError, "Chain is too long for the provided space"
  end
  kind = 'minecraft:repeating_command_block'
  chain.commands.each_with_index do |c,i|
    cursor = sequence[i+@offset]
    next_cursor = if i+1 < sequence.length
                    sequence[i+1]
                  else
                    Location.new(cursor.x, cursor.y+1, cursor.z)
                  end
    facing = if next_cursor.x - cursor.x == 1
               DSL::Facing::EAST
             elsif next_cursor.x - cursor.x == -1
               DSL::Facing::WEST
             elsif next_cursor.y - cursor.y == 1
               DSL::Facing::UP
             elsif next_cursor.y - cursor.y == -1
               DSL::Facing::DOWN
             elsif next_cursor.z - cursor.z == 1
               DSL::Facing::SOUTH
             elsif next_cursor.z - cursor.z == -1
               DSL::Facing::NORTH
             end
    context.blocks[cursor] = DSL::CommandBlock.new(context, cursor.x, cursor.y, cursor.z, facing, kind, {'auto'=>1})
    context.blocks[cursor].command c
    kind = 'minecraft:chain_command_block'
  end
  @offset += self.next_power_of_two(chain.commands.length)
end
fill_space(rect) click to toggle source
# File lib/mcblocky/executor.rb, line 166
def fill_space(rect)
  path = []
  zrange = rect.z1..rect.z2
  zrange.each do |z|
    rz = z - rect.z1
    yrange = rect.y1..rect.y2
    yrange = yrange.to_a.reverse if rz % 2 != 0
    yrange.each do |y|
      ry = y - rect.y1
      xrange = rect.x1..rect.x2
      xrange = xrange.to_a.reverse if (ry+rz) % 2 != 0
      xrange.each do |x|
        path << Location.new(x, y, z)
      end
    end
  end
  path
end

Protected Instance Methods

next_power_of_two(n) click to toggle source
# File lib/mcblocky/executor.rb, line 186
def next_power_of_two(n)
  l = Math.log(n, 2)
  # ceiling
  l = l.to_i + 1 if l != l.to_i
  return (2**l).to_i
end