Skip to content

Instantly share code, notes, and snippets.

@jb55
Created May 7, 2011 02:56
Show Gist options
  • Select an option

  • Save jb55/960155 to your computer and use it in GitHub Desktop.

Select an option

Save jb55/960155 to your computer and use it in GitHub Desktop.
minecraft stuff
{EventEmitter} = require 'events'
mmap = require 'mmap'
path = require 'path'
fs = require 'fs'
# Used for accessing blocks
class Blocks
@chunks = {}
# Blocks takes a path to a world directory.
constructor: (@path, @log, @options) ->
@options or= {}
@chunkLoader = @options.loader or @loadChunk
# Load all of our chunks by mapping to virtual memory. This implements
# on-demand chunk loading, loading blocks into RAM only when we access them.
load: (cb) ->
chunkFiles @path, (err, files) ->
cb(err) if err
n_chunks = files.length
ev = new EventEmitter
chunks = []
n = 0
# Pass each chunk file into our chunk loader
# This will emit a load event when a chunk finishes loading
@chunkLoader(file, ev, @log) for file in files
# When a chunk has finished loading, add it to our chunk list
ev.on 'load', (err, chunk) ->
n++
return @log.error("Chunk failed to load: #{err.message}") if err
return @log.error("Too many chunk load events emitted") if n > n_chunks
@log.info("Loaded chunk #{chunk.x}, #{chunk.y}")
chunks.push chunk
cb(chunks) if n is n_chunks
# Get a specific chunk from an x, y coordinate
getChunk: (x, y) ->
# Get a specific block from an x, y coordinate. Returns an integer
getBlock: (x, y, z, cb) ->
# Load a chunk in a non-lazy manner
@loadChunkStrict: @loadChunk
# Load a chunk using the default method, mmap/demand paging
@loadChunk: (file, ev) ->
fs.stat file, (err, stats) ->
return ev.emit('load', err) if err
size = stats.size
fs.open file, 'r', (err, fd) ->
return ev.emit('load', err) if err
# Parse the region numbers out of the chunk filename
filename = path.basename file
[_, x, y, _] = filename.split('.')
# mmap our chunk into virtual memory, the node Buffer object uses this
# underlying mapping when reading and writing to it
buffer = mmap.map(size, mmap.PROT_READ, mmap.MAP_PRIVATE, fd, 0)
chunk = { x: x, y: y, file: file, buffer: buffer }
ev.emit('load', err, chunk)
# Returns true if .mcr is in a filename
@isMcrFile: (f) -> path.basename(f).indexOf(".mcr") != -1
# Get a list of chunks from a path
@chunkFiles: (p, cb) ->
region = path.join p 'region'
# If there's a region directory in the current path, use that instead
path.exists region, (regionExists) ->
p = region if regionExists
fs.readdir p, (err, files) ->
cb(err) if err
cb(null, f for f in files when isMcrFile(f) is true)
module.exports = Blocks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment