mirror of
https://github.com/danbulant/flying-squid
synced 2026-07-07 12:10:46 +00:00
Initial commit
This commit is contained in:
commit
817912180b
5 changed files with 457 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules/
|
||||||
6
README.md
Normal file
6
README.md
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
mc-plain-server
|
||||||
|
==============
|
||||||
|
|
||||||
|
This is mc-plain-server.
|
||||||
|
|
||||||
|
|
||||||
110
mc-plain-server.js
Normal file
110
mc-plain-server.js
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
var mc = require('minecraft-protocol');
|
||||||
|
var states = mc.protocol.states;
|
||||||
|
var World = require('./world');
|
||||||
|
|
||||||
|
var options = {
|
||||||
|
motd: 'Vox Industries',
|
||||||
|
'max-players': 127,
|
||||||
|
port: 25565,
|
||||||
|
'online-mode': false,
|
||||||
|
};
|
||||||
|
|
||||||
|
var world = new World();
|
||||||
|
for (var x = 0; x < 16;x++) {
|
||||||
|
for (var z = 0; z < 16; z++) {
|
||||||
|
world.put(x, 50, z, "block_data", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = mc.createServer(options);
|
||||||
|
|
||||||
|
server.on('login', function(client) {
|
||||||
|
broadcast({ text: client.username+' joined the game.', color: "yellow" });
|
||||||
|
var addr = client.socket.remoteAddress + ':' + client.socket.remotePort;
|
||||||
|
console.log(client.username+' connected', '('+addr+')');
|
||||||
|
|
||||||
|
client.on('end', function() {
|
||||||
|
broadcast({ text: client.username+' left the game.', color: "yellow" });
|
||||||
|
console.log(client.username+' disconnected', '('+addr+')');
|
||||||
|
});
|
||||||
|
|
||||||
|
// send init data so client will start rendering world
|
||||||
|
client.write(0x01, {
|
||||||
|
entityId: client.id,
|
||||||
|
levelType: 'default',
|
||||||
|
gameMode: 1,
|
||||||
|
dimension: 0,
|
||||||
|
difficulty: 0,
|
||||||
|
maxPlayers: server.maxPlayers
|
||||||
|
});
|
||||||
|
var chunkBulk = world.packMapChunkBulk([0,0], function(err, packetData) {
|
||||||
|
if (err) {
|
||||||
|
console.log(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
client.write(0x26, packetData);
|
||||||
|
client.write(0x08, {
|
||||||
|
x: 6,
|
||||||
|
y: 53,
|
||||||
|
z: 6,
|
||||||
|
yaw: 0,
|
||||||
|
pitch: 0,
|
||||||
|
onGround: true
|
||||||
|
});
|
||||||
|
console.log("Written position, player should spawn");
|
||||||
|
client.write(0x03, {
|
||||||
|
age: [0,0],
|
||||||
|
time: [0,1]
|
||||||
|
});
|
||||||
|
client.write(0x2B, {
|
||||||
|
reason: 3,
|
||||||
|
gameMode: 1
|
||||||
|
});
|
||||||
|
});
|
||||||
|
client.on([states.PLAY, 0x01], function(data) {
|
||||||
|
var message = '<'+client.username+'>' + ' ' + data.message;
|
||||||
|
broadcast(message, client.username);
|
||||||
|
console.log(message);
|
||||||
|
});
|
||||||
|
client.on('packet', function(packet) {
|
||||||
|
console.log(packet);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('error', function(error) {
|
||||||
|
console.log('Error:', error);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('listening', function() {
|
||||||
|
console.log('Server listening on port', server.socketServer.address().port);
|
||||||
|
});
|
||||||
|
|
||||||
|
function broadcast(message, username) {
|
||||||
|
var client, translate;
|
||||||
|
translate = username ? 'chat.type.announcement' : 'chat.type.text';
|
||||||
|
username = username || 'Server';
|
||||||
|
for (var clientId in server.clients) {
|
||||||
|
if (!server.clients.hasOwnProperty(clientId)) continue;
|
||||||
|
|
||||||
|
client = server.clients[clientId];
|
||||||
|
var msg = {
|
||||||
|
translate: translate,
|
||||||
|
"with": [
|
||||||
|
username
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (typeof message === "string") {
|
||||||
|
msg["with"].push(message);
|
||||||
|
} else {
|
||||||
|
Object.keys(message).forEach(function(key) {
|
||||||
|
if (key === "text") {
|
||||||
|
msg["with"].push(message[key]);
|
||||||
|
} else {
|
||||||
|
msg[key] = message[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
client.write(0x02, { message: JSON.stringify(msg) });
|
||||||
|
}
|
||||||
|
}
|
||||||
25
package.json
Normal file
25
package.json
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"name": "mc-plain-server",
|
||||||
|
"description": "",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "./mc-plain-server.js",
|
||||||
|
"author": {
|
||||||
|
"name": "roblabla",
|
||||||
|
"email": "robinlambertz.dev@gmail.com"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"licenses": {
|
||||||
|
"type": "mit"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minecraft-protocol": "git://github.com/roblabla/node-minecraft-protocol.git#feature-mc1.7",
|
||||||
|
"buffers": "0.1.1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/YOUR_LOGIN/mc-plain-server.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "http://github.com/YOUR_LOGIN/mc-plain-server/issues"
|
||||||
|
}
|
||||||
|
}
|
||||||
315
world.js
Normal file
315
world.js
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
var util = require('util')
|
||||||
|
, zlib = require('zlib')
|
||||||
|
, Buffers = require('buffers');
|
||||||
|
|
||||||
|
module.exports = World;
|
||||||
|
|
||||||
|
function BiomeData() {
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomeData.prototype.fill = function() {
|
||||||
|
if (!this.data) {
|
||||||
|
this.data = new Buffer(256);
|
||||||
|
for (var i = 0;i < 256;i++) {
|
||||||
|
this.data[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomeData.prototype.unpack = function(buf) {
|
||||||
|
this.data = buf.slice(0, 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomeData.prototype.pack = function() {
|
||||||
|
this.fill();
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomeData.prototype.get = function(x, y, z) {
|
||||||
|
this.fill();
|
||||||
|
return this.data[x + ((y * 16) + z) * 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomeData.prototype.put = function(x, y, z, data) {
|
||||||
|
this.fill();
|
||||||
|
this.data[x + ((y * 16) + z) * 16] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChunkData() {
|
||||||
|
this.length = 16*16*16;
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkData.prototype.fill = function() {
|
||||||
|
if (!this.data) {
|
||||||
|
this.data = new Buffer(this.length);
|
||||||
|
for (var i = 0;i < this.length;i++) {
|
||||||
|
this.data[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkData.prototype.unpack = function(buff) {
|
||||||
|
this.data = buff.slice(0, this.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkData.prototype.pack = function() {
|
||||||
|
this.fill();
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkData.prototype.get = function(x, y, z) {
|
||||||
|
this.fill();
|
||||||
|
return this.data[x + ((y * 16) + z) * 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkData.prototype.put = function(x, y, z, data) {
|
||||||
|
this.fill();
|
||||||
|
this.data[x + ((y * 16) + z) * 16] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChunkDataNibble() {
|
||||||
|
this.length = 16*16*8;
|
||||||
|
this.data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(ChunkDataNibble, ChunkData);
|
||||||
|
|
||||||
|
ChunkDataNibble.prototype.get = function(x, y, z) {
|
||||||
|
this.fill();
|
||||||
|
var r = x % 2;
|
||||||
|
x = Math.floor(x/2);
|
||||||
|
var i = x + ((y * 16) + z) * 16;
|
||||||
|
|
||||||
|
if (r === 0) {
|
||||||
|
return this.data[i] >>> 4;
|
||||||
|
} else {
|
||||||
|
return this.data[i] & 0x0F;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkDataNibble.prototype.put = function(x, y, z, data) {
|
||||||
|
this.fill();
|
||||||
|
var r = x % 2;
|
||||||
|
x = Math.floor(x/2);
|
||||||
|
var i = x + ((y * 16) + z) * 16;
|
||||||
|
|
||||||
|
if (r === 0) {
|
||||||
|
this.data[i] = (this.data[i] & 0x0F) | ((data & 0x0F) << 4);
|
||||||
|
} else {
|
||||||
|
this.data[i] = (this.data[i] & 0xF0) | (this.data & 0x0F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Chunk() {
|
||||||
|
this.block_data = new ChunkData();
|
||||||
|
this.block_meta = new ChunkDataNibble();
|
||||||
|
this.block_add = new ChunkDataNibble();
|
||||||
|
this.light_block = new ChunkDataNibble();
|
||||||
|
this.light_sky = new ChunkDataNibble();
|
||||||
|
}
|
||||||
|
|
||||||
|
function ChunkColumn() {
|
||||||
|
this.chunks = new Array(16);
|
||||||
|
for (var i = 0;i < 16;i++) this.chunks[i] = null;
|
||||||
|
|
||||||
|
this.biome = new BiomeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkColumn.prototype.unpack = function(buff, mask1, mask2, skylight) {
|
||||||
|
if (typeof skylight == "undefined") skylight = true;
|
||||||
|
|
||||||
|
this.unpack_section(buff, "block_data", mask1);
|
||||||
|
this.unpack_section(buff, "block_meta", mask1);
|
||||||
|
this.unpack_section(buff, "light_block", mask1);
|
||||||
|
if (skylight)
|
||||||
|
this.unpack_section(buff, "light_sky", mask1);
|
||||||
|
this.unpack_section(buff, "block_add", mask2);
|
||||||
|
this.biome.unpack(buff);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkColumn.prototype.unpack_section = function(buff, section, mask) {
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
if (mask & (1 << i)) {
|
||||||
|
if (this.chunks[i] === null) {
|
||||||
|
this.chunks[i] = new Chunk();
|
||||||
|
}
|
||||||
|
this.chunks[i][section].unpack(buff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkColumn.prototype.pack = function() {
|
||||||
|
var bufs = new Buffers();
|
||||||
|
var mask1 = 0;
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
if (this.chunks[i] !== null) {
|
||||||
|
mask1 |= 1 << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var block_data = this.pack_section("block_data");
|
||||||
|
var block_meta = this.pack_section("block_meta");
|
||||||
|
var light_block = this.pack_section("light_block");
|
||||||
|
var light_sky = this.pack_section("light_sky");
|
||||||
|
var mask2 = 0;
|
||||||
|
bufs.push(block_data, block_meta, light_block, light_sky);
|
||||||
|
return {
|
||||||
|
data: bufs.toBuffer(),
|
||||||
|
mask1: mask1,
|
||||||
|
mask2: mask2,
|
||||||
|
skylight: true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ChunkColumn.prototype.pack_section = function(section) {
|
||||||
|
var bufs = new Buffers();
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
if (this.chunks[i] !== null)
|
||||||
|
bufs.push(this.chunks[i][section].pack());
|
||||||
|
}
|
||||||
|
return bufs.toBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
function World() {
|
||||||
|
this.columns = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.unpack = function(packetData) {
|
||||||
|
var data = zlib.inflate(packetData.compressedChunkData);
|
||||||
|
packetData.meta.forEach(function (meta) {
|
||||||
|
var key = [meta.x, meta.z];
|
||||||
|
var column;
|
||||||
|
if (key in this.columns) {
|
||||||
|
column = this.columns[key];
|
||||||
|
} else {
|
||||||
|
column = new ChunkColumn();
|
||||||
|
this.columns[key] = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
column.unpack(data, meta.bitMap, meta.addMap, packetData.skyLightSent);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.packMapChunkBulk = function() {
|
||||||
|
var bufs = new Buffers();
|
||||||
|
var metadatas = [];
|
||||||
|
var cb = arguments[arguments.length - 1];
|
||||||
|
|
||||||
|
// First pass, get all the metadatas and buffers.
|
||||||
|
for (var i = 0;i < arguments.length - 1; i++) {
|
||||||
|
var arg = arguments[i];
|
||||||
|
var data = this.columns[arg];
|
||||||
|
if (this.columns === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var packetContent = data.pack();
|
||||||
|
bufs.push(packetContent.data);
|
||||||
|
var metadata = {
|
||||||
|
x: arg[0],
|
||||||
|
z: arg[1],
|
||||||
|
bitMap: packetContent.mask1,
|
||||||
|
addBitMap: packetContent.mask2
|
||||||
|
}
|
||||||
|
metadatas.push(metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deflate/compress the buffers
|
||||||
|
var deflate = zlib.createDeflate();
|
||||||
|
var deflateBuffer = new Buffer(0);
|
||||||
|
deflate.on('data', function(data) {
|
||||||
|
deflateBuffer = Buffer.concat([deflateBuffer, data]);
|
||||||
|
});
|
||||||
|
deflate.on('end', function() {
|
||||||
|
cb(null, {
|
||||||
|
data: {
|
||||||
|
skyLightSent: true,
|
||||||
|
meta: metadatas,
|
||||||
|
compressedChunkData: deflateBuffer
|
||||||
|
}});
|
||||||
|
});
|
||||||
|
deflate.on('error', function(err) {
|
||||||
|
console.log("error");
|
||||||
|
cb(err);
|
||||||
|
});
|
||||||
|
deflate.write(bufs.toBuffer());
|
||||||
|
deflate.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.get = function(x, y, z, key) {
|
||||||
|
var rx = x % 16;
|
||||||
|
x = Math.floor(x / 16);
|
||||||
|
var ry = y % 16;
|
||||||
|
y = Math.floor(y / 16);
|
||||||
|
var rz = z % 16;
|
||||||
|
z = Math.floor(z / 16);
|
||||||
|
|
||||||
|
if (!([x,z] in this.columns)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var column = this.columns[[x,z]];
|
||||||
|
var chunk = column.chunks[y];
|
||||||
|
|
||||||
|
if (chunk == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk[key].get(rx, ry, rz);
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.put = function(x, y, z, key, data) {
|
||||||
|
var rx = x % 16;
|
||||||
|
x = Math.floor(x / 16);
|
||||||
|
var ry = y % 16;
|
||||||
|
y = Math.floor(y / 16);
|
||||||
|
var rz = z % 16;
|
||||||
|
z = Math.floor(z / 16);
|
||||||
|
|
||||||
|
var column;
|
||||||
|
if ([x,z] in this.columns) {
|
||||||
|
column = this.columns[[x,z]];
|
||||||
|
} else {
|
||||||
|
column = new ChunkColumn();
|
||||||
|
this.columns[[x,z]] = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
var chunk = column.chunks[y];
|
||||||
|
if (chunk == null) {
|
||||||
|
chunk = new Chunk();
|
||||||
|
column.chunks[y] = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk[key].put(rx, ry, rz, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.get_biome = function(x, z) {
|
||||||
|
var rx = x % 16;
|
||||||
|
x = Math.floor(x / 16);
|
||||||
|
var rz = z % 16;
|
||||||
|
z = Math.floor(z / 16);
|
||||||
|
|
||||||
|
if (!([x,z] in this.columns)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.columns[[x,z]].biome.get(rx, rz);
|
||||||
|
}
|
||||||
|
|
||||||
|
World.prototype.put_biome = function(x, z, data) {
|
||||||
|
var rx = x % 16;
|
||||||
|
x = Math.floor(x / 16);
|
||||||
|
var rz = z % 16;
|
||||||
|
z = Math.floor(z / 16);
|
||||||
|
|
||||||
|
var column;
|
||||||
|
if ([x,z] in this.columns) {
|
||||||
|
column = this.columns[[x,z]];
|
||||||
|
} else {
|
||||||
|
column = new ChunkColumn();
|
||||||
|
this.columns[[x,z]] = column;
|
||||||
|
}
|
||||||
|
|
||||||
|
return column.biome.put(rx, rz, data);
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue