diff --git a/server/Workers/ChunkPacketGenerator.js b/server/Workers/ChunkPacketGenerator.js new file mode 100644 index 0000000..9f01ec3 --- /dev/null +++ b/server/Workers/ChunkPacketGenerator.js @@ -0,0 +1,71 @@ +const { Worker, MessageChannel, MessagePort, isMainThread, parentPort } = require('worker_threads'); + +const bufferStuff = require("../bufferStuff.js"); + +let chunkY = 0; +let busyInterval = null; + +parentPort.on("message", (chunk) => { + if (busyInterval == null) { + busyInterval = setInterval(() => {}, 86400000); + } + + chunkY = 0; + // I couldn't figure out how to construct a chunk lmao + // __ima just send each block individually__ + // Using multi block chunks now! + // TODO: yknow, figure out how to chunk. + let chunksToSend = []; + + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + chunkY += 16; + chunksToSend.push(doSquareChunk(chunk)); + + parentPort.postMessage([chunk[3], chunksToSend, chunk[4], chunk[5]]); +}); + +function doSquareChunk(chunk) { + let blocksToSend = []; + for (let y = 0; y < 16; y++) { + for (let x = 0; x < 16; x++) { + for (let z = 0; z < 16; z++) { + if (chunk[2][chunkY + y][x][z] == 0) continue; // don't send air lol + blocksToSend.push([chunk[2][chunkY + y][x][z], x & 0xf, z & 0xf, chunkY + y]); + } + } + } + + const writer = new bufferStuff.Writer(); + writer.writeByte(0x34); + writer.writeInt(chunk[0]); + writer.writeInt(chunk[1]); + writer.writeShort(blocksToSend.length); + // Block coords + for (let blocks of blocksToSend) { + writer.writeShort((blocks[1] << 12 | blocks[2] << 8 | blocks[3]) - 32768); + } + // Block types + for (let blocks of blocksToSend) { + writer.writeByte(blocks[0]); + } + // Block metadata + for (let blocks of blocksToSend) { + writer.writeByte(0); + } + + //user.chunksToSend.add(writer.buffer) // so we don't flood the client queue these + //parentPort.postMessage(writer.buffer); + return writer.buffer; +} \ No newline at end of file diff --git a/server/chunkManager.js b/server/chunkManager.js index b95e378..5a4831a 100644 --- a/server/chunkManager.js +++ b/server/chunkManager.js @@ -1,17 +1,77 @@ const FunkyArray = require("./Util/funkyArray.js"); const bufferStuff = require("./bufferStuff.js"); +const { Worker, isMainThread, parentPort } = require('worker_threads'); + +const workerPath = __dirname + "/Workers/ChunkPacketGenerator.js"; + module.exports = class { constructor() { this.chunks = {}; this.queuedBlockUpdates = new FunkyArray(); + global.generatingChunks = true; + + this.threadPool = []; + this.workPool = new FunkyArray(); + + this.toRemove = []; + + for (let i = 0; i < 6; i++) { + const worker = new Worker(workerPath); + this.threadPool.push([false, worker]); + const myID = i; + worker.on("message", (message) => { + const user = global.getUserByKey(message[0]); + for (let square of message[1]) { user.chunksToSend.add(Buffer.from(square)); } + this.threadPool[myID][0] = false; + this.toRemove.push(message[2]); + }); + } + + setInterval(() => { + if (this.workPool.getLength() > 0) { + let limit = Math.min(this.workPool.getLength(), this.threadPool.length); + for (let i = 0; i < limit; i++) { + for (let i1 = 0; i1 < this.threadPool.length; i1++) { + let thread = this.threadPool[i1]; + if (!thread[0]) { + const key = this.workPool.itemKeys[i]; + const item = this.workPool.getByKey(key); + // Already being processed + if (item == null) break; + if (item[0] == true) { + limit += 1; + break; + } + item[0] = true; + item[1][4] = key; + item[1][5] = i1; + thread[1].postMessage(item[1]); + thread[0] = true; + break; + } + } + } + + for (let item of this.toRemove) { + //console.log("removing item " + this.workPool.getByKey(item)[0]); + this.workPool.remove(item); + } + this.toRemove = []; + } + }, 1000 / 20); + + const chunkStartTime = new Date().getTime(); for (let x = -3; x < 4; x++) { for (let z = -3; z < 4; z++) { this.createChunk(x, z); } } + console.log("Chunk generation took " + (new Date().getTime() - chunkStartTime) + "ms"); + + global.generatingChunks = false; } // TODO: Store metadata! @@ -24,12 +84,13 @@ module.exports = class { for (let y = 0; y < 128; y++) { this.chunks[cx][cz][y] = {}; for (let x = 0; x < 16; x++) { - this.chunks[cx][cz][y][x] = []; + this.chunks[cx][cz][y][x] = {}; for (let z = 0; z < 16; z++) { if (y == 64) { - this.chunks[cx][cz][y][x].push(2); // Make a tree :) if (Math.random() <= 0.01) { + this.chunks[cx][cz][y][x][z] = 3; + const newX = x + (16 * cx), newZ = z + (16 * cz); // trunk this.setBlock(17, newX, y + 1, newZ); @@ -37,61 +98,25 @@ module.exports = class { this.setBlock(17, newX, y + 3, newZ); this.setBlock(17, newX, y + 4, newZ); // leaves - this.setBlock(18, newX + 2, y + 3, newZ + 2); - this.setBlock(18, newX + 1, y + 3, newZ + 2); - this.setBlock(18, newX, y + 3, newZ + 2); - this.setBlock(18, newX - 1, y + 3, newZ + 2); - this.setBlock(18, newX - 2, y + 3, newZ + 2); + // left + this.setBlock(18, newX, y + 5, newZ); + // right line + } else { + this.chunks[cx][cz][y][x][z] = 2; } } - else if (y == 63 || y == 62) this.chunks[cx][cz][y][x].push(3); - else if (y == 0) this.chunks[cx][cz][y][x].push(7); - else if (y < 62) this.chunks[cx][cz][y][x].push(1); - else this.chunks[cx][cz][y][x].push(0); + else if (y == 63 || y == 62) this.chunks[cx][cz][y][x][z] = 3; + else if (y == 0) this.chunks[cx][cz][y][x][z] = 7; + else if (y < 62) this.chunks[cx][cz][y][x][z] = 1; + else this.chunks[cx][cz][y][x][z] = 0; } } } } - multiBlockChunk(chunkX = 0, chunkZ = 0, user) { - const writer = new bufferStuff.Writer(); - - // I couldn't figure out how to construct a chunk lmao - // __ima just send each block individually__ - // Using multi block chunks now! - // TODO: yknow, figure out how to chunk. - let blocksToSend = []; - for (let y = 0; y < 128; y++) { - blocksToSend = []; - for (let x = 0; x < 16; x++) { - for (let z = 0; z < 16; z++) { - if (this.chunks[chunkX][chunkZ][y][x][z] == 0) continue; // don't send air lol - blocksToSend.push([this.chunks[chunkX][chunkZ][y][x][z], x & 0xf, z & 0xf]); - } - } - - if (blocksToSend.length > 0) { - writer.reset(); - writer.writeByte(0x34); - writer.writeInt(chunkX); - writer.writeInt(chunkZ); - writer.writeShort(blocksToSend.length); - // Block coords - for (let blocks of blocksToSend) { - writer.writeShort((blocks[1] << 12 | blocks[2] << 8 | y) - 32768); - } - // Block types - for (let blocks of blocksToSend) { - writer.writeByte(blocks[0]); - } - // Block metadata - for (let blocks of blocksToSend) { - writer.writeByte(0); - } - - user.chunksToSend.add(writer.buffer) // so we don't flood the client queue these - } - } + async multiBlockChunk(chunkX = 0, chunkZ = 0, user) { + //worker.postMessage([chunkX, chunkZ, this.chunks[chunkX][chunkZ]]); + this.workPool.add([false, [chunkX, chunkZ, this.chunks[chunkX][chunkZ], user.id, null, null]]); } setBlock(id = 0, x = 0, y = 0, z = 0) { diff --git a/server/server.js b/server/server.js index 9d5eb5c..e3dffe6 100644 --- a/server/server.js +++ b/server/server.js @@ -17,6 +17,10 @@ global.fromIDPool = function() { let netUsers = {}, netUserKeys = Object.keys(netUsers); +global.getUserByKey = function(key) { + return netUsers[key]; +} + function addUser(socket) { let user = new User(global.fromIDPool(), socket); netUsers[user.id] = user; @@ -25,8 +29,9 @@ function addUser(socket) { return user; } -function removeUser() { - +function removeUser(id) { + delete netUsers[id]; + netUserKeys = Object.keys(netUsers); } let config = {}; @@ -34,6 +39,7 @@ let config = {}; let entities = {}; global.chunkManager = new ChunkManager(); +global.generatingChunks = false; let tickInterval, tickCounter = BigInt(0); let tickRate = BigInt(20); @@ -43,11 +49,36 @@ module.exports.init = function(config) { console.log(`Up! Running at 0.0.0.0:${config.port}`); tickInterval = setInterval(() => { + // Update Chunks + if (!global.generatingChunks) { + let itemsToRemove = []; + 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]; + 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 in netUserKeys) { + const user = netUsers[userKey]; + if (user.loginFinished) user.socket.write(packet); + } + } catch (e) {} + } + + for (let item of itemsToRemove) { + global.chunkManager.queuedBlockUpdates.remove(item, false); + } + } + + // Update users for (let key of netUserKeys) { const user = netUsers[netUserKeys]; let itemsToRemove = []; - for (let i = 0; i < Math.min(user.chunksToSend.getLength(), 128); i++) { + for (let i = 0; i < Math.min(user.chunksToSend.getLength(), 32); i++) { const chunkKey = user.chunksToSend.itemKeys[i]; itemsToRemove.push(chunkKey); user.socket.write(user.chunksToSend.items[chunkKey]); @@ -64,7 +95,7 @@ module.exports.init = function(config) { }, 1000 / parseInt(tickRate.toString())); } -module.exports.connection = function(socket = new Socket) { +module.exports.connection = async function(socket = new Socket) { const thisUser = addUser(socket); socket.on('data', function(chunk) { @@ -79,12 +110,13 @@ module.exports.connection = function(socket = new Socket) { socket.write(new PacketMappingTable[NamedPackets.LoginRequest](reader.readInt(), reader.readString(), reader.readLong(), reader.readByte()).writePacket(thisUser.id)); socket.write(new PacketMappingTable[NamedPackets.SpawnPosition]().writePacket()); + const dt = new Date().getTime(); for (let x = -3; x < 4; x++) { for (let z = -3; z < 4; z++) { socket.write(new PacketMappingTable[NamedPackets.PreChunk](x, z, true).writePacket()); - global.chunkManager.multiBlockChunk(x, z, thisUser); } } + console.log("Chunk packet generation took " + (new Date().getTime() - dt) + "ms"); socket.write(new PacketMappingTable[NamedPackets.BlockChange](8, 64, 8, 20, 0).writePacket()); socket.write(new PacketMappingTable[NamedPackets.Player](true).writePacket()); @@ -94,6 +126,12 @@ module.exports.connection = function(socket = new Socket) { socket.write(new PacketMappingTable[NamedPackets.PlayerPositionAndLook](8.5, 65 + 1.6200000047683716, 65, 8.5, 0, 0, false).writePacket()); thisUser.loginFinished = true; + + for (let x = -3; x < 4; x++) { + for (let z = -3; z < 4; z++) { + global.chunkManager.multiBlockChunk(x, z, thisUser); + } + } break; case NamedPackets.Handshake: @@ -106,9 +144,11 @@ module.exports.connection = function(socket = new Socket) { socket.on('end', function() { console.log("Connection closed"); + removeUser(thisUser.id); }); socket.on('error', function(err) { console.log("Connection error!"); + removeUser(thisUser.id); }); } \ No newline at end of file