diff --git a/server/Generators/GeneratorPerlin.js b/server/Generators/GeneratorPerlin.js index f70b0cb..4ddb97a 100644 --- a/server/Generators/GeneratorPerlin.js +++ b/server/Generators/GeneratorPerlin.js @@ -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]; } \ No newline at end of file diff --git a/server/Workers/ChunkWorker.js b/server/Workers/ChunkWorker.js index fad6beb..d353aae 100644 --- a/server/Workers/ChunkWorker.js +++ b/server/Workers/ChunkWorker.js @@ -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; diff --git a/server/chunkManager.js b/server/chunkManager.js index 6ce0811..60a087d 100644 --- a/server/chunkManager.js +++ b/server/chunkManager.js @@ -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]); } } \ No newline at end of file diff --git a/server/server.js b/server/server.js index 1fdcff7..1a83055 100644 --- a/server/server.js +++ b/server/server.js @@ -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: