rubycraft 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,83 @@
1
+ RubyCraft
2
+ ==============
3
+
4
+ RubyCraft is a simple library for manipulating [Minecraft](http://www.minecraft.net/)
5
+ region files.
6
+
7
+ Region files are files with the mcr extension on the region folder of a save folder. The
8
+ save folders are located below the saves folder of the minecraft data folder (for
9
+ instance, on Linux it is ~/.minecraft/saves/$SAVENAME/region, and on mac it is
10
+ ~/Library/Application Support/minecraft/$SAVENAME/region). More about save folders
11
+ [here](http://www.minecraftwiki.net/wiki/Tutorials/Minecraft_Help_FAQ#Common_fixes).
12
+
13
+
14
+ Quick Example
15
+ --------
16
+ filename = " ~/.minecraft/saves/$SAVENAME/region/r.0.0.mcr"
17
+ r = Region.fromFile(filename)
18
+ r.chunk(0, 0).block_map { :gold }
19
+ r.exportToFile filename
20
+
21
+ For more examples, check the
22
+ [examples](https://github.com/danielribeiro/RubyCraft/tree/master/examples) folder.
23
+
24
+ Regions
25
+ ---------
26
+ When getting many chunks from Region#chunk method, don't forget to invoke Region#unloadChunk(z,
27
+ x). This way the chunk will not occupy memory.
28
+
29
+ Alternatively you can use the Region#cube method. Example
30
+
31
+ r = Region.fromFile(filename)
32
+ c = r.cube(0, 0, 0, :width => 50, :length => 50, :height => 128)
33
+ c.each do |block, z, x, y|
34
+ block.name = :wool
35
+ block.color = :orange
36
+ end
37
+
38
+ It receives the z, x, y of the origin point of the cube, and its respective width, length
39
+ and height. The chunk load/unload is abstracted way on this interface. The cube can
40
+ receive a block, or it will return an Enumerable that iterates over the blocks of the
41
+ cube. The proc receives four arguments: the block, and its relative coordinates to the
42
+ cube's origin point.
43
+
44
+ Chunks
45
+ ---------
46
+ Chunks are both enumerable and indexable:
47
+
48
+ chunk[0, 0, 0].name = :gold
49
+ chunk.each { |block| block.name = :gold }
50
+
51
+
52
+ Note that chunks have size 16x16x128 (width, length, height). Usually you don't create
53
+ chunks directly, but get them through Region#chunk method.
54
+
55
+ Blocks
56
+ ---------
57
+ Blocks have 3 attributes: block_type, pos and data. [Block type](https://github.com/danielribeiro/RubyCraft/blob/master/lib/block_type.rb) tells the name, id and
58
+ transparency (boolean) of the block. The pos attribute indicates the position of the block
59
+ inside its chunk, and data is the integer [data
60
+ value](http://www.minecraftwiki.net/wiki/Data_values).
61
+
62
+ Id is not usually accessed directly, as the name attribute provides a more friendly
63
+ interface. For wool blocks, changing the color directly is also possible in a more
64
+ friendly way.
65
+
66
+ block.name = :wool
67
+ block.color = :purple
68
+ p block.color
69
+
70
+
71
+ Dependencies:
72
+ ---------
73
+ [Nbtfile](http://github.com/mental/nbtfile)
74
+
75
+ Meta
76
+ ----
77
+
78
+ Created by Daniel Ribeiro
79
+
80
+ Released under the MIT License: http://www.opensource.org/licenses/mit-license.php
81
+
82
+ http://github.com/danielribeiro/RubyCraft
83
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/rdoctask'
6
+ require 'rake/testtask'
7
+ require 'spec/rake/spectask'
8
+
9
+ begin
10
+ require 'jeweler'
11
+ Jeweler::Tasks.new do |gem|
12
+ gem.name = "rubycraft"
13
+ gem.summary = %Q{Lib for manipualting Minecraft world files}
14
+ gem.description = %Q{It allows you to change all the
15
+ blocks in region files in whatever way you see fit. Example: http://bit.ly/r62qGo}
16
+ gem.email = "danrbr@gmail.com"
17
+ gem.homepage = "http://github.com/danielribeiro/RubyCraft"
18
+ gem.authors = ["Daniel Ribeiro"]
19
+ gem.add_dependency 'nbtfile', '>=0.2.0'
20
+ gem.files = FileList["[A-Z]*", "{bin,lib}/**/*"]
21
+ # gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
22
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
23
+ end
24
+ Jeweler::GemcutterTasks.new
25
+ rescue LoadError
26
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
27
+ end
28
+
29
+
30
+ Rake::RDocTask.new do |rdoc|
31
+ files =['README', 'LICENSE', 'lib/**/*.rb']
32
+ rdoc.rdoc_files.add(files)
33
+ rdoc.main = "README" # page to start on
34
+ rdoc.title = "RubyCraft Docs"
35
+ rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
36
+ rdoc.options << '--line-numbers'
37
+ end
38
+
39
+ Spec::Rake::SpecTask.new do |t|
40
+ t.spec_files = FileList['spec/**/*spec.rb']
41
+ t.libs << Dir["lib"]
42
+ end
43
+
44
+ desc "Acceptance test"
45
+ task :atest do
46
+ require 'spec/acceptanceEdit'
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/block.rb ADDED
@@ -0,0 +1,77 @@
1
+ require 'block_type'
2
+
3
+ # A minecraft block. Its position is given by a coord[x, z, y]
4
+ class Block
5
+
6
+ attr_accessor :block_type, :pos, :data
7
+ def initialize(blockType, data = 0)
8
+ @blockType = blockType
9
+ @data = 0
10
+ end
11
+
12
+ def self.get(key)
13
+ new BlockType.get key
14
+ end
15
+
16
+ def self.of(key)
17
+ self[key]
18
+ end
19
+
20
+ def self.[](key)
21
+ new BlockType[key]
22
+ end
23
+
24
+
25
+ def color=(color)
26
+ @data = BlockColor::InvertedColor[color]
27
+ end
28
+
29
+ def color
30
+ BlockColor.typeColor[@data].name
31
+ end
32
+
33
+ def blockColor
34
+ BlockColor.typeColor[@data]
35
+ end
36
+
37
+ def is(name)
38
+ self.name == name.to_s
39
+ end
40
+
41
+ def name
42
+ @blockType.name
43
+ end
44
+
45
+ def id
46
+ @blockType.id
47
+ end
48
+
49
+ def transparent
50
+ @blockType.transparent
51
+ end
52
+
53
+ #sets block type by name
54
+ def name=(newName)
55
+ return if name == newName.to_s
56
+ @blockType = BlockType[newName]
57
+ end
58
+
59
+ #sets block type by id
60
+ def id=(id)
61
+ return if id == id
62
+ @blockType = BlockType.get id
63
+ end
64
+
65
+ def y
66
+ pos[2]
67
+ end
68
+
69
+ def z
70
+ pos[1]
71
+ end
72
+
73
+ def x
74
+ pos[0]
75
+ end
76
+ end
77
+
data/lib/block_type.rb ADDED
@@ -0,0 +1,191 @@
1
+ BlockTypeDSL = proc do
2
+ transparent_block 0, :air
3
+ block 1, :stone
4
+ block 2, :grass
5
+ block 3, :dirt
6
+ block 4, :cobblestone
7
+ block 5, :planks
8
+ transparent_block 6, :sapling
9
+ block 7, :bedrock
10
+ block 8, :watersource
11
+ block 9, :water
12
+ block 10, :lavasource
13
+ block 11, :lava
14
+ block 12, :sand
15
+ block 13, :gravel
16
+ block 14, :goldore
17
+ block 15, :ironore
18
+ block 16, :coal
19
+ block 17, :log
20
+ block 18, :leaves
21
+ block 19, :sponge
22
+ transparent_block 20, :glass
23
+ block 21, :lapisore
24
+ block 22, :lapis
25
+ block 23, :dispenser
26
+ block 24, :sandstone
27
+ block 25, :note
28
+ block 26, :bed
29
+ transparent_block 27, :powered_rail
30
+ transparent_block 28, :detector_rail
31
+ block 29, :sticky_piston
32
+ transparent_block 30, :cobweb
33
+ transparent_block 31, :tall_grass
34
+ transparent_block 32, :dead_shrubs
35
+ block 33, :piston
36
+ block 34, :piston_extension
37
+ block 35, :wool
38
+ transparent_block 37, :dandelion
39
+ transparent_block 38, :rose
40
+ transparent_block 39, :brown_mushroom
41
+ transparent_block 40, :red_mushroom
42
+ block 41, :gold
43
+ block 42, :iron
44
+ block 43, :slabs
45
+ block 44, :slab
46
+ block 45, :brick
47
+ block 46, :tnt
48
+ block 47, :bookshelf
49
+ block 48, :mossy
50
+ block 49, :obsidian
51
+ transparent_block 50, :torch
52
+ transparent_block 51, :fire
53
+ block 52, :spawner
54
+ block 53, :stairs
55
+ block 54, :chest
56
+ transparent_block 55, :redstone_wire
57
+ block 56, :diamond_ore
58
+ block 57, :diamond_block
59
+ block 58, :crafting_table
60
+ block 59, :seeds
61
+ block 60, :farmland
62
+ block 61, :furnace
63
+ block 62, :burning_furnace
64
+ transparent_block 63, :signpost
65
+ transparent_block 64, :door
66
+ transparent_block 65, :ladder
67
+ transparent_block 66, :rails
68
+ block 67, :cobblestone_stairs
69
+ transparent_block 68, :wall_sign
70
+ transparent_block 69, :lever
71
+ transparent_block 70, :stone_pressure_plate
72
+ transparent_block 71, :iron_door
73
+ transparent_block 72, :wooden_pressure_plate
74
+ block 73, :redstone_ore
75
+ block 74, :glowing_redstone_ore
76
+ transparent_block 75, :redstone_torch_off
77
+ transparent_block 76, :redstone_torch_on
78
+ transparent_block 77, :stone_button
79
+ block 78, :snow
80
+ block 79, :ice
81
+ block 80, :snow_block
82
+ transparent_block 81, :cactus
83
+ block 82, :clay
84
+ block 83, :sugar_cane
85
+ block 84, :jukebox
86
+ transparent_block 85, :fence
87
+ block 86, :pumpkin
88
+ block 87, :netherrack
89
+ block 88, :soulsand
90
+ block 89, :glowstone
91
+ transparent_block 90, :portal
92
+ block 91, :jock_o_lantern
93
+ transparent_block 92, :cake
94
+ transparent_block 93, :repeater_off
95
+ transparent_block 94, :repeater_on
96
+ block 95, :locked_chest
97
+ transparent_block 96, :trapdoor
98
+ end
99
+
100
+
101
+ # DSL: color name r, g, b
102
+ BlockColorDSL = proc do
103
+ white 221, 221, 221
104
+ orange 233, 126, 55
105
+ magenta 179, 75, 200
106
+ light_blue 103, 137, 211
107
+ yellow 192, 179, 28
108
+ light_green 59, 187, 47
109
+ pink 217, 132, 153
110
+ dark_gray 66, 67, 67
111
+ gray 157, 164, 165
112
+ cyan 39, 116, 148
113
+ purple 128, 53, 195
114
+ blue 39, 51, 153
115
+ brown 85, 51, 27
116
+ dark_green 55, 76, 24
117
+ red 162, 44, 42
118
+ black 26, 23, 23
119
+ end
120
+
121
+ class BlockColor
122
+ @typeColor = []
123
+
124
+ def self.method_missing(name, *args)
125
+ args << @typeColor.size
126
+ @typeColor << new(name, *args)
127
+ end
128
+
129
+ def self.typeColor
130
+ @typeColor
131
+ end
132
+
133
+ attr_reader :name, :r, :g, :b, :data
134
+ def initialize(name, r, g, b, data)
135
+ @name = name
136
+ @r = r
137
+ @g = g
138
+ @b = b
139
+ @data = data
140
+ end
141
+
142
+ def rgb
143
+ [r, g, b]
144
+ end
145
+
146
+ class_eval &BlockColorDSL
147
+ InvertedColor = Hash[typeColor.each_with_index.map { |obj, i| [obj.name, i] }]
148
+ end
149
+
150
+ # class methods and dsl for block
151
+ class BlockType
152
+ @blocks = {}
153
+ @blocks_by_name = {}
154
+ attr_reader :id, :name, :transparent
155
+
156
+ def initialize(id, name, transparent)
157
+ @id = id
158
+ @name = name.to_s
159
+ @transparent = transparent
160
+ end
161
+
162
+ def self.block(id, name, transparent = false)
163
+ block = new id, name, transparent
164
+ @blocks[id] = block
165
+ @blocks_by_name[name.to_s] = block
166
+
167
+ end
168
+
169
+ def self.transparent_block(id, name)
170
+ block id, name, true
171
+ end
172
+
173
+ def self.get(key)
174
+ if @blocks.has_key?(key)
175
+ return @blocks[key].clone
176
+ end
177
+ new(key, "unknown(#{key})", false)
178
+ end
179
+
180
+ def self.of(key)
181
+ self[key]
182
+ end
183
+
184
+ def self.[](key)
185
+ key = key.to_s
186
+ return @blocks_by_name[key] if @blocks_by_name.has_key?(key)
187
+ raise "no such name: #{key}"
188
+ end
189
+
190
+ class_eval &BlockTypeDSL
191
+ end
@@ -0,0 +1,46 @@
1
+ require 'stringio'
2
+
3
+ # Utils for manipulating bytes back and forth as strings, strings and numbers
4
+ module ByteConverter
5
+ def toByteString(array)
6
+ array.pack('C*')
7
+ end
8
+
9
+ def intBytes(i)
10
+ [i >> 24, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF]
11
+ end
12
+
13
+ def stringToByteArray(str)
14
+ str.bytes.to_a
15
+ end
16
+
17
+ def arrayToIO(arr)
18
+ io = StringIO.new
19
+ io.write toByteString arr
20
+ io.rewind
21
+ io
22
+ end
23
+
24
+ def stringToIo(str)
25
+ arrayToIO stringToByteArray(str)
26
+ end
27
+
28
+ def concat(array, enum)
29
+ for i in enum
30
+ array << i
31
+ end
32
+ end
33
+
34
+ def bytesToInt(array)
35
+ array.pack('C*').unpack("N").first
36
+ end
37
+
38
+ def pad(array, count, value = 0)
39
+ count.times do
40
+ array << value
41
+ end
42
+ array
43
+ end
44
+
45
+ extend self
46
+ end
data/lib/chunk.rb ADDED
@@ -0,0 +1,118 @@
1
+ # Represents a chunk data
2
+ require 'nbt_helper'
3
+ require 'byte_converter'
4
+ require 'block'
5
+ require 'matrix3d'
6
+
7
+ # Chunks are enumerable over blocks
8
+ class Chunk
9
+ include Enumerable
10
+ include ZlibHelper
11
+
12
+ Width = 16
13
+ Length = 16
14
+ Height = 128
15
+
16
+ def self.fromNbt(bytes)
17
+ new NbtHelper.fromNbt bytes
18
+ end
19
+
20
+ def initialize(nbtData)
21
+ name, @nbtBody = nbtData
22
+ bytes = level["Blocks"].value.bytes
23
+ @blocks = matrixfromBytes bytes
24
+ @blocks.each_triple_index do |b, z, x, y|
25
+ b.pos = [z, x, y]
26
+ end
27
+ data = level["Data"].value.bytes.to_a
28
+ @blocks.each_with_index do |b, index|
29
+ v = data[index / 2]
30
+ if index % 2 == 0
31
+ b.data = v & 0xF
32
+ else
33
+ b.data = v >> 4
34
+ end
35
+ end
36
+ end
37
+
38
+ # Iterates over the blocks
39
+ def each(&block)
40
+ @blocks.each &block
41
+ end
42
+
43
+
44
+ # Converts all blocks on data do another type. Gives the block and sets
45
+ # the received name
46
+ def block_map(&block)
47
+ each { |b| b.name = yield b }
48
+ end
49
+
50
+ # Converts all blocks on data do another type. Gives the block name sets
51
+ # the received name
52
+ def block_type_map(&block)
53
+ each { |b| b.name = yield b.name.to_sym }
54
+ end
55
+
56
+ def [](z, x, y)
57
+ @blocks[z, x, y]
58
+ end
59
+
60
+ def []=(z, x, y, value)
61
+ @blocks[z, x, y] = value
62
+ end
63
+
64
+ def export
65
+ level["Data"] = byteArray exportLevelData
66
+ level["Blocks"] = byteArray @blocks.map { |b| b.id }
67
+ level["HeightMap"] = byteArray exportHeightMap
68
+ ["", @nbtBody]
69
+ end
70
+
71
+ def toNbt
72
+ NbtHelper.toBytes export
73
+ end
74
+
75
+ protected
76
+ def exportHeightMap
77
+ zwidth, xwidth, ywidth = @blocks.bounds
78
+ matrix = Array.new(zwidth) { Array.new(xwidth) { 1 }}
79
+ @blocks.each_triple_index do |b, z, x, y|
80
+ unless b.transparent
81
+ matrix[z][x] = [matrix[z][x], y + 1].max
82
+ end
83
+ end
84
+ ret = []
85
+ matrix.each do |line|
86
+ line.each do |height|
87
+ ret << height
88
+ end
89
+ end
90
+ ret
91
+ end
92
+
93
+ def level
94
+ @nbtBody["Level"]
95
+ end
96
+
97
+ def exportLevelData
98
+ data = []
99
+ @blocks.each_with_index do |b, i|
100
+ if i % 2 == 0
101
+ data << b.data
102
+ else
103
+ data[i / 2] += (b.data << 4)
104
+ end
105
+ end
106
+ data
107
+ end
108
+
109
+ def byteArray(data)
110
+ NBTFile::Types::ByteArray.new ByteConverter.toByteString(data)
111
+ end
112
+
113
+ def matrixfromBytes(bytes)
114
+ Matrix3d.new(Width, Length, Height).fromArray bytes.map {|byte| Block.get(byte) }
115
+ end
116
+
117
+
118
+ end
data/lib/matrix3d.rb ADDED
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env ruby
2
+ class IndexOutOfBoundsError < StandardError
3
+
4
+ end
5
+
6
+ class Matrix3d
7
+ include Enumerable
8
+
9
+ def bounds
10
+ [@xlimit, @ylimit, @zlimit]
11
+ end
12
+
13
+ def initialize(d1,d2,d3)
14
+ @xlimit = d1
15
+ @ylimit = d2
16
+ @zlimit = d3
17
+ @data = Array.new(d1) { Array.new(d2) { Array.new(d3) } }
18
+ end
19
+
20
+ def [](x, y, z)
21
+ @data[x][y][z]
22
+ end
23
+
24
+ def []=(x, y, z, value)
25
+ @data[x][y][z] = value
26
+ end
27
+
28
+ def put(index, value)
29
+ ar = indexToArray(index)
30
+ self[*ar] = value
31
+ end
32
+
33
+ def get(index)
34
+ ar = indexToArray(index)
35
+ self[*ar]
36
+ end
37
+
38
+ def each(&block)
39
+ for z in @data
40
+ for y in z
41
+ for x in y
42
+ yield x
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def each_triple_index(&block)
49
+ return enum_for:each_triple_index unless block_given?
50
+ @data.each_with_index do |plane, x|
51
+ plane.each_with_index do |column, y|
52
+ column.each_with_index do |value, z|
53
+ yield value, x ,y ,z
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ #Actually from any iterable
60
+ def fromArray(ar)
61
+ ar.each_with_index { |obj,i| put i, obj }
62
+ return self
63
+ end
64
+
65
+
66
+ def to_a(default = nil)
67
+ map do |x|
68
+ if x.nil?
69
+ default
70
+ else
71
+ x
72
+ end
73
+ end
74
+ end
75
+
76
+ protected
77
+ def indexToArray(index)
78
+ x = index / (@zlimit * @ylimit)
79
+ index -= x * (@zlimit * @ylimit)
80
+ y = index / @zlimit
81
+ z = index % @zlimit
82
+ return x, y, z
83
+ end
84
+
85
+
86
+ end
data/lib/nbt_helper.rb ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ require 'nbtfile'
3
+ require 'zlib'
4
+ require 'byte_converter'
5
+ # Patching nbtfile clases so that they don't gzip/ungzip incorrectly the zlib bytes from
6
+ #mcr files. Use the methods from ZlibHelper
7
+ class NBTFile::Private::Tokenizer
8
+ def initialize(io)
9
+ @gz = io
10
+ @state = NBTFile::Private::TopTokenizerState.new
11
+ end
12
+ end
13
+
14
+ class NBTFile::Emitter
15
+ def initialize(stream)
16
+ @gz = stream
17
+ @state = NBTFile::Private::TopEmitterState.new
18
+ end
19
+ end
20
+
21
+ module ZlibHelper
22
+ def compress(str)
23
+ Zlib::Deflate.deflate(str)
24
+ end
25
+
26
+ def decompress(str)
27
+ Zlib::Inflate.inflate(str)
28
+ end
29
+ extend self
30
+ end
31
+
32
+ # Handles converting bytes to/from nbt regions, which are compressesed/decompress
33
+ module NbtHelper
34
+ extend ByteConverter
35
+ extend ZlibHelper
36
+
37
+ module_function
38
+ def fromNbt(bytes)
39
+ NBTFile.read stringToIo decompress toByteString bytes
40
+ end
41
+
42
+ def toBytes(nbt)
43
+ output = StringIO.new
44
+ name, body = nbt
45
+ NBTFile.write(output, name, body)
46
+ stringToByteArray compress output.string
47
+ end
48
+ end
data/lib/region.rb ADDED
@@ -0,0 +1,257 @@
1
+ require 'chunk'
2
+
3
+ class LazyChunkDelegate
4
+ include ByteConverter
5
+ include ZlibHelper
6
+
7
+ def initialize(bytes)
8
+ @bytes = bytes
9
+ @chunk = nil
10
+ end
11
+
12
+ def each(&block)
13
+ _getchunk.each &block
14
+ end
15
+
16
+ def block_map(&block)
17
+ _getchunk.block_map &block
18
+ end
19
+ def block_type_map(&block)
20
+ _getchunk.block_type_map &block
21
+ end
22
+
23
+ def [](z, x, y)
24
+ _getchunk[z, x, y]
25
+ end
26
+
27
+ def []=(z, x, y, value)
28
+ _getchunk[z, x, y] = value
29
+ end
30
+
31
+ def export
32
+ _getchunk.export
33
+ end
34
+
35
+
36
+ def toNbt
37
+ return @bytes if @chunk.nil?
38
+ @chunk.toNbt
39
+ end
40
+
41
+
42
+ # unloacs the loaded chunk. Needed for memory optmization
43
+ def _unload
44
+ return if @chunk.nil?
45
+ @bytes = @chunk.toNbt
46
+ @chunk = nil
47
+ end
48
+
49
+ protected
50
+ def _getchunk
51
+ if @chunk.nil?
52
+ @chunk = Chunk.fromNbt @bytes
53
+ end
54
+ @chunk
55
+ end
56
+
57
+ end
58
+
59
+ # Enumerable over chunks
60
+ class Region
61
+ include Enumerable
62
+ include ByteConverter
63
+ include ZlibHelper
64
+
65
+ class RegionWritter
66
+ def initialize(io)
67
+ @io = io
68
+ end
69
+
70
+ def pad(count, value = 0)
71
+ self << Array.new(count) { value }
72
+ end
73
+
74
+ def <<(o)
75
+ input = o.kind_of?(Array) ? o : [o]
76
+ @io << ByteConverter.toByteString(input)
77
+ end
78
+
79
+ def close
80
+ @io.close
81
+ end
82
+ end
83
+
84
+ def self.fromFile(filename)
85
+ new ByteConverter.stringToByteArray IO.read filename
86
+ end
87
+
88
+ def initialize(bytes)
89
+ raise "Must be an io" if bytes.kind_of?(String)
90
+ @bytes = bytes
91
+ @chunks = Array.new(32) { Array.new(32) }
92
+ readChunks bytes
93
+ end
94
+
95
+ def chunk(z, x)
96
+ @chunks[z][x]
97
+ end
98
+
99
+ def unloadChunk(z, x)
100
+ @chunks[z][x]._unload
101
+ end
102
+
103
+ def each(&block)
104
+ @chunks.each do |line|
105
+ line.each do |chunk|
106
+ yield chunk
107
+ end
108
+ end
109
+ end
110
+
111
+ def cube(z, y, x, opts = {}, &block)
112
+ c = ChunkCube.new(self, [z, y, x], opts[:width], opts[:length], opts[:height])
113
+ return c unless block_given?
114
+ c.each &block
115
+ end
116
+
117
+ def exportTo(io)
118
+ output = RegionWritter.new io
119
+ chunks = getChunks
120
+ writeChunkOffsets output, chunks
121
+ output.pad blockSize, dummytimestamp
122
+ writeChunks output, chunks
123
+ output.close
124
+ end
125
+
126
+ def exportToFile(filename)
127
+ File.open(filename, "wb") { |f| exportTo f }
128
+ end
129
+
130
+
131
+ protected
132
+ def readChunks(bytes)
133
+ bytes[0..(blockSize - 1)].each_slice(4).each_with_index do |ar, i|
134
+ offset = bytesToInt [0] + ar[0..-2]
135
+ count = ar.last
136
+ if count > 0
137
+ @chunks[i / 32][i % 32 ] = readChunk(offset, bytes)
138
+ end
139
+ end
140
+ end
141
+
142
+ def readChunk(offset, bytes)
143
+ o = offset * blockSize
144
+ bytecount = bytesToInt bytes[o..(o + 4)]
145
+ o += 5
146
+ nbtBytes = bytes[o..(o + bytecount - 2)]
147
+ LazyChunkDelegate.new nbtBytes
148
+ end
149
+
150
+ def chunkSize(chunk)
151
+ chunk.size + chunkMetaDataSize
152
+ end
153
+
154
+ def chunkBlocks(chunk)
155
+ ((chunkSize chunk).to_f / blockSize).ceil
156
+ end
157
+
158
+ def writeChunks(output, chunks)
159
+ for chunk in chunks
160
+ next if chunk.nil?
161
+ output << intBytes(chunk.size + 1)
162
+ output << defaultCompressionType
163
+ output << chunk
164
+ remaining = blockSize - chunkSize(chunk)
165
+ output.pad remaining % blockSize
166
+ end
167
+ end
168
+
169
+ def writeChunkOffsets(output, chunks)
170
+ lastVacantPosition = 2
171
+ for chunk in chunks
172
+ if chunk
173
+ sizeCount = chunkBlocks chunk
174
+ output << intBytes(lastVacantPosition)[1..3]
175
+ output << sizeCount
176
+ lastVacantPosition += sizeCount
177
+ else
178
+ output.pad 4
179
+ end
180
+ end
181
+ end
182
+
183
+ def getChunks
184
+ map do |chunk|
185
+ if chunk.nil?
186
+ nil
187
+ else
188
+ chunk.toNbt
189
+ end
190
+ end
191
+ end
192
+
193
+ def chunkMetaDataSize
194
+ 5
195
+ end
196
+
197
+ def defaultCompressionType
198
+ 2
199
+ end
200
+
201
+ def dummytimestamp
202
+ 0
203
+ end
204
+
205
+ def blockSize
206
+ 4096
207
+ end
208
+
209
+ end
210
+
211
+
212
+ class ChunkCube
213
+ include Enumerable
214
+
215
+ # width corresponds do z, length to x, and height to y.
216
+ def initialize(region, initialPos, width, length, height)
217
+ @region = region
218
+ @initialPos = initialPos
219
+ @width = width || 1
220
+ @length = length || 1
221
+ @height = height || 1
222
+ end
223
+
224
+ def each(&block)
225
+ z, x, y = @initialPos
226
+ firstChunkX = x / chunkSide
227
+ firstChunkZ = z / chunkSide
228
+ lastChunkX = (x + @length - 1) / chunkSide
229
+ lastChunkZ = (z + @width - 1) / chunkSide
230
+ for j in firstChunkZ..lastChunkZ
231
+ for i in firstChunkX..lastChunkX
232
+ iterateOverChunk j, i, &block
233
+ end
234
+ end
235
+ end
236
+
237
+ protected
238
+ def iterateOverChunk(j, i, &block)
239
+ chunk = @region.chunk(j, i)
240
+ return if chunk.nil?
241
+ z, x, y = @initialPos
242
+ chunk.each do |b|
243
+ globalZ = b.z + (j * chunkSide)
244
+ globalX = b.x + (i * chunkSide)
245
+ if globalZ.between?(z, z + @width - 1) and
246
+ globalX.between?(x, x + @length - 1) and
247
+ b.y.between?(y, y + @height - 1)
248
+ yield b, globalZ - z, globalX - x , b.y - y
249
+ end
250
+ end
251
+ @region.unloadChunk(j, i)
252
+ end
253
+
254
+ def chunkSide
255
+ 16
256
+ end
257
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubycraft
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Daniel Ribeiro
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-03 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: nbtfile
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 0
32
+ - 2
33
+ - 0
34
+ version: 0.2.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ description: |-
38
+ It allows you to change all the
39
+ blocks in region files in whatever way you see fit. Example: http://bit.ly/r62qGo
40
+ email: danrbr@gmail.com
41
+ executables: []
42
+
43
+ extensions: []
44
+
45
+ extra_rdoc_files:
46
+ - README.md
47
+ files:
48
+ - README.md
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/block.rb
52
+ - lib/block_type.rb
53
+ - lib/byte_converter.rb
54
+ - lib/chunk.rb
55
+ - lib/matrix3d.rb
56
+ - lib/nbt_helper.rb
57
+ - lib/region.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/danielribeiro/RubyCraft
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options: []
64
+
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ none: false
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ hash: 3
73
+ segments:
74
+ - 0
75
+ version: "0"
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ hash: 3
82
+ segments:
83
+ - 0
84
+ version: "0"
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.6.2
89
+ signing_key:
90
+ specification_version: 3
91
+ summary: Lib for manipualting Minecraft world files
92
+ test_files: []
93
+