From 1917165caf6ac6eda4aed4eab386808837a1a525 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Tue, 13 Oct 2015 23:17:01 -0700 Subject: [PATCH] Change serv.world to serv.worlds. Added playerPlugins/world.js and moved a lot of stuff from login.js there. Generating multiple worlds works and moving players there works as well. A few issues here and there. --- doc/api.md | 38 ++++++- src/lib/playerPlugins/blocks.js | 9 +- src/lib/playerPlugins/chest.js | 4 +- src/lib/playerPlugins/digging.js | 2 +- src/lib/playerPlugins/login.js | 124 +++-------------------- src/lib/playerPlugins/updatePositions.js | 20 ++++ src/lib/playerPlugins/world.js | 124 +++++++++++++++++++++++ src/lib/serverPlugins/daycycle.js | 28 ++--- src/lib/serverPlugins/tick.js | 24 ++--- src/lib/serverPlugins/world.js | 28 ++++- src/lib/worldGenerations/nether.js | 31 ++++++ 11 files changed, 278 insertions(+), 154 deletions(-) create mode 100644 src/lib/playerPlugins/world.js create mode 100644 src/lib/worldGenerations/nether.js diff --git a/doc/api.md b/doc/api.md index 88b4f45..1b686ed 100644 --- a/doc/api.md +++ b/doc/api.md @@ -11,7 +11,7 @@ - [serv.entityMaxId](#serventitymaxid) - [serv.players](#servplayers) - [serv.uuidToPlayer](#servuuidtoplayer) - - [serv.world](#servworld) + - [serv.worlds](#servworlds) - [serv.entities](#serventities) - [serv.bannedPlayers](#servbannedplayers) - [serv.time](#servtime) @@ -41,6 +41,7 @@ - [player.entity](#playerentity) - [player.username](#playerusername) - [player.view](#playerview) + - [player.world](#playerworld) - [Events](#events-1) - ["connected"](#connected) - ["spawned"](#spawned) @@ -49,6 +50,7 @@ - ["chat" (message)](#chat-message) - ["kicked" (kicker,reason)](#kicked-kickerreason) - ["positionChanged"](#positionchanged) + - ["teleport"](#teleport) - [Methods](#methods-1) - [player.login()](#playerlogin) - [player.ban(reason)](#playerbanreason) @@ -62,6 +64,8 @@ - [player.setGameMode(gameMode)](#playersetgamemodegamemode) - [player.handleCommand(command)](#playerhandlecommandcommand) - [player.updateHealth(health)](#playerupdatehealthhealth) + - [player.sendPosition(position, opt)](#playersendpositionposition-opt) + - [player.changeWorld(worldNumber, opt)](#playerchangeworldworldnumber-opt) - [Low level properties](#low-level-properties) - [player._client](#player_client) - [Low level methods](#low-level-methods) @@ -98,9 +102,9 @@ Array of connected players Object uuid to players -#### serv.world +#### serv.worlds -The map +Array of all worlds. By default, 0 is Overworld, 1 is Nether, and 2 is the End, however any plugin may push more worlds (`serv.worlds.push(new World(...))`) and the player can be sent there with `player.changeWorld(worldNumber, opt)`. #### serv.entities @@ -224,6 +228,10 @@ The username of the player The view size of the player, for example 8 for 16x16 +#### player.world + +Which world the player is in. Get it by using `serv.worlds[player.world]`. + ### Events #### "connected" @@ -252,7 +260,11 @@ Fires when the player says `message`. #### "positionChanged" -fires when the position changed +fires when the position changes in small amounts (walking, running, or flying) + +#### "teleport" + +fires when the position changes in larger amounts (player's position set by `player.sendPosition()`) ### Methods @@ -309,6 +321,24 @@ handle `command` update the player health. +#### player.sendPosition(position, opt) + +Teleport the player to any `position` in the same world. Use `player.changeWorld` to teleport to any world. Options: + +- yaw: Set the yaw, default is the current yaw of the player +- pitch: Set the pitch, default is the current pitch of the player + +#### player.changeWorld(worldNumber, opt) + +Change which world the player is in (Overworld, Nether, End, or any other world added to `serv.worlds`). Options: + +- gamemode: Gamemode of the world (Default is player gamemode) +- difficulty: Difficulty of world. Default is 0 (easiest) +- dimension: Dimension of world. 0 is Overworld, -1 is Nether, 1 is End (Default is 0) +- position: Position player spawns, default is their default spawn point +- yaw: Yaw in which they spawn, default is 0 +- pitch: Pitch in which they spawn, default is 0 + ### Low level properties #### player._client diff --git a/src/lib/playerPlugins/blocks.js b/src/lib/playerPlugins/blocks.js index bc763cc..81e6422 100644 --- a/src/lib/playerPlugins/blocks.js +++ b/src/lib/playerPlugins/blocks.js @@ -2,12 +2,13 @@ module.exports=inject; function inject(serv,player) { - function changeBlock(position,blockType) + async function changeBlock(position,blockType) { - player.getOthers().forEach(function(player) { - player.sendBlock(position, blockType); + player.getOthers().forEach(function(p) { + if (p.world != player.world) return; + p.sendBlock(position, blockType); }); - return serv.world.setBlockType(position,blockType); + return await player.world.setBlockType(position,blockType); } function sendBlock(position, blockType) { // Call from serv.setBlock unless you want "local" fake blocks diff --git a/src/lib/playerPlugins/chest.js b/src/lib/playerPlugins/chest.js index 3cb72db..4c104aa 100644 --- a/src/lib/playerPlugins/chest.js +++ b/src/lib/playerPlugins/chest.js @@ -8,8 +8,8 @@ function inject(serv, player) var referencePosition=new vec3(packet.location.x,packet.location.y,packet.location.z); if (player.entity.crouching) return; try { - var id = await serv.world.getBlockType(referencePosition); - var blockAbove = await serv.world.getBlockType(referencePosition.clone().add(new vec3(0, 1, 0))); + var id = await player.world.getBlockType(referencePosition); + var blockAbove = await player.world.getBlockType(referencePosition.clone().add(new vec3(0, 1, 0))); if (id == 54) { if (blockAbove) { diff --git a/src/lib/playerPlugins/digging.js b/src/lib/playerPlugins/digging.js index d81bc32..db392f7 100644 --- a/src/lib/playerPlugins/digging.js +++ b/src/lib/playerPlugins/digging.js @@ -6,7 +6,7 @@ function inject(serv,player) { player._client.on("block_dig",function(packet){ var pos=new Vec3(packet.location); - serv.world.getBlock(pos) + player.world.getBlock(pos) .then(block => { currentlyDugBlock=block; if(currentlyDugBlock.type==0) return; diff --git a/src/lib/playerPlugins/login.js b/src/lib/playerPlugins/login.js index 12c5d26..e02f277 100644 --- a/src/lib/playerPlugins/login.js +++ b/src/lib/playerPlugins/login.js @@ -1,5 +1,4 @@ var Entity=require("prismarine-entity"); -var spiralloop = require('spiralloop'); var Vec3=require("vec3"); module.exports=inject; @@ -15,6 +14,7 @@ function inject(serv,player) player.entity.health = 20; player.entity.food = 20; player.view=8; + player.world=serv.overworld; player.username=player._client.username; serv.players.push(player); serv.uuidToPlayer[player._client.uuid] = player; @@ -40,61 +40,16 @@ function inject(serv,player) player.entity.position=toFixedPosition(player.spawnPoint); } - function spiral(arr) - { - var t=[]; - spiralloop(arr,function(x,z){ - t.push([x,z]); - }); - return t; - } - - - function sendChunk(chunkX,chunkZ,column) - { - player._client.write('map_chunk', { - x: chunkX, - z: chunkZ, - groundUp: true, - bitMap: 0xffff, - chunkData: column.dump() - }); - return Promise.resolve(); - } - - function sendChunksAroundPlayer(view) - { - player.lastPositionChunkUpdated=player.entity.position; - var playerChunkX=Math.floor(player.entity.position.x/16/32); - var playerChunkZ=Math.floor(player.entity.position.z/16/32); - return spiral([view*2,view*2]) - .map(t => ({ - chunkX:playerChunkX+t[0]-view, - chunkZ:playerChunkZ+t[1]-view - })) - .filter(({chunkX,chunkZ}) => { - var key=chunkX+","+chunkZ; - var loaded=player.loadedChunks[key]; - if(!loaded) player.loadedChunks[key]=1; - return !loaded; - }) - .reduce((acc,{chunkX,chunkZ})=> - acc - //.then(() => sleep(100)) - .then(() => serv.world.getColumn(chunkX,chunkZ)) - .then((column) => sendChunk(chunkX,chunkZ,column)) - ,Promise.resolve()); - } - function sendMap() { - return sendChunksAroundPlayer(3); + return player.sendNearbyChunks(3) + .catch((err) => setTimeout(function() { throw err; }), 0); } function sendRestMap() { player.sendingChunks=true; - sendChunksAroundPlayer(player.view) + player.sendNearbyChunks(player.view) .then(() => player.sendingChunks=false) .catch((err)=> setTimeout(function(){throw err;},0)); @@ -102,20 +57,19 @@ function inject(serv,player) if(!player.sendingChunks && player.entity.position.distanceTo(player.lastPositionChunkUpdated)>16*32) { player.sendingChunks=true; - sendChunksAroundPlayer(player.view) + player.sendNearbyChunks(player.view) .then(() => player.sendingChunks=false) .catch((err)=> setTimeout(function(){throw err;},0)); } }); + player.on("teleport", function() { + player.sendingChunks=true; + player.sendNearbyChunks(player.view) + .then(() => player.sendingChunks=false) + .catch((err)=> setTimeout(function(){throw err;},0)); + }); } - - function sleep(ms = 0) { - return new Promise(r => setTimeout(r, ms)); - } - - - function sendSpawnPosition() { console.log("setting spawn at "+player.spawnPoint); @@ -124,21 +78,6 @@ function inject(serv,player) }); } - function sendInitialPosition() - { - player.entity.position=toFixedPosition(player.spawnPoint); - player.entity.yaw=0; - player.entity.pitch=0; - player._client.write('position', { - x: player.entity.position.x/32, - y: player.entity.position.y/32, - z: player.entity.position.z/32, - yaw: player.entity.yaw, - pitch: player.entity.pitch, - flags: 0x00 - }); - } - function updateTime() { player._client.write('update_time', { @@ -182,41 +121,6 @@ function inject(serv,player) } - - function spawnOthers() - { - player.getOthers().forEach(function (otherPlayer) { - player._client.write('named_entity_spawn', { - entityId: otherPlayer.entity.id, - playerUUID: otherPlayer._client.uuid, - x: otherPlayer.entity.position.x, - y: otherPlayer.entity.position.y, - z: otherPlayer.entity.position.z, - yaw: otherPlayer.entity.yaw, - pitch: otherPlayer.entity.pitch, - currentItem: 0, - metadata: otherPlayer.entity.metadata - }); - }); - - } - - function spawn() - { - player._writeOthers('named_entity_spawn',{ - entityId: player.entity.id, - playerUUID: player._client.uuid, - x: player.entity.position.x, - y: player.entity.position.y, - z: player.entity.position.z, - yaw: player.entity.yaw, - pitch: player.entity.pitch, - currentItem: 0, - metadata: player.entity.metadata - }); - } - - function announceJoin() { serv.broadcast(player.username + ' joined the game.', "yellow"); @@ -238,7 +142,6 @@ function inject(serv,player) sendLogin(); await sendMap(); sendSpawnPosition(); - sendInitialPosition(); player.updateHealth(player.entity.health); player.emit("spawned"); @@ -246,9 +149,8 @@ function inject(serv,player) updateTime(); setGameMode(player.gameMode); fillTabList(); - spawnOthers(); - spawn(); + player.spawn(); announceJoin(); setTimeout(sendRestMap,100); @@ -256,6 +158,4 @@ function inject(serv,player) player.setGameMode=setGameMode; player.login=login; - player.sendInitialPosition=sendInitialPosition; - player.spawn=spawn; } \ No newline at end of file diff --git a/src/lib/playerPlugins/updatePositions.js b/src/lib/playerPlugins/updatePositions.js index 9190e68..63a0717 100644 --- a/src/lib/playerPlugins/updatePositions.js +++ b/src/lib/playerPlugins/updatePositions.js @@ -82,4 +82,24 @@ function inject(serv,player) player.entity.onGround = onGround; player.emit("positionChanged"); } + + function setPosition(pos, opt) { + opt = opt || {}; + if (pos) player.entity.position = toFixedPosition(pos); + if (typeof opt.yaw != 'undefined') player.entity.yaw=opt.yaw; + if (typeof opt.pitch != 'undefined') player.entity.pitch=opt.pitch; + + player._client.write('position', { + x: player.entity.position.x/32, + y: player.entity.position.y/32, + z: player.entity.position.z/32, + yaw: player.entity.yaw, + pitch: player.entity.pitch, + flags: 0x00 + }); + player.spawnForOthers(); + player.getNearbyPlayers(); + player.emit('teleport'); + } + player.setPosition = setPosition; } \ No newline at end of file diff --git a/src/lib/playerPlugins/world.js b/src/lib/playerPlugins/world.js new file mode 100644 index 0000000..ebc1831 --- /dev/null +++ b/src/lib/playerPlugins/world.js @@ -0,0 +1,124 @@ +var vec3=require("vec3"); +var spiralloop = require('spiralloop'); + +module.exports = inject; + +function inject(serv, player) { + function spawn() { + player.setPosition(player.spawnPoint, { yaw: 0, pitch: 0, exact: true }); + } + + function spawnForOthers() { + player._writeOthers('named_entity_spawn',{ // _writeOthersWithinDistance? + entityId: player.entity.id, + playerUUID: player._client.uuid, + x: player.entity.position.x, + y: player.entity.position.y, + z: player.entity.position.z, + yaw: player.entity.yaw, + pitch: player.entity.pitch, + currentItem: 0, + metadata: player.entity.metadata + }); + } + + function getNearbyPlayers() { + player.getOthers().forEach(function (otherPlayer) { + if (otherPlayer.world != player.world) return; // Also check distance from player? + player._client.write('named_entity_spawn', { + entityId: otherPlayer.entity.id, + playerUUID: otherPlayer._client.uuid, + x: otherPlayer.entity.position.x, + y: otherPlayer.entity.position.y, + z: otherPlayer.entity.position.z, + yaw: otherPlayer.entity.yaw, + pitch: otherPlayer.entity.pitch, + currentItem: 0, + metadata: otherPlayer.entity.metadata + }); + }); + } + + function sendChunk(chunkX,chunkZ,column) + { + player._client.write('map_chunk', { + x: chunkX, + z: chunkZ, + groundUp: true, + bitMap: 0xffff, + chunkData: column.dump() + }); + return Promise.resolve(); + } + + function spiral(arr) + { + var t=[]; + spiralloop(arr,function(x,z){ + t.push([x,z]); + }); + return t; + } + + function sendNearbyChunks(view, world) + { + world = world || player.world; + player.lastPositionChunkUpdated=player.entity.position; + var playerChunkX=Math.floor(player.entity.position.x/16/32); + var playerChunkZ=Math.floor(player.entity.position.z/16/32); + + return spiral([view*2,view*2]) + .map(t => ({ + chunkX:playerChunkX+t[0]-view, + chunkZ:playerChunkZ+t[1]-view + })) + .filter(({chunkX,chunkZ}) => { + var key=chunkX+","+chunkZ; + var loaded=player.loadedChunks[key]; + if(!loaded) player.loadedChunks[key]=1; + return !loaded; + }) + .reduce((acc,{chunkX,chunkZ})=> + acc + //.then(() => sleep(100)) + .then(() => world.getColumn(chunkX,chunkZ)) + .then((column) => player.sendChunk(chunkX,chunkZ,column)) + ,Promise.resolve()); + } + + function sleep(ms = 0) { + return new Promise(r => setTimeout(r, ms)); + } + + function changeWorld(world, opt) { + + opt = opt || {}; + player.world = world; + player.loadedChunks={}; + if (typeof opt.gamemode != 'undefined') player.gameMode = opt.gamemode; + player._client.write("respawn",{ + dimension: opt.dimension || 0, + difficulty: opt.difficulty || 0, + gamemode: opt.gamemode || player.gameMode, + levelType:'default' + }); + player.emit('change_world'); + player.setPosition(opt.position || player.spawnPoint, { yaw: opt.yaw || 0, pitch: opt.pitch || 0 }); // Automatically sends chunks around players + } + + player.spawn = spawn; + player.spawnForOthers = spawnForOthers; + player.getNearbyPlayers = getNearbyPlayers; + player.sendNearbyChunks = sendNearbyChunks; + player.changeWorld = changeWorld; + player.sendChunk = sendChunk; + + player.on('chat', function(message) { + if (message == 'world') { + player.changeWorld(serv.netherworld, { + position: new vec3(0, 60, 0), + dimension: -1 + }); + } + }); +} \ No newline at end of file diff --git a/src/lib/serverPlugins/daycycle.js b/src/lib/serverPlugins/daycycle.js index fd8f61e..528056e 100644 --- a/src/lib/serverPlugins/daycycle.js +++ b/src/lib/serverPlugins/daycycle.js @@ -2,22 +2,22 @@ module.exports = inject; function inject(serv, settings) { - serv.setTime = function(time) { - serv.time = time; - serv._writeAll('update_time', { - age: [0, 0], // TODO - time: [0, serv.time] - }); - } + serv.setTime = function(time) { + serv.time = time; + serv._writeAll('update_time', { + age: [0, 0], // TODO + time: [0, serv.time] + }); + } - serv.doDaylightCycle = true; + serv.doDaylightCycle = true; - serv.time = 0; + serv.time = 0; - serv.on('tick', function(count) { - if (!serv.doDaylightCycle) return; - if (count % 20 == 0) { + serv.on('tick', function(count) { + if (!serv.doDaylightCycle) return; + if (count % 20 == 0) { serv.setTime((serv.time + 20) % 24000); // Vanilla only does it every second - } - }) + } + }) } \ No newline at end of file diff --git a/src/lib/serverPlugins/tick.js b/src/lib/serverPlugins/tick.js index 6d50fbb..9088100 100644 --- a/src/lib/serverPlugins/tick.js +++ b/src/lib/serverPlugins/tick.js @@ -2,24 +2,24 @@ module.exports = inject; function inject(serv, settings) { - serv.setTickInterval = setTickInterval; - serv.stopTickInterval = stopTickInterval; - serv.tickCount = 0; + serv.setTickInterval = setTickInterval; + serv.stopTickInterval = stopTickInterval; + serv.tickCount = 0; - serv.setTickInterval(20); + serv.setTickInterval(20); } function setTickInterval(ticksPerSecond) { - var serv = this; - serv.stopTickInterval(); + var serv = this; + serv.stopTickInterval(); - serv.tickInterval = setInterval(function() { - serv.tickCount++; - serv.emit('tick', serv.tickCount); - }, 1000/ticksPerSecond); + serv.tickInterval = setInterval(function() { + serv.tickCount++; + serv.emit('tick', serv.tickCount); + }, 1000/ticksPerSecond); } function stopTickInterval() { - if (this.tickInterval) clearInterval(serv.tickInterval); - this.tickInterval = null; + if (this.tickInterval) clearInterval(serv.tickInterval); + this.tickInterval = null; } \ No newline at end of file diff --git a/src/lib/serverPlugins/world.js b/src/lib/serverPlugins/world.js index 918a476..ff56187 100644 --- a/src/lib/serverPlugins/world.js +++ b/src/lib/serverPlugins/world.js @@ -7,14 +7,32 @@ var generations={ 'grass_field':require("../worldGenerations/grass_field"), 'diamond_square':require("../worldGenerations/diamond_square"), 'superflat':require("../worldGenerations/superflat"), - 'all_the_blocks':require("../worldGenerations/all_the_blocks") + 'all_the_blocks':require("../worldGenerations/all_the_blocks"), + 'nether':require("../worldGenerations/nether") }; module.exports = inject; -function inject(serv,{regionFolder,generation={"name":"diamond_square","options":{"worldHeight":80}}}={}) { +function inject(serv,{generation={"name":"diamond_square","options":{"worldHeight":80}}}={}) { generation.options.seed=generation.options.seed || Math.random()*Math.pow(2, 32); serv.emit("seed",generation.options.seed); - serv.world = new World(generations[generation.name](generation.options),regionFolder); - serv._worldSync=new WorldSync(serv.world); -} + serv.overworld = new World(generations[generation.name](generation.options)); + serv.netherworld = new World(generations["nether"]({})); + //serv.endworld = new World(generations["end"]({})); + + //serv._worldSync=new WorldSync(serv.worlds[0]); + + function pregenWorld(world, size=10) { + for (var x = -size; x < size; x++) { + for (var z = -size; z < size; z++) { + world.getColumn(x, z); + } + } + } + serv.pregenWorld = pregenWorld; + + serv.pregenWorld(serv.overworld); + serv.log('Pre-Generated Overworld'); + serv.pregenWorld(serv.netherworld); + serv.log('Pre-Generated Nether'); +} \ No newline at end of file diff --git a/src/lib/worldGenerations/nether.js b/src/lib/worldGenerations/nether.js new file mode 100644 index 0000000..86f5d1b --- /dev/null +++ b/src/lib/worldGenerations/nether.js @@ -0,0 +1,31 @@ +var Chunk = require('prismarine-chunk')(require("../version")); +var Vec3 = require('vec3'); + +function generation({level=50}={}) { + function generateChunk(chunkX, chunkZ) { + var chunk=new Chunk(); + for (var x = 0; x < 16; x++) { + for (var z = 0; z < 16; z++) { + var bedrockheighttop = 1 + Math.round(Math.random()*3) + var bedrockheightbottom = 1 + Math.round(Math.random()*3) + for (var y = 0; y < 128; y++) { // Nether only goes up to 128 + let block; + let data; + + if (y < bedrockheightbottom) block = 7; + else if (y < 50) block = 87; + else if (y > 127 - bedrockheighttop) block = 7; + + var pos = new Vec3(x, y, z); + if (block) chunk.setBlockType(pos, block); + if (data) chunk.setBlockData(pos, data); + // Don't need to set light data in nether + } + } + } + return chunk; + } + return generateChunk; +} + +module.exports = generation; \ No newline at end of file