metadata support

I wanted to put tall grass down but it defaults to shrubs and needs a damage value
This commit is contained in:
Holly Stubbs 2021-10-21 01:29:25 +01:00
parent fff42cf947
commit 5b0fe58b4e
Signed by: tgpholly
GPG key ID: B8583C4B7D18119E
4 changed files with 189 additions and 49 deletions

View file

@ -1,6 +1,6 @@
const { perlin2D } = require("./perlin.js");
module.exports = function(cx = 0, cz = 0) {
module.exports = function(cx = 0, cz = 0, seed = 0) {
// Create bare chunk
let chunk = {};
for (let y = 0; y < 128; y++) {
@ -8,7 +8,7 @@ module.exports = function(cx = 0, cz = 0) {
for (let x = 0; x < 16; x++) {
chunk[y][x] = {};
for (let z = 0; z < 16; z++) {
chunk[y][x][z] = 0;
chunk[y][x][z] = [0, 0];
}
}
}
@ -21,12 +21,20 @@ module.exports = function(cx = 0, cz = 0) {
stripTopCoord[x][z] = 0;
}
}
// Generate top layer of grass
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
const yCoord = stripTopCoord[x][z] = Math.floor(64 + (perlin2D(((cx << 4) + x) / 15, ((cz << 4) + z) / 15) * 8));
chunk[yCoord][x][z] = 2;
// NOTE: Because of the way this is, it is not random at all. The heightmap is simply offset so uhhh.
// TODO: Figure out a better way of dealing with this :)
const layer1 = (64 + (perlin2D(((cx << 4) + x) / 15, ((cz << 4) + z) / 15) * 10));
const layer2 = (64 + (perlin2D(((cx + (10 + seed) << 4) + x) / 15, ((cz + (4 + seed) << 4) + z) / 15) * 10));
const layer3_1 = (64 + (perlin2D(((cx + (-15 + seed) << 4) + x) / 15, ((cz + (-2 + seed) << 4) + z) / 15) * 23));
const layer3_2 = (64 + (perlin2D(((cx + (25 + seed) << 4) + x) / 15, ((cz + (-17 + seed) << 4) + z) / 15) * 40));
const layer3 = (layer3_1 + layer3_2) / 2;
const average = Math.floor((layer1 + layer2 + layer3) / 3);
stripTopCoord[x][z] = average;
chunk[average][x][z][0] = 2;
}
}
@ -38,9 +46,9 @@ module.exports = function(cx = 0, cz = 0) {
topM2 = topM1 - 1;
for (let y = stripTopCoord[x][z]; y != -1; y--) {
if (y == topM1 || y == topM2) chunk[y][x][z] = 3;
else if (y == 0) chunk[y][x][z] = 7;
else if (y < topM2) chunk[y][x][z] = 1;
if (y == topM1 || y == topM2) chunk[y][x][z][0] = 3;
else if (y == 0) chunk[y][x][z][0] = 7;
else if (y < topM2) chunk[y][x][z][0] = 1;
}
}
}
@ -49,12 +57,123 @@ module.exports = function(cx = 0, cz = 0) {
for (let y = 0; y < 128; y++) {
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
if (chunk[y][x][z] == 0 && y < 62) chunk[y][x][z] = 9;
if (chunk[y][x][z][0] == 0 && y < 64) chunk[y][x][z][0] = 9;
if (y < 127 && y > 0) if (chunk[y][x][z] == 9 && chunk[y - 1][x][z] == 2) chunk[y - 1][x][z] = 3;
if (y < 127 && y > 0) if (chunk[y][x][z][0] == 9 && chunk[y - 1][x][z][0] == 2) chunk[y - 1][x][z][0] = 3;
//if (x == 0 && z == 0) chunk[y][x][z] = 57;
}
}
}
return chunk;
let treeBlocks = [];
const chunkX = cx << 4;
const chunkZ = cz << 4;
// 3rd pass???
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
const topBlock = stripTopCoord[x][z];
if (chunk[topBlock][x][z][0] == 2 && Math.floor(Math.random() * 5) == 0) {
chunk[topBlock + 1][x][z][0] = 31;
chunk[topBlock + 1][x][z][1] = 1;
}
// Need a better way of doing this it currently takes a severely long time (gee I wonder why)
if (chunk[topBlock][x][z][0] == 2 && Math.floor(Math.random() * 200) == 0) {
chunk[topBlock][x][z][0] = 3;
// Logs
treeBlocks.push([(chunkX + x), topBlock + 1, (chunkZ + z), 17]);
treeBlocks.push([(chunkX + x), topBlock + 2, (chunkZ + z), 17]);
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z), 17]);
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z), 17]);
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z), 17]);
// Leaves
// Layer 1
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 3, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 3, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 3, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 3, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 3, (chunkZ + z) + 2, 18]);
// Layer 2
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 4, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 4, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) + 2, topBlock + 4, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 4, (chunkZ + z) + 2, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) - 2, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 2, topBlock + 4, (chunkZ + z) + 2, 18]);
// Layer 3
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 5, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) - 1, topBlock + 5, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 5, (chunkZ + z) - 1, 18]);
// Layer 4
treeBlocks.push([(chunkX + x) - 1, topBlock + 6, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x) + 1, topBlock + 6, (chunkZ + z), 18]);
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z) - 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z) + 1, 18]);
treeBlocks.push([(chunkX + x), topBlock + 6, (chunkZ + z), 18]);
}
}
}
return [chunk, treeBlocks];
}

View file

@ -26,14 +26,15 @@ parentPort.on("message", (data) => {
case "generate":
const startTime = new Date().getTime();
parentPort.postMessage([data[0], generateChunk(data[1], data[2]), data[1], data[2], data[3]]);
console.log(`Chunk (${data[1]}, ${data[2]}) took ${new Date().getTime() - startTime}ms to generate`);
const chunkData = generateChunk(data[1], data[2], data[4]);
parentPort.postMessage([data[0], chunkData[0], data[1], data[2], data[3], chunkData[1]]);
console.log(`Chunk took ${new Date().getTime() - startTime}ms`);
break;
}
});
function generateChunk(x = 0, z = 0) {
return GeneratorPerlin(x, z);
function generateChunk(x = 0, z = 0, seed = 0) {
return GeneratorPerlin(x, z, seed);
}
function doChunk(chunk) {
@ -56,11 +57,11 @@ function doChunk(chunk) {
for (let x = 0; x < 16; x++) {
for (let z = 0; z < 16; z++) {
for (let y = 0; y < 128; y++) {
blocks.writeByte(chunk[2][y][x][z]); // The block
blocks.writeByte(chunk[2][y][x][z][0]);
if (blockMeta) {
metadata.writeByte(0x00); // TODO: Metadata
metadata.writeNibble(chunk[2][y - 1][x][z][1], chunk[2][y][x][z][1]); // NOTE: This is sorta jank but it does work
// Light level 15 for 2 blocks (1111 1111)
lighting.writeUByte(0xFF); // TODO: Lighting (Client seems to do it's own so it's not top priority)
lighting.writeNibble(15, 15); // TODO: Lighting (Client seems to do it's own (when a block update happens) so it's not top priority)
}
// Hack for nibble stuff
blockMeta = !blockMeta;

View file

@ -7,6 +7,7 @@
const { Worker } = require('worker_threads');
const FunkyArray = require("./Util/funkyArray.js");
const pRandom = require("./Util/prettyRandom.js");
const config = require("../config.json");
const workerPath = `${__dirname}/Workers/ChunkWorker.js`;
@ -23,6 +24,9 @@ module.exports = class {
this.workPool = new FunkyArray();
this.toRemove = [];
// TODO: Figure out a better way of doing this?
this.seed = pRandom(-2147483647, 2147483647) - new Date().getTime() * pRandom(1, 6);
// WoAh!!! Thread pool in js!?!??!???!11!?!?!
for (let i = 0; i < config.worldThreads; i++) {
const worker = new Worker(workerPath);
@ -39,9 +43,15 @@ module.exports = class {
break;
case "generate":
const startTime = new Date().getTime();
this.chunks[data[2]][data[3]] = data[1];
this.toRemove.push(data[4]);
const treeBlocksRef = data[5];
treeBlocksRef.forEach((block) => {
this.setBlock(block[0], block[1], block[2], block[3]);
});
this.threadPool[myID][0] = false;
console.log(`Trees fin took ${new Date().getTime() - startTime}ms`);
break;
}
});
@ -79,13 +89,7 @@ module.exports = class {
}
this.toRemove = [];
}
}, 1000 / 20);
for (let x = -3; x < 4; x++) {
for (let z = -3; z < 4; z++) {
this.createChunk(x, z);
}
}
}, 1000 / 60);
global.generatingChunks = false;
}
@ -94,7 +98,7 @@ module.exports = class {
createChunk(cx = 0, cz = 0) {
if (this.chunks[cx] == null) this.chunks[cx] = {};
this.workPool.add([false, ["generate", cx, cz, null]]);
this.workPool.add([false, ["generate", cx, cz, null, this.seed]]);
}
chunkExists(cx = 0, cz = 0) {
@ -109,17 +113,19 @@ module.exports = class {
this.workPool.add([false, ["chunk", [chunkX, chunkZ, this.chunks[chunkX][chunkZ]], user.id, null]]);
}
setBlock(id = 0, x = 0, y = 0, z = 0) {
setBlock(x = 0, y = 0, z = 0, id = 0, metadata = 0) {
if (y < 0 || y > 127) return console.error("Tried to set a block outside of the world!");
const chunkX = x >> 4;
const chunkZ = z >> 4;
const blockX = x - (16 * chunkX);
const blockZ = z - (16 * chunkZ);
const blockX = x - (chunkX << 4);
const blockZ = z - (chunkZ << 4);
// Don't queue a block update if that block is already this block
//if (this.chunks[chunkX][chunkZ][y][blockX][blockZ] == id) return;
// Don't queue a block update if that block is already this block (wow those ifs)
if (this.chunks[chunkX] != null)
if (this.chunks[chunkX][chunkZ] != null)
if (this.chunks[chunkX][chunkZ][y][blockX][blockZ] == id) return;
this.queuedBlockUpdates.add([id, chunkX, chunkZ, y, blockX, blockZ]);
this.queuedBlockUpdates.add([chunkX, chunkZ, y, blockX, blockZ, id, metadata]);
}
}

View file

@ -84,33 +84,41 @@ module.exports.init = function(config) {
}
// Do chunk updates
// Don't update if chunk is generating
if (!global.generatingChunks) {
if (true) {
let itemsToRemove = [];
// Do a max of 128 chunk updates per tick
// Do a max of 128 block updates per tick
for (let i = 0; i < Math.min(global.chunkManager.queuedBlockUpdates.getLength(), 128); i++) {
const chunkUpdateKey = global.chunkManager.queuedBlockUpdates.itemKeys[i];
const chunkUpdate = global.chunkManager.queuedBlockUpdates.items[chunkUpdateKey];
// Don't update if chunk is nonexistant
if (global.chunkManager.chunks[chunkUpdate[1]] == null) continue;
if (global.chunkManager.chunks[chunkUpdate[1]][chunkUpdate[2]] == null) continue;
itemsToRemove.push(chunkUpdateKey);
try {
global.chunkManager.chunks[chunkUpdate[1]][chunkUpdate[2]][chunkUpdate[3]][chunkUpdate[4]][chunkUpdate[5]] = chunkUpdate[0];
const packet = new PacketMappingTable[NamedPackets.BlockChange](chunkUpdate[4] + (16 * chunkUpdate[1]), chunkUpdate[3], chunkUpdate[5] + (16 * chunkUpdate[2]), chunkUpdate[0]).writePacket();
for (let userKey of netUserKeys) {
const user = netUsers[userKey];
if (user.loginFinished) user.socket.write(packet);
}
} catch (e) {
console.error(e);
// TODO: Remove this once infinite terrain is in :)
if (chunkUpdate[0] < -10 || chunkUpdate[0] > 10 || chunkUpdate[1] < -10 || chunkUpdate[1] > 10) {
itemsToRemove.push(chunkUpdateKey);
continue;
}
// If the chunk just plain doesn't exist (yet) skip this one
if (global.chunkManager.chunks[chunkUpdate[0]] == null) continue;
if (global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]] == null) continue;
global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]][chunkUpdate[2]][chunkUpdate[3]][chunkUpdate[4]][0] = chunkUpdate[5];
global.chunkManager.chunks[chunkUpdate[0]][chunkUpdate[1]][chunkUpdate[2]][chunkUpdate[3]][chunkUpdate[4]][1] = chunkUpdate[6];
const packet = new PacketMappingTable[NamedPackets.BlockChange](chunkUpdate[3] + (chunkUpdate[0] << 4), chunkUpdate[2], chunkUpdate[4] + (chunkUpdate[1] << 4), chunkUpdate[5], chunkUpdate[6]).writePacket();
for (let userKey of netUserKeys) {
const user = netUsers[userKey];
if (user.loginFinished) user.socket.write(packet);
}
itemsToRemove.push(chunkUpdateKey);
}
for (let item of itemsToRemove) {
global.chunkManager.queuedBlockUpdates.remove(item, false);
}
global.chunkManager.queuedBlockUpdates.regenerateIterableArray();
}
// Entity update!
@ -143,6 +151,12 @@ module.exports.init = function(config) {
tickCounter++;
worldTime++;
}, 1000 / parseInt(tickRate.toString()));
for (let x = -3; x < 4; x++) {
for (let z = -3; z < 4; z++) {
global.chunkManager.createChunk(x, z);
}
}
}
module.exports.connection = async function(socket = new Socket) {
@ -294,7 +308,7 @@ module.exports.connection = async function(socket = new Socket) {
const y = reader.readByte();
const z = reader.readInt();
global.chunkManager.setBlock(0, x, y, z);
global.chunkManager.setBlock(x, y, z, 0);
}
break;
@ -313,7 +327,7 @@ module.exports.connection = async function(socket = new Socket) {
}
const block = reader.readShort();
global.chunkManager.setBlock(block, x + xOff, y + yOff, z + zOff);
global.chunkManager.setBlock(x + xOff, y + yOff, z + zOff, block);
break;
case NamedPackets.Player: