From 3ba46f0e97e24adc3dc479b46e85163e7c5553f2 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Sun, 22 Nov 2015 16:06:59 -0800 Subject: [PATCH 01/10] Adding behaviors --- src/lib/behavior.js | 7 ++- src/lib/plugins/animations.js | 12 ++-- src/lib/plugins/blocks.js | 9 ++- src/lib/plugins/chat.js | 17 ++++-- src/lib/plugins/daycycle.js | 7 ++- src/lib/plugins/digging.js | 18 +++--- src/lib/plugins/placeBlock.js | 7 ++- src/lib/plugins/pvp.js | 33 +++++++---- src/lib/plugins/respawn.js | 20 ++++--- src/lib/plugins/sound.js | 6 +- src/lib/plugins/updatePositions.js | 94 +++++++++++++++++------------- src/lib/plugins/world.js | 18 ++++-- 12 files changed, 159 insertions(+), 89 deletions(-) diff --git a/src/lib/behavior.js b/src/lib/behavior.js index 2ae13b4..b0f3ec2 100644 --- a/src/lib/behavior.js +++ b/src/lib/behavior.js @@ -1,20 +1,23 @@ module.exports = (obj) => { - return async (eventName, data, func, opt) => { + return async (eventName, data, func, cancelFunc) => { var hiddenCancelled = false; var cancelled = false; var cancelCount = 0; - var cancel = (hidden) => { // Hidden shouldn't be used often but it's not hard to implement so meh + var defaultCancel = true; + var cancel = (dC=true, hidden=false) => { // Hidden shouldn't be used often but it's not hard to implement so meh if (hidden) hiddenCancelled = true; else { cancelled = true; cancelCount++; } + defaultCancel = dC; } await obj.emit(eventName + '_cancel', data, cancel); await obj.emit(eventName, data, cancelled, cancelCount); if (!hiddenCancelled && !cancelled) await func(data); + else if (cancelFunc && defaultCancel) cancelFunc(data); await obj.emit(eventName + '_done', data, cancelled); } diff --git a/src/lib/plugins/animations.js b/src/lib/plugins/animations.js index 19f3a3f..db6818b 100644 --- a/src/lib/plugins/animations.js +++ b/src/lib/plugins/animations.js @@ -1,10 +1,14 @@ module.exports.player=function(player) { player._client.on("arm_animation", () => - player._writeOthersNearby("animation", { - entityId: player.id, - animation: 0 - })); + player.behavior('punch', {}, () => { + player._writeOthersNearby("animation", { + entityId: player.id, + animation: 0 + }); + }) + ); + player._client.on("entity_action", ({actionId} = {}) => { if(actionId == 3) { diff --git a/src/lib/plugins/blocks.js b/src/lib/plugins/blocks.js index 909673c..59e3603 100644 --- a/src/lib/plugins/blocks.js +++ b/src/lib/plugins/blocks.js @@ -13,10 +13,17 @@ module.exports.player=function(player,serv) }; player.sendBlock = (position, blockType, blockData) => // Call from player.setBlock unless you want "local" fake blocks - player._client.write("block_change",{ + player.behavior('sendBlock', { + position: position, + id: blockType, + damage: blockData + }, ({position, id, damage}) => { + player._client.write("block_change",{ location:position, type:blockType<<4 | blockData + }); }); + player.setBlock = (position,blockType,blockData) => serv.setBlock(player.world,position,blockType,blockData); diff --git a/src/lib/plugins/chat.js b/src/lib/plugins/chat.js index d5ea1c2..5e78e6f 100644 --- a/src/lib/plugins/chat.js +++ b/src/lib/plugins/chat.js @@ -11,12 +11,21 @@ module.exports.player=function(player,serv) { player._client.on('chat', ({message} = {}) => { if(message[0]=="/") { - var command = message.slice(1); - player.handleCommand(command); + player.behavior('command', { + message: message + }, ({message}) => { + var command = message.slice(1); + player.handleCommand(command); + }); } else { - serv.broadcast('<' + player.username + '>' + ' ' + message); - player.emit("chat",message); + player.behavior('chat', { + message: message, + broadcastMessage: '<' + player.username + '>' + ' ' + message, + broadcast: true + }, ({message, broadcast, broadcastMessage}) => { + if (broadcast) serv.broadcast(broadcastMessage); + }); } }); diff --git a/src/lib/plugins/daycycle.js b/src/lib/plugins/daycycle.js index 6048f86..6c9ffd5 100644 --- a/src/lib/plugins/daycycle.js +++ b/src/lib/plugins/daycycle.js @@ -14,7 +14,12 @@ module.exports.server=function(serv) { serv.on('tick', (delta,count) => { if (!serv.doDaylightCycle) return; if (count % 20 == 0) { - serv.setTime((serv.time + 20) % 24000); // Vanilla only does it every second + serv.behavior('changeTime', { + old: serv.time, + newTime: serv.time + 20 + }, ({newTime}) => { + serv.setTime((serv.time + 20) % 24000); // Vanilla only does it every second + }); } }) }; diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 378e28e..9f4f674 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -2,6 +2,11 @@ var Vec3 = require("vec3").Vec3; module.exports.player=function(player,serv) { + + function cancelDig({position, block}) { + player.sendBlock(position, block.id, block.data); + } + player._client.on("block_dig",({location,status} = {}) => { var pos=new Vec3(location.x,location.y,location.z); player.world.getBlock(pos) @@ -19,31 +24,30 @@ module.exports.player=function(player,serv) block: block }, ({position}) => { return startDigging(position); - }); + }, cancelDig); else if(status==2) player.behavior('finishDig', { // Finish dig survival position: position, block: block }, ({position}) => { return completeDigging(position); - }); + }, cancelDig); else if(status==1) player.behavior('cancelDig', { // Cancel dig survival position: position, block: block }, ({position}) => { return cancelDigging(position); - }); + }, cancelDig); else if(status==0 && player.gameMode==1) player.behavior('dig', { // Start/finish dig creative position: position, block: block }, ({position}) => { return creativeDigging(position); - }); - } - )} - ) + }, cancelDig); + }, cancelDig) + }) .catch((err)=> setTimeout(() => {throw err;},0)) }); diff --git a/src/lib/plugins/placeBlock.js b/src/lib/plugins/placeBlock.js index ab7e6df..e3a6c8b 100644 --- a/src/lib/plugins/placeBlock.js +++ b/src/lib/plugins/placeBlock.js @@ -26,7 +26,8 @@ module.exports.player=function(player,serv) position: placedPosition, reference: referencePosition, playSound: true, - sound: 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone') + sound: 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone'), + world: player.world }, ({direction, heldItem, position, reference, playSound, sound}) => { if (playSound) { serv.playSound(sound, player.world, placedPosition.clone().add(new Vec3(0.5, 0.5, 0.5)), { @@ -46,6 +47,10 @@ module.exports.player=function(player,serv) location:position }); } + }, async () => { + var id = await player.world.getBlockType(placedPosition); + var damage = await player.world.getBlockData(placedPosition); + player.sendBlock(placedPosition, id, damage); }); }); }; diff --git a/src/lib/plugins/pvp.js b/src/lib/plugins/pvp.js index 18262e8..a9d9f51 100644 --- a/src/lib/plugins/pvp.js +++ b/src/lib/plugins/pvp.js @@ -17,22 +17,31 @@ module.exports.player=function(player,serv) var attackedEntity = serv.entities[entityId]; if(!attackedEntity || (attackedEntity.gameMode != 0 && attackedEntity.type == 'player')) return; - attackedEntity.updateHealth(attackedEntity.health - 1); - serv.playSound('game.player.hurt', player.world, attackedEntity.position.scaled(1/32)); + player.behavior('attack', { + attackedEntity: attackedEntity, + sound: 'game.player.hurt', + playSound: true, + damage: 1, + velocity: attackedEntity.position.minus(player.position).plus(new Vec3(0, 0.5, 0)).scaled(5), + maxVelocity: new Vec3(4, 4, 4), + animation: true + }, ({entity, sound, playSound, damage, velocity, maxVelocity, animation}) => { + attackedEntity.updateHealth(attackedEntity.health - dealDamage); + serv.playSound(sound, player.world, attackedEntity.position.scaled(1/32)); - var attackVelocity = attackedEntity.position.minus(player.position).plus(new Vec3(0, 0.5, 0)).scaled(5/32); - attackedEntity.sendVelocity(attackVelocity, new Vec3(4, 4, 4)); + attackedEntity.sendVelocity(velocity.scaled(1/32), maxVelocity); - if(attackedEntity.health<=0) - attackedEntity._writeOthers('entity_status',{ + if(attackedEntity.health<=0 && animation) + attackedEntity._writeOthers('entity_status',{ + entityId:attackedEntity.id, + entityStatus:3 + }); + else if (animation) + attackedEntity._writeOthers('animation',{ entityId:attackedEntity.id, - entityStatus:3 + animation:1 }); - else - attackedEntity._writeOthers('animation',{ - entityId:attackedEntity.id, - animation:1 - }); + }); } player._client.on("use_entity", ({mouse,target} = {}) => { diff --git a/src/lib/plugins/respawn.js b/src/lib/plugins/respawn.js index bacc817..a1bdc89 100644 --- a/src/lib/plugins/respawn.js +++ b/src/lib/plugins/respawn.js @@ -2,16 +2,18 @@ module.exports.player=function(player) { player._client.on("client_command", ({payload}) => { if(payload == 0) { - player._client.write("respawn",{ - dimension:0, - difficulty:0, - gamemode:player.gameMode, - levelType:'default' + player.behavior('requestRespawn', {}, () => { + player._client.write("respawn",{ + dimension:0, + difficulty:0, + gamemode:player.gameMode, + levelType:'default' + }); + player.sendPosition(); + player.updateHealth(20); + player.nearbyEntities=[]; + player.updateAndSpawn(); }); - player.sendPosition(); - player.updateHealth(20); - player.nearbyEntities=[]; - player.updateAndSpawn(); } }); }; \ No newline at end of file diff --git a/src/lib/plugins/sound.js b/src/lib/plugins/sound.js index fd4f281..672861b 100644 --- a/src/lib/plugins/sound.js +++ b/src/lib/plugins/sound.js @@ -40,11 +40,12 @@ module.exports.player=function(player,serv) { serv.playSound(sound, player.world, null, opt); }; - player._client.on('block_place', ({location}={}) => { + player._client.on('placeBlock_cancel', ({location}={}, cancel) => { if (player.crouching) return; var pos=new Vec3(location.x,location.y,location.z); player.world.getBlockType(pos).then((id) => { if (id != 25) return; + cancel(); if (!player.world.blockEntityData[pos.toString()]) player.world.blockEntityData[pos.toString()] = {}; var data = player.world.blockEntityData[pos.toString()]; if (typeof data.note == 'undefined') data.note = -1; @@ -54,11 +55,12 @@ module.exports.player=function(player,serv) { }).catch((err)=> setTimeout(() => {throw err;},0)); }); - player._client.on('block_dig', ({location,status} = {}) => { + player._client.on('dig_cancel', ({location,status} = {}, cancel) => { if (status != 0 || player.gameMode == 1) return; var pos=new Vec3(location.x,location.y,location.z); player.world.getBlockType(pos).then((id) => { if (id != 25) return; + cancel(); if (!player.world.blockEntityData[pos.toString()]) player.world.blockEntityData[pos.toString()] = {}; var data = player.world.blockEntityData[pos.toString()]; if (typeof data.note == 'undefined') data.note = 0; diff --git a/src/lib/plugins/updatePositions.js b/src/lib/plugins/updatePositions.js index 749f41d..dba6c20 100644 --- a/src/lib/plugins/updatePositions.js +++ b/src/lib/plugins/updatePositions.js @@ -17,21 +17,29 @@ module.exports.player=function(player) } function sendLook(yaw,pitch,onGround) { - var convYaw=conv(yaw); - var convPitch=conv(pitch); - if (convYaw == player.yaw && convPitch == player.pitch) return; - player._writeOthersNearby("entity_look", { - entityId: player.id, - yaw: convYaw, - pitch: convPitch, + player.behavior('look', { + yaw: yaw, + pitch: pitch, onGround: onGround - }); - player.yaw = convYaw; - player.pitch = convPitch; - player.onGround = onGround; - player._writeOthersNearby("entity_head_rotation", { - entityId: player.id, - headYaw: convYaw + }, ({yaw, pitch, onGround}) => { + var convYaw=conv(yaw); + var convPitch=conv(pitch); + if (convYaw == player.yaw && convPitch == player.pitch) return; + player._writeOthersNearby("entity_look", { + entityId: player.id, + yaw: convYaw, + pitch: convPitch, + onGround: onGround + }); + player.yaw = convYaw; + player.pitch = convPitch; + player.onGround = onGround; + player._writeOthersNearby("entity_head_rotation", { + entityId: player.id, + headYaw: convYaw + }); + }, () => { + player.sendPosition(); }); } @@ -44,33 +52,39 @@ module.exports.player=function(player) }); function sendRelativePositionChange(newPosition, onGround) { - if (player.position.distanceTo(new Vec3(0, 0, 0)) != 0) { - var diff = newPosition.minus(player.position); - if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) - { - player._writeOthersNearby('entity_teleport', { - entityId:player.id, - x: newPosition.x, - y: newPosition.y, - z: newPosition.z, - yaw: player.yaw, - pitch: player.pitch, - onGround: onGround - }); + player.behavior('move', { + onGround: onGround, + position: newPosition + }, ({onGround, position}) => { + if (player.position.distanceTo(new Vec3(0, 0, 0)) != 0) { + var diff = newPosition.minus(player.position); + if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) + { + player._writeOthersNearby('entity_teleport', { + entityId:player.id, + x: newPosition.x, + y: newPosition.y, + z: newPosition.z, + yaw: player.yaw, + pitch: player.pitch, + onGround: onGround + }); + } + else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) { + player._writeOthersNearby('rel_entity_move', { + entityId: player.id, + dX: diff.x, + dY: diff.y, + dZ: diff.z, + onGround: onGround + }); + } } - else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) { - player._writeOthersNearby('rel_entity_move', { - entityId: player.id, - dX: diff.x, - dY: diff.y, - dZ: diff.z, - onGround: onGround - }); - } - } - player.position = newPosition; - player.onGround = onGround; - player.emit("positionChanged"); + player.position = newPosition; + player.onGround = onGround; + }, () => { + player.sendPosition(); + }); } player.sendPosition = () => { diff --git a/src/lib/plugins/world.js b/src/lib/plugins/world.js index 9fab63d..1804d58 100644 --- a/src/lib/plugins/world.js +++ b/src/lib/plugins/world.js @@ -70,14 +70,20 @@ module.exports.player=function(player,serv) { player.sendChunk = (chunkX,chunkZ,column) => { - player._client.write('map_chunk', { + return player.behavior('sendChunk', { x: chunkX, z: chunkZ, - groundUp: true, - bitMap: 0xffff, - chunkData: column.dump() - }); - return Promise.resolve(); + chunk: column + }, ({x, z, chunk}) => { + player._client.write('map_chunk', { + x: x, + z: z, + groundUp: true, + bitMap: 0xffff, + chunkData: chunk.dump() + }); + return Promise.resolve(); + }) }; function spiral(arr) From 93ab42152c8d8d954ab7fb4ac4e74058d6b07f14 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Sun, 22 Nov 2015 18:34:54 -0800 Subject: [PATCH 02/10] Add behaviors, edit docs a TON (YOU'RE WELCOME) --- doc/api.md | 446 +++++++++++++++++++++++++++-- src/lib/plugins/blocks.js | 2 +- src/lib/plugins/chat.js | 7 +- src/lib/plugins/digging.js | 68 ++--- src/lib/plugins/entities.js | 2 +- src/lib/plugins/external.js | 1 + src/lib/plugins/log.js | 2 +- src/lib/plugins/placeBlock.js | 3 +- src/lib/plugins/pvp.js | 6 +- src/lib/plugins/updatePositions.js | 47 +-- 10 files changed, 488 insertions(+), 96 deletions(-) diff --git a/doc/api.md b/doc/api.md index fdda9f9..04ff260 100644 --- a/doc/api.md +++ b/doc/api.md @@ -19,6 +19,7 @@ - [serv.time](#servtime) - [serv.tickCount](#servtickcount) - [serv.doDaylightCycle](#servdodaylightcycle) + - [serv.plugins](#servplugins) - [Events](#events) - ["error" (error)](#error-error) - ["clientError" (client,error)](#clienterror-clienterror) @@ -48,21 +49,70 @@ - [server._writeAll(packetName, packetFields)](#server_writeallpacketname-packetfields) - [server._writeArray(packetName, packetFields, playerArray)](#server_writearraypacketname-packetfields-playerarray) - [server._writeNearby(packetName, packetFields, loc)](#server_writenearbypacketname-packetfields-loc) - - [Player](#player) + - [Entity](#entity-1) - [Properties](#properties-1) - - [player.entity](#playerentity) + - [entity.id](#entityid) + - [entity.position](#entityposition) + - [entity.world](#entityworld) + - [entity.type](#entitytype) + - [entity.entityType](#entityentitytype) + - [entity.nearbyEntities](#entitynearbyentities) + - [entity.viewDistance](#entityviewdistance) + - [entity.health](#entityhealth) + - [entity.pitch](#entitypitch) + - [entity.headPitch](#entityheadpitch) + - [entity.yaw](#entityyaw) + - [entity.gravity](#entitygravity) + - [entity.terminalvelocity](#entityterminalvelocity) + - [entity.friction](#entityfriction) + - [entity.size](#entitysize) + - [entity.deathTime](#entitydeathtime) + - [entity.pickupTime](#entitypickuptime) + - [entity.bornTime](#entityborntime) + - [entity.itemId](#entityitemid) + - [entity.itemDamage](#entityitemdamage) + - [entity.metadata](#entitymetadata) + - [entity.nearbyEntities](#entitynearbyentities-1) + - [Events](#events-1) + - [Behaviors](#behaviors) + - [FORMAT](#format) + - ["move"](#move) + - [Methods](#methods-1) + - [entity.getData(pluginName)](#entitygetdatapluginname) + - [entity.getOthers()](#entitygetothers) + - [entity.getOtherPlayers()](#entitygetotherplayers) + - [entity.getNearby()](#entitygetnearby) + - [entity.getNearbyPlayers()](#entitygetnearbyplayers) + - [entity.nearbyPlayers()](#entitynearbyplayers) + - [Low level Methods](#low-level-methods) + - [entity._writeOthers(packetName, packetFields)](#entity_writeotherspacketname-packetfields) + - [entity._writeOthersNearby(packetName, packetFields)](#entity_writeothersnearbypacketname-packetfields) + - [Player](#player) + - [Properties](#properties-2) - [player.username](#playerusername) - [player.view](#playerview) - - [player.world](#playerworld) - - [player.nearbyPlayers](#playernearbyplayers) - - [Events](#events-1) + - [Events](#events-2) - ["connected"](#connected) - ["spawned"](#spawned) - ["disconnected"](#disconnected) - ["chat" (message)](#chat-message) - ["kicked" (kicker,reason)](#kicked-kickerreason) - ["positionChanged"](#positionchanged) - - [Methods](#methods-1) + - [Behaviors](#behaviors-1) + - ["move"](#move-1) + - ["look"](#look) + - ["chat"](#chat) + - ["command"](#command) + - ["punch"](#punch) + - ["sendBlock"](#sendblock) + - ["sendChunk"](#sendchunk) + - ["dig"](#dig) + - ["dug"](#dug) + - ["cancelDig"](#canceldig) + - ["placeBlock"](#placeblock) + - ["attack"](#attack) + - ["requestRespawn"](#requestrespawn) + - [Methods](#methods-2) - [player.login()](#playerlogin) - [player.ban(reason)](#playerbanreason) - [player.kick(reason)](#playerkickreason) @@ -83,8 +133,6 @@ - [Low level properties](#low-level-properties) - [player._client](#player_client) - [Low level methods](#low-level-methods-1) - - [player._writeOthers(packetName, packetFields)](#player_writeotherspacketname-packetfields) - - [player._writeOthersNearby(packetName, packetFields)](#player_writeothersnearbypacketname-packetfields) @@ -159,6 +207,10 @@ Best to use with modulo (e.g. Something every 10 seconds is `serv.tickCount % 20 Default `true`. If false, time will not automatically pass. +#### serv.plugins + +List of all plugins. Use serv.plugins[pluginName] to get a plugin's object and data. + ### Events #### "error" (error) @@ -293,13 +345,227 @@ Writes packet to every player in playerArray Writes packet to all players within distance of loc. loc has the same paramater as loc in server.getNearby() -## Player +## Entity + +Players are a type of entity, so they will have most of the attributes and methods ### Properties -#### player.entity +#### entity.id -The entity of the player, of type `Flying-squid.Entity` +ID of entity on server + +#### entity.position + +Current position (currently in fixed position (x32 what you'd expect) so do entity.position.scaled(1/32) to get normal position) + +#### entity.world + +World object entity is in + +#### entity.type + +Either "player", "mob", or "object" (currently) + +#### entity.entityType + +Sub-category of entity. For mobs, this is which mob (Zombie/Skeleton, etc). For objects, this is which object (Arrow/Dropped item, etc) + +#### entity.nearbyEntities + +Nearby entities to this entity + +#### entity.viewDistance + +How far away entities are loaded/unloaded (used for players ATM) + +#### entity.health + +How many half-hearts an entity has of health (e.g. Player has 20). Not really used for objects, only players and mobs. + +#### entity.pitch + +Pitch of entity (rotation sideways) + +#### entity.headPitch + +Pitch of entity's head + +#### entity.yaw + +Yaw of entity (rotation looking up and down) + +#### entity.gravity + +Gravity of entity (non-players) to calculate physics. + +#### entity.terminalvelocity + +Only applies to gravity, really. You can still apply a velocity larger than terminal velocity. + +#### entity.friction + +Decreases velocity when touching blocks + +#### entity.size + +Used to calculate collisions for server-side entities + +#### entity.deathTime + +How much time before an entity despawns (in ms) + +#### entity.pickupTime + +How long before an entity can be picked up (in ms) + +#### entity.bornTime + +When an entity was born. Used with pickupTime and deathTime (time in epoch) + +#### entity.itemId + +If a block drop, what item id + +#### entity.itemDamage + +If a block drop, what item damage + +#### entity.metadata + +Metadata for the entity (not like block metadata/damage). Contains stuff like NBT. + +#### entity.nearbyEntities + +List of entities that the entity believes is nearby. + +### Events + + + +### Behaviors + +Behaviors are very interesting. Let me explain to you how they work: + +Behaviors are a special type of event. They are editable and allow defaults to be cancellable making the powerful +for plugins to take control of and interact with each other. Three different events get called +for a behavior: +- EVENTNAME_cancel +- EVENTNAME +- EVENTNAME_done + +EVENTNAME_cancel passses the paramaters `data` (object of all info about behavior. Changing the data could have effects on outcome) and `cancel`, a function. This event is run before the default action. If `cancel()` is called, it will cancel the default action. More on this later. + +EVENTNAME passes `data` as well as `cancelled` so plugins can check if the default behavior has been cancelled. This is event is run +before the default action. + +EVENTNAME_done passes `data` and `cancelled`. This event is run before the default action. + +Example: One plugin wants to cancel a player's movement while another wants to say "HI" when they move + +Plugin A: +```js +player.on('move_cancel', ({position}, cancel) => { + cancel(); // If player tries to move, shoots them back where they came from +}); +``` + +Plugin B: +```js +player.on('move', ({position}, cancelled) => { + if (!cancelled) player.chat('HI!'); +}) +``` + +When a player normally moves, the server saves their position and sends it to all clients. Therefore, if a "move" behavior was truly cancelled, +the player would be able to move freely while the server and other players would see the player stationary. This doesn't happen because +behaviors can have "default cancel functions". In the case of a player's "move", the default cancel function sends them back where they +came from. To prevent this from happening, use the "preventDefaultCancel" paramater: cancel(false); + +Plugin C +```js +player.on('move_cancel', ({position}, cancel) => { + cancel(false); // Doesn't teleport player back +}); +``` + +If we keep Plugin B and replace Plugin A with Plugin C, we'll see that the player can move freely but will not recieve the +word "HI" and other players will be unable to see their movements. + +Finally, there is hidden cancel. This is the second paramater in cancel, and allows plugins to hide the fact that they cancelled +the default action from other plugins. It's best not to use this, but I know somebody will someday need this. + +Plugin D +```js +player.on('move_cancel', ({position}, cancel) => { + cancel(false, true); // Player doesn't teleport back and now "cancelled" will be false +}) +``` + +Using Plugin B and D together, the player will be able to move freely and will be spammed with "HI", however the server will not store +their position and other players will not see the player move. + +#### FORMAT + +Defition of behavior. +- var1: Variable with value, can be changed (default: defaultValue) +- var2 (u): Variable with value. You can change it however it will not have any effect on the defautl action (and could screw with other plugins, watch out!). U stands for unused + +Default: What happens if this isn't cancelled. + +Cancelled: What happens if this is cancelled and preventDefaultCancel is still false. + +#### "move" + +Emitted when server calculates new position for the entity (DOES NOT APPLY TO PLAYER!) +- old (u): Where the entity came from +- onGround (u): If the entity is on the ground + +Default: Send entity relative-move or teleport packets to all nearby players + +Cancelled: Set entity position to old position + +### Methods + +#### entity.getData(pluginName) + +Gets object that stores data, personalized per plugin. Returns null if plugin does not exist. + +Shortcut for: entity.pluginData[pluginName]; + +#### entity.getOthers() + +Get every other entity other than self + +#### entity.getOtherPlayers() + +Gets every player other than self (all players if entity is not a player) + +#### entity.getNearby() + +Gets all entities nearby (within entity.viewDistance) + +#### entity.getNearbyPlayers() + +Gets all nearby players regardless of what client thinks + +#### entity.nearbyPlayers() + +Gets all nearby players that client can see + +### Low level Methods + +#### entity._writeOthers(packetName, packetFields) + +Writes to all other players on server + +#### entity._writeOthersNearby(packetName, packetFields) + +Writes to all players within viewDistance + +## Player + +### Properties #### player.username @@ -309,14 +575,6 @@ The username of the player The view size of the player, for example 8 for 16x16 -#### player.world - -The world which the player is in. - -#### player.nearbyPlayers - -Nearby players. - ### Events #### "connected" @@ -343,6 +601,148 @@ Fires when the player says `message`. fires when the position changes in small amounts (walking, running, or flying) +### Behaviors + +See entity "Behaviors" for more info + +#### "move" + +When player tries to move +- position (u): New position player is trying to move to +- onGround (u): Whether player thinks they're on the ground or not + +Default: Save position/onGround and write to all nearby players + +Cancelled: Snap back to old position + +#### "look" + +When player tries to look somewhere +- yaw (u): New yaw player is looking +- pitch (u): New pitch player is looking +- onGround (u): If player thinks they're on the ground + +Default: Save look directions, send to all nearby players + +Cancelled: Snap their view back to old yaw and pitch + +#### "chat" + +Emitted when player tries to say something (unless they're message starts with /, then refer to "command") +- message (u): Message player sent +- broadcastMessage: What is put in server chat (Default: message) + +Default: Broadcasts to server their message + +Cancelled: Nothing + +#### "command" + +Emitted when player starts their message with a slash +- message: Their commands (includes the slash) + +Default: Handle command by command system + +Cancelled: Nothing + +#### "punch" + +When player tries to punch nothing + +Default: Send punch animation to nearby players + +Cancelled: Nothing + +#### "sendBlock" + +Emitted when sending a block to a player (block changed). This is separate for every player, cancelling this for one player causes ghost blocks! +- position: Position of the block +- id: ID of the block +- data: Metadata of the block + +Default: Send block change to player. + +Cancelled: Nothing + +#### "sendChunk" + +Emitted when sending a chunk to a player (loading it in) +- x: Chunk X +- z: Chunk Z +- chunk: Chunk data + +Default: Continue sending chunk to client + +Cancelled: Nothing + +#### "dig" + +Emitted when any player STARTS digging (i.e. survival only) +- position: Position of block being mined +- block (u): Block being mined + +Default: Allow player to start mining block, send changes in break animation to other players + +Cancelled: Stop them from digging + +#### "dug" + +Emitted when a player finishes digging something (or a player in creative breaks a block) +- position: Position of block dug +- block (u): Block dug + +Default: Save new block as air, sends to all nearby players + +Cancelled: Send to player the block that was there + +#### "cancelDig" + +Emitted when a player cancels digging in the middle (i.e. survival only) +- position: Position of block that was being mined +- block (u): Block that was being mined + +Default: Stop animation for all players, save stop digging + +Cancelled: Nothing + +#### "placeBlock" + +Emitted when a player places a block +- position: Position they're attempting to place the block +- id: Id of block being placed +- data: Data of block being placed +- reference (u): Reference block that was placed on +- direction (u): Direction vector from reference to position +- playSound: Which sound to play (Default: true) +- sound: Sound to play (Default: default sound for that material) + +Default: Place block for server and nearby players + +Cancelled: Replace block with old block for player + +#### "attack" + +Emitted when a player attacks an entity +- attackdEntity: Entity being attacked +- playSound: Play sound (Default: true) +- sound: Sound to play (default is game.player.hurt) +- damage: Damage to deal (default is based off player's weapon, player's potions, attackEntity's potions, and attackedEntity armor) +- velocity: Which way should attackedEntity move when hit +- maxVelocity: maxVelocity from consecutive hits +- animation: Play death/hit animation + +Default: Damage entity, play sound, send velocity, play animation for death/hit + +Cancelled: Nothing + +#### "requestRespawn" + +Emitted when a player tries to respawn + +Default: Let them respawn + +Cancelled: Nothing. You monster. + ### Methods #### player.login() @@ -430,10 +830,4 @@ The internal implementation to communicate with a client ### Low level methods -#### player._writeOthers(packetName, packetFields) - -write to other players than `player` the packet `packetName` with fields `packetFields` - -#### player._writeOthersNearby(packetName, packetFields) - -write to other players in same world that are within 150 blocks (see player.getNearby()) +Same as entity diff --git a/src/lib/plugins/blocks.js b/src/lib/plugins/blocks.js index 59e3603..efd5528 100644 --- a/src/lib/plugins/blocks.js +++ b/src/lib/plugins/blocks.js @@ -16,7 +16,7 @@ module.exports.player=function(player,serv) player.behavior('sendBlock', { position: position, id: blockType, - damage: blockData + data: blockData }, ({position, id, damage}) => { player._client.write("block_change",{ location:position, diff --git a/src/lib/plugins/chat.js b/src/lib/plugins/chat.js index 5e78e6f..1ddc98e 100644 --- a/src/lib/plugins/chat.js +++ b/src/lib/plugins/chat.js @@ -12,7 +12,7 @@ module.exports.player=function(player,serv) player._client.on('chat', ({message} = {}) => { if(message[0]=="/") { player.behavior('command', { - message: message + command: message }, ({message}) => { var command = message.slice(1); player.handleCommand(command); @@ -21,10 +21,9 @@ module.exports.player=function(player,serv) else { player.behavior('chat', { message: message, - broadcastMessage: '<' + player.username + '>' + ' ' + message, - broadcast: true + broadcastMessage: '<' + player.username + '>' + ' ' + message }, ({message, broadcast, broadcastMessage}) => { - if (broadcast) serv.broadcast(broadcastMessage); + serv.broadcast(broadcastMessage); }); } }); diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 9f4f674..56442d2 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -4,49 +4,43 @@ module.exports.player=function(player,serv) { function cancelDig({position, block}) { - player.sendBlock(position, block.id, block.data); + player.sendBlock(position, block.type, block.metadata); } player._client.on("block_dig",({location,status} = {}) => { var pos=new Vec3(location.x,location.y,location.z); player.world.getBlock(pos) .then(block => { - player.behavior('digPacket', { - position: pos, - status: status, - block: block - }, ({position,status,block}) => { - currentlyDugBlock=block; - if(currentlyDugBlock.type==0) return; - if(status==0 && player.gameMode!=1) - player.behavior('dig', { // Start dig survival - position: position, - block: block - }, ({position}) => { - return startDigging(position); - }, cancelDig); - else if(status==2) - player.behavior('finishDig', { // Finish dig survival - position: position, - block: block - }, ({position}) => { - return completeDigging(position); - }, cancelDig); - else if(status==1) - player.behavior('cancelDig', { // Cancel dig survival - position: position, - block: block - }, ({position}) => { - return cancelDigging(position); - }, cancelDig); - else if(status==0 && player.gameMode==1) - player.behavior('dig', { // Start/finish dig creative - position: position, - block: block - }, ({position}) => { - return creativeDigging(position); - }, cancelDig); - }, cancelDig) + currentlyDugBlock=block; + if(currentlyDugBlock.type==0) return; + if(status==0 && player.gameMode!=1) + player.behavior('dig', { // Start dig survival + position: position, + block: block + }, ({position}) => { + return startDigmehging(position); + }, cancelDig); + else if(status==2) + player.behavior('dug', { // Finish dig survival + position: position, + block: block + }, ({position}) => { + return completeDigging(position); + }, cancelDig); + else if(status==1) + player.behavior('cancelDig', { // Cancel dig survival + position: position, + block: block + }, ({position}) => { + return cancelDigging(position); + }); + else if(status==0 && player.gameMode==1) + player.behavior('dug', { // Start/finish dig creative + position: position, + block: block + }, ({position}) => { + return creativeDigging(position); + }, cancelDig); }) .catch((err)=> setTimeout(() => {throw err;},0)) }); diff --git a/src/lib/plugins/entities.js b/src/lib/plugins/entities.js index 3771561..25000ac 100644 --- a/src/lib/plugins/entities.js +++ b/src/lib/plugins/entities.js @@ -167,7 +167,7 @@ module.exports.entity=function(entity,serv){ }; - entity.on("positionChanged",() => { + entity.on("move",() => { if(entity.position.distanceTo(entity.lastPositionPlayersUpdated)>2*32) entity.updateAndSpawn(); }); diff --git a/src/lib/plugins/external.js b/src/lib/plugins/external.js index 09faa89..74c3893 100644 --- a/src/lib/plugins/external.js +++ b/src/lib/plugins/external.js @@ -19,6 +19,7 @@ module.exports.server = function(serv, settings) { } for (var p in settings.plugins) { + if (settings.plugins[p].disabled) continue; try { serv.addPlugin(p, require(p), settings.plugins[p]); } catch (err) { diff --git a/src/lib/plugins/log.js b/src/lib/plugins/log.js index 711c280..735e773 100644 --- a/src/lib/plugins/log.js +++ b/src/lib/plugins/log.js @@ -53,7 +53,7 @@ module.exports.player=function(player,serv) player.on("disconnected",() => serv.log("[INFO]: " + player.username + ' disconnected')); - player.on("chat", message => serv.log("[INFO] " + '<' + player.username + '>' + ' ' + message)); + player.on("chat", ({message}) => serv.log("[INFO] " + '<' + player.username + '>' + ' ' + message)); player.on("kicked",(kicker,reason) => serv.log(kicker.username + " kicked " + player.username + (reason ? " (" + reason + ")" : ""))); diff --git a/src/lib/plugins/placeBlock.js b/src/lib/plugins/placeBlock.js index e3a6c8b..4105e3a 100644 --- a/src/lib/plugins/placeBlock.js +++ b/src/lib/plugins/placeBlock.js @@ -22,12 +22,11 @@ module.exports.player=function(player,serv) direction: directionVector, heldItem: heldItem, id: heldItem.blockId, - damage: heldItem.itemDamage, + data: heldItem.itemDamage, position: placedPosition, reference: referencePosition, playSound: true, sound: 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone'), - world: player.world }, ({direction, heldItem, position, reference, playSound, sound}) => { if (playSound) { serv.playSound(sound, player.world, placedPosition.clone().add(new Vec3(0.5, 0.5, 0.5)), { diff --git a/src/lib/plugins/pvp.js b/src/lib/plugins/pvp.js index a9d9f51..78c5414 100644 --- a/src/lib/plugins/pvp.js +++ b/src/lib/plugins/pvp.js @@ -25,9 +25,9 @@ module.exports.player=function(player,serv) velocity: attackedEntity.position.minus(player.position).plus(new Vec3(0, 0.5, 0)).scaled(5), maxVelocity: new Vec3(4, 4, 4), animation: true - }, ({entity, sound, playSound, damage, velocity, maxVelocity, animation}) => { - attackedEntity.updateHealth(attackedEntity.health - dealDamage); - serv.playSound(sound, player.world, attackedEntity.position.scaled(1/32)); + }, ({attackedEntity, sound, playSound, damage, velocity, maxVelocity, animation}) => { + attackedEntity.updateHealth(attackedEntity.health - damage); + serv.playSound(sound, player.world, attackedEntity.position.scaled(1/32)); attackedEntity.sendVelocity(velocity.scaled(1/32), maxVelocity); diff --git a/src/lib/plugins/updatePositions.js b/src/lib/plugins/updatePositions.js index dba6c20..6f8f823 100644 --- a/src/lib/plugins/updatePositions.js +++ b/src/lib/plugins/updatePositions.js @@ -21,7 +21,7 @@ module.exports.player=function(player) yaw: yaw, pitch: pitch, onGround: onGround - }, ({yaw, pitch, onGround}) => { + }, () => { var convYaw=conv(yaw); var convPitch=conv(pitch); if (convYaw == player.yaw && convPitch == player.pitch) return; @@ -55,7 +55,7 @@ module.exports.player=function(player) player.behavior('move', { onGround: onGround, position: newPosition - }, ({onGround, position}) => { + }, () => { if (player.position.distanceTo(new Vec3(0, 0, 0)) != 0) { var diff = newPosition.minus(player.position); if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) @@ -101,26 +101,31 @@ module.exports.player=function(player) module.exports.entity=function(entity,serv){ entity.sendPosition = ({oldPos,onGround}) => { - var diff = entity.position.minus(oldPos); - - if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) - entity._writeOthersNearby('entity_teleport', { - entityId: entity.id, - x: entity.position.x, - y: entity.position.y, - z: entity.position.z, - yaw: entity.yaw, - pitch: entity.pitch, - onGround: onGround - }); - else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) serv._writeNearby('rel_entity_move', { - entityId: entity.id, - dX: diff.x, - dY: diff.y, - dZ: diff.z, + entity.behavior('move', { + old: oldPos, onGround: onGround - }, entity); + }, ({old,onGround}) => { + var diff = entity.position.minus(oldPos); - entity.emit('positionChanged', oldPos); + if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) + entity._writeOthersNearby('entity_teleport', { + entityId: entity.id, + x: entity.position.x, + y: entity.position.y, + z: entity.position.z, + yaw: entity.yaw, + pitch: entity.pitch, + onGround: onGround + }); + else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) serv._writeNearby('rel_entity_move', { + entityId: entity.id, + dX: diff.x, + dY: diff.y, + dZ: diff.z, + onGround: onGround + }, entity); + }, () => { + entity.position = oldPos; + }); }; }; \ No newline at end of file From de59be23ad177747b1f03cec370e76ec3d25e5d3 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Sun, 22 Nov 2015 19:31:00 -0800 Subject: [PATCH 03/10] Trying to fix sound, no success so far --- doc/api.md | 2 +- src/lib/plugins/chat.js | 5 ++--- src/lib/plugins/digging.js | 8 ++++---- src/lib/plugins/sound.js | 33 +++++++++++++++------------------ 4 files changed, 22 insertions(+), 26 deletions(-) diff --git a/doc/api.md b/doc/api.md index 04ff260..c6a8c3b 100644 --- a/doc/api.md +++ b/doc/api.md @@ -639,7 +639,7 @@ Cancelled: Nothing #### "command" Emitted when player starts their message with a slash -- message: Their commands (includes the slash) +- command: Their commands (excludes the slash) Default: Handle command by command system diff --git a/src/lib/plugins/chat.js b/src/lib/plugins/chat.js index 1ddc98e..c2f3c9d 100644 --- a/src/lib/plugins/chat.js +++ b/src/lib/plugins/chat.js @@ -12,9 +12,8 @@ module.exports.player=function(player,serv) player._client.on('chat', ({message} = {}) => { if(message[0]=="/") { player.behavior('command', { - command: message - }, ({message}) => { - var command = message.slice(1); + command: message.slice(1) + }, ({command}) => { player.handleCommand(command); }); } diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 56442d2..2893b44 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -15,28 +15,28 @@ module.exports.player=function(player,serv) if(currentlyDugBlock.type==0) return; if(status==0 && player.gameMode!=1) player.behavior('dig', { // Start dig survival - position: position, + position: pos, block: block }, ({position}) => { return startDigmehging(position); }, cancelDig); else if(status==2) player.behavior('dug', { // Finish dig survival - position: position, + position: pos, block: block }, ({position}) => { return completeDigging(position); }, cancelDig); else if(status==1) player.behavior('cancelDig', { // Cancel dig survival - position: position, + position: pos, block: block }, ({position}) => { return cancelDigging(position); }); else if(status==0 && player.gameMode==1) player.behavior('dug', { // Start/finish dig creative - position: position, + position: pos, block: block }, ({position}) => { return creativeDigging(position); diff --git a/src/lib/plugins/sound.js b/src/lib/plugins/sound.js index 672861b..44f5dad 100644 --- a/src/lib/plugins/sound.js +++ b/src/lib/plugins/sound.js @@ -40,31 +40,28 @@ module.exports.player=function(player,serv) { serv.playSound(sound, player.world, null, opt); }; - player._client.on('placeBlock_cancel', ({location}={}, cancel) => { + player.on('placeBlock_cancel', async ({position, reference}, cancel) => { if (player.crouching) return; - var pos=new Vec3(location.x,location.y,location.z); - player.world.getBlockType(pos).then((id) => { - if (id != 25) return; - cancel(); - if (!player.world.blockEntityData[pos.toString()]) player.world.blockEntityData[pos.toString()] = {}; - var data = player.world.blockEntityData[pos.toString()]; - if (typeof data.note == 'undefined') data.note = -1; - data.note++; - data.note %= 25; - serv.playNoteBlock(data.note, player.world, pos); - }).catch((err)=> setTimeout(() => {throw err;},0)); + var id = await player.world.getBlockType(reference); + if (id != 25) return; + cancel(false); + if (!player.world.blockEntityData[reference.toString()]) player.world.blockEntityData[reference.toString()] = {}; + var data = player.world.blockEntityData[reference.toString()]; + if (typeof data.note == 'undefined') data.note = -1; + data.note++; + data.note %= 25; + serv.playNoteBlock(data.note, player.world, reference); }); - player._client.on('dig_cancel', ({location,status} = {}, cancel) => { + player.on('dig_cancel', async ({position, reference}, cancel) => { if (status != 0 || player.gameMode == 1) return; - var pos=new Vec3(location.x,location.y,location.z); - player.world.getBlockType(pos).then((id) => { + return player.world.getBlockType(reference).then((id) => { if (id != 25) return; cancel(); - if (!player.world.blockEntityData[pos.toString()]) player.world.blockEntityData[pos.toString()] = {}; - var data = player.world.blockEntityData[pos.toString()]; + if (!player.world.blockEntityData[reference.toString()]) player.world.blockEntityData[reference.toString()] = {}; + var data = player.world.blockEntityData[reference.toString()]; if (typeof data.note == 'undefined') data.note = 0; - serv.playNoteBlock(data.not,player.world, pos, data.note); + serv.playNoteBlock(data.not,player.world, reference, data.note); }).catch((err)=> setTimeout(() => {throw err;},0)); }); From fa99c915511968a490bb5e2edad835c5d67dbaec Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 09:25:59 -0800 Subject: [PATCH 04/10] Small fixes --- TODO.md | 2 +- doc/api.md | 2 +- src/lib/plugins/digging.js | 2 +- src/lib/plugins/sound.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index b2b5b3c..6974ddf 100644 --- a/TODO.md +++ b/TODO.md @@ -20,11 +20,11 @@ This is a list that will keep the developers and the community organized! - [x] Other dimensions - [x] Block dropping - [x] Entity basics + - [x] Plugin System ### To-do #### Priority - [ ] Inventories (saving the blocks players get) - - [ ] Plugin System - [ ] Crafting and Smelting - [ ] Add in cluster functionality diff --git a/doc/api.md b/doc/api.md index c6a8c3b..38fdb11 100644 --- a/doc/api.md +++ b/doc/api.md @@ -723,7 +723,7 @@ Cancelled: Replace block with old block for player #### "attack" Emitted when a player attacks an entity -- attackdEntity: Entity being attacked +- attackedEntity: Entity being attacked - playSound: Play sound (Default: true) - sound: Sound to play (default is game.player.hurt) - damage: Damage to deal (default is based off player's weapon, player's potions, attackEntity's potions, and attackedEntity armor) diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 2893b44..a7c8832 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -18,7 +18,7 @@ module.exports.player=function(player,serv) position: pos, block: block }, ({position}) => { - return startDigmehging(position); + return startDigging(position); }, cancelDig); else if(status==2) player.behavior('dug', { // Finish dig survival diff --git a/src/lib/plugins/sound.js b/src/lib/plugins/sound.js index 44f5dad..2961543 100644 --- a/src/lib/plugins/sound.js +++ b/src/lib/plugins/sound.js @@ -57,7 +57,7 @@ module.exports.player=function(player,serv) { if (status != 0 || player.gameMode == 1) return; return player.world.getBlockType(reference).then((id) => { if (id != 25) return; - cancel(); + cancel(false); if (!player.world.blockEntityData[reference.toString()]) player.world.blockEntityData[reference.toString()] = {}; var data = player.world.blockEntityData[reference.toString()]; if (typeof data.note == 'undefined') data.note = 0; From a68b10bfa016fee013f0a179d1cd609b716827e3 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 11:23:50 -0800 Subject: [PATCH 05/10] Change to async emits --- src/index.js | 1 + src/lib/behavior.js | 10 +++++----- src/lib/plugins/sound.js | 18 ++++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/index.js b/src/index.js index 8cc72f0..5978229 100644 --- a/src/index.js +++ b/src/index.js @@ -3,6 +3,7 @@ var EventEmitter = require('events').EventEmitter; var path = require('path'); var requireIndex = require('requireindex'); var plugins = requireIndex(path.join(__dirname, 'lib', 'plugins')); +require('emit-then').register(); if (process.env.NODE_ENV === 'dev'){ require('longjohn'); } diff --git a/src/lib/behavior.js b/src/lib/behavior.js index b0f3ec2..118bbd7 100644 --- a/src/lib/behavior.js +++ b/src/lib/behavior.js @@ -13,12 +13,12 @@ module.exports = (obj) => { defaultCancel = dC; } - await obj.emit(eventName + '_cancel', data, cancel); - await obj.emit(eventName, data, cancelled, cancelCount); + await obj.emitThen(eventName + '_cancel', data, cancel).catch((err)=> setTimeout(() => {throw err;},0)); + await obj.emitThen(eventName, data, cancelled, cancelCount).catch((err)=> setTimeout(() => {throw err;},0)); - if (!hiddenCancelled && !cancelled) await func(data); - else if (cancelFunc && defaultCancel) cancelFunc(data); + if (!hiddenCancelled && !cancelled) await func(data).catch((err)=> setTimeout(() => {throw err;},0)); + else if (cancelFunc && defaultCancel) await cancelFunc(data).catch((err)=> setTimeout(() => {throw err;},0)); - await obj.emit(eventName + '_done', data, cancelled); + await obj.emitThen(eventName + '_done', data, cancelled).catch((err)=> setTimeout(() => {throw err;},0)); } } \ No newline at end of file diff --git a/src/lib/plugins/sound.js b/src/lib/plugins/sound.js index 2961543..1c91f20 100644 --- a/src/lib/plugins/sound.js +++ b/src/lib/plugins/sound.js @@ -53,16 +53,14 @@ module.exports.player=function(player,serv) { serv.playNoteBlock(data.note, player.world, reference); }); - player.on('dig_cancel', async ({position, reference}, cancel) => { - if (status != 0 || player.gameMode == 1) return; - return player.world.getBlockType(reference).then((id) => { - if (id != 25) return; - cancel(false); - if (!player.world.blockEntityData[reference.toString()]) player.world.blockEntityData[reference.toString()] = {}; - var data = player.world.blockEntityData[reference.toString()]; - if (typeof data.note == 'undefined') data.note = 0; - serv.playNoteBlock(data.not,player.world, reference, data.note); - }).catch((err)=> setTimeout(() => {throw err;},0)); + player.on('dig_cancel', async ({position}, cancel) => { + var id = await player.world.getBlockType(position); + if (id != 25) return; + cancel(false); + if (!player.world.blockEntityData[position.toString()]) player.world.blockEntityData[position.toString()] = {}; + var data = player.world.blockEntityData[position.toString()]; + if (typeof data.note == 'undefined') data.note = 0; + serv.playNoteBlock(data.note ,player.world, position); }); From 565309846b7787904249cf6de6b6dc9994d86f4e Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 13:54:23 -0800 Subject: [PATCH 06/10] Fix packages, add catch to commands --- package.json | 5 +++-- src/lib/plugins/commands.js | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index af024c5..2a00b2f 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "dependencies": { "babel-runtime": "^5.4.4", + "emit-then": "^1.0.2", "minecraft-data": "0.7.0", "minecraft-protocol": "0.16.2", "mkdirp": "0.5.1", @@ -32,10 +33,10 @@ "prismarine-block": "0.1.0", "prismarine-chunk": "0.2.1", "prismarine-entity": "0.1.0", - "prismarine-world": "0.3.3", - "prismarine-world-sync": "0.1.0", "prismarine-item": "0.0.0", "prismarine-windows": "0.0.0", + "prismarine-world": "0.3.3", + "prismarine-world-sync": "0.1.0", "random-seed": "^0.2.0", "request-promise": "^0.4.3", "requireindex": "~1.0.0", diff --git a/src/lib/plugins/commands.js b/src/lib/plugins/commands.js index c0f26cb..009a88e 100644 --- a/src/lib/plugins/commands.js +++ b/src/lib/plugins/commands.js @@ -75,6 +75,6 @@ module.exports.player=function(player) { player.handleCommand = (str) => { - player.commands.use(str); + player.commands.use(str).catch((err)=> setTimeout(() => {throw err;},0)); }; }; From 6a9281c1953703d728873efc53614dc63860455d Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 16:12:51 -0800 Subject: [PATCH 07/10] Add more to "dug" behavior and add "breakAnimation" behavior --- doc/api.md | 19 ++++++++++ src/lib/plugins/digging.js | 76 +++++++++++++++++++++++++------------- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/doc/api.md b/doc/api.md index 38fdb11..08978bd 100644 --- a/doc/api.md +++ b/doc/api.md @@ -690,6 +690,12 @@ Cancelled: Stop them from digging Emitted when a player finishes digging something (or a player in creative breaks a block) - position: Position of block dug - block (u): Block dug +- dropBlock: Should it drop a block object (Default: false in creative, otherwise true) +- blockDropPosition: Where block is dropped (Default: center of block) +- blockDropWorld: World block is dropped in (Default is the world the player/block is in) +- blockDropVelocity: The velocity the block has when dropped (Default: random) +- blockDropId: ID of the block dropped +- blockDropDamage: Damage of the block dropped Default: Save new block as air, sends to all nearby players @@ -705,6 +711,19 @@ Default: Stop animation for all players, save stop digging Cancelled: Nothing +##### "breakAnimation" + +Emitted when the server believes the break animation should increase (not sent by client!) +- position: Position of block being updated +- state: New state being changed to +- lastState (u): Last state of block +- start (u): When mining started +- timePassed (u): How long between start and now + +Default: Send animation to everyone + +Cancelled: Nothing + #### "placeBlock" Emitted when a player places a block diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index a7c8832..47ba08c 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -21,12 +21,7 @@ module.exports.player=function(player,serv) return startDigging(position); }, cancelDig); else if(status==2) - player.behavior('dug', { // Finish dig survival - position: pos, - block: block - }, ({position}) => { - return completeDigging(position); - }, cancelDig); + completeDigging(position); else if(status==1) player.behavior('cancelDig', { // Cancel dig survival position: pos, @@ -35,12 +30,7 @@ module.exports.player=function(player,serv) return cancelDigging(position); }); else if(status==0 && player.gameMode==1) - player.behavior('dug', { // Start/finish dig creative - position: pos, - block: block - }, ({position}) => { - return creativeDigging(position); - }, cancelDig); + return creativeDigging(position); }) .catch((err)=> setTimeout(() => {throw err;},0)) }); @@ -73,12 +63,20 @@ module.exports.player=function(player,serv) newDestroyState=newDestroyState>9 ? 9 : newDestroyState; if(newDestroyState!=lastDestroyState) { - lastDestroyState=newDestroyState; - player._writeOthersNearby("block_break_animation",{ - "entityId":currentAnimationId, - "location":location, - "destroyStage":newDestroyState - }); + player.behavior('breakAnimation', { + lastState: lastDestroyState, + state: newDestroyState, + start: startDigging, + timePassed: currentDiggingTime, + position: location + }, ({lastState, state}) => { + lastDestroyState=state; + player._writeOthersNearby("block_break_animation",{ + "entityId":currentAnimationId, + "location":location, + "destroyStage":state + }); + }) } } } @@ -98,13 +96,19 @@ module.exports.player=function(player,serv) clearInterval(animationInterval); var diggingTime=new Date()-startDiggingTime; if(expectedDiggingTime-diggingTime<100) { - player.changeBlock(location,0,0); - // Drop block - serv.spawnObject(2, player.world, location.offset(0.5, 0.5, 0.5), { - velocity: new Vec3(Math.random()*4 - 2, Math.random()*2 + 2, Math.random()*4 - 2), - itemId: currentlyDugBlock.type, - itemDamage: currentlyDugBlock.metadata - }); + player.behavior('dug', { + position: location, + block: currentlyDugBlock, + dropBlock: true, + blockDropPosition: location.offset(0.5, 0.5, 0.5), + blockDropWorld: player.world, + blockDropVelocity: new Vec3(Math.random()*4 - 2, Math.random()*2 + 2, Math.random()*4 - 2), + blockDropId: currentlyDugBlock.type, + blockDropDamage: currentlyDugBlock.metadata + }, (data) => { + player.changeBlock(data.position,0,0); + if (data.dropBlock) dropBlock(data); + }) } else { @@ -115,10 +119,30 @@ module.exports.player=function(player,serv) } } + function dropBlock({blockDropPosition, blockDropWorld, blockDropVelocity, blockDropId, blockDropDamage}) { + serv.spawnObject(2, blockDropWorld, blockDropPosition, { + velocity: blockDropVelocity, + itemId: blockDropId, + itemDamage: blockDropDamage + }); + } + function creativeDigging(location) { - return player.changeBlock(location,0,0); + player.behavior('dug', { + position: location, + block: currentlyDugBlock, + dropBlock: true, + blockDropPosition: location.offset(0.5, 0.5, 0.5), + blockDropWorld: player.world, + blockDropVelocity: new Vec3(Math.random()*4 - 2, Math.random()*2 + 2, Math.random()*4 - 2), + blockDropId: currentlyDugBlock.type, + blockDropDamage: currentlyDugBlock.metadata + }, (data) => { + player.changeBlock(data.position,0,0); + if (data.dropBlock) dropBlock(data); + }); } }; \ No newline at end of file From 0355195cd9fa6bd4458e19a245baf218ce92ba16 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 16:27:35 -0800 Subject: [PATCH 08/10] No more crashes, update API, add more stuff --- doc/api.md | 3 +++ src/lib/plugins/digging.js | 20 +++++++++++++------- src/lib/plugins/entities.js | 6 +++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/doc/api.md b/doc/api.md index 08978bd..8cb8012 100644 --- a/doc/api.md +++ b/doc/api.md @@ -109,6 +109,7 @@ - ["dig"](#dig) - ["dug"](#dug) - ["cancelDig"](#canceldig) + - ["breakAnimation"](#breakanimation) - ["placeBlock"](#placeblock) - ["attack"](#attack) - ["requestRespawn"](#requestrespawn) @@ -696,6 +697,8 @@ Emitted when a player finishes digging something (or a player in creative breaks - blockDropVelocity: The velocity the block has when dropped (Default: random) - blockDropId: ID of the block dropped - blockDropDamage: Damage of the block dropped +- blockDropPickup: Time before user can pick up the block (Default: 0.5 seconds) +- blockDropDeath: Time before item despawns (Default: 5 minutes) Default: Save new block as air, sends to all nearby players diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 47ba08c..5e0b29d 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -21,7 +21,7 @@ module.exports.player=function(player,serv) return startDigging(position); }, cancelDig); else if(status==2) - completeDigging(position); + completeDigging(pos); else if(status==1) player.behavior('cancelDig', { // Cancel dig survival position: pos, @@ -30,7 +30,7 @@ module.exports.player=function(player,serv) return cancelDigging(position); }); else if(status==0 && player.gameMode==1) - return creativeDigging(position); + return creativeDigging(pos); }) .catch((err)=> setTimeout(() => {throw err;},0)) }); @@ -104,7 +104,9 @@ module.exports.player=function(player,serv) blockDropWorld: player.world, blockDropVelocity: new Vec3(Math.random()*4 - 2, Math.random()*2 + 2, Math.random()*4 - 2), blockDropId: currentlyDugBlock.type, - blockDropDamage: currentlyDugBlock.metadata + blockDropDamage: currentlyDugBlock.metadata, + blockDropPickup: 500, + blockDropDeath: 60*5*1000 }, (data) => { player.changeBlock(data.position,0,0); if (data.dropBlock) dropBlock(data); @@ -119,11 +121,13 @@ module.exports.player=function(player,serv) } } - function dropBlock({blockDropPosition, blockDropWorld, blockDropVelocity, blockDropId, blockDropDamage}) { + function dropBlock({blockDropPosition, blockDropWorld, blockDropVelocity, blockDropId, blockDropDamage, blockDropPickup, blockDropDeath}) { serv.spawnObject(2, blockDropWorld, blockDropPosition, { velocity: blockDropVelocity, itemId: blockDropId, - itemDamage: blockDropDamage + itemDamage: blockDropDamage, + pickupTime: blockDropPickup, + deathTime: blockDropDeath }); } @@ -133,12 +137,14 @@ module.exports.player=function(player,serv) player.behavior('dug', { position: location, block: currentlyDugBlock, - dropBlock: true, + dropBlock: false, blockDropPosition: location.offset(0.5, 0.5, 0.5), blockDropWorld: player.world, blockDropVelocity: new Vec3(Math.random()*4 - 2, Math.random()*2 + 2, Math.random()*4 - 2), blockDropId: currentlyDugBlock.type, - blockDropDamage: currentlyDugBlock.metadata + blockDropDamage: currentlyDugBlock.metadata, + blockDropPickup: 500, + blockDropDeath: 60*5*1000 }, (data) => { player.changeBlock(data.position,0,0); if (data.dropBlock) dropBlock(data); diff --git a/src/lib/plugins/entities.js b/src/lib/plugins/entities.js index 25000ac..f1638fa 100644 --- a/src/lib/plugins/entities.js +++ b/src/lib/plugins/entities.js @@ -25,7 +25,7 @@ module.exports.server=function(serv,options) { return entity; }; - serv.spawnObject = (type, world, position, {pitch=0,yaw=0,velocity=new Vec3(0,0,0),data=1,itemId,itemDamage=0}={}) => { + serv.spawnObject = (type, world, position, {pitch=0,yaw=0,velocity=new Vec3(0,0,0),data=1,itemId,itemDamage=0,pickupTime=500,deathTime=60*1000}={}) => { var object = serv.initEntity('object', type, world, position.scaled(32).floored()); object.data = data; object.velocity = velocity.scaled(32).floored(); @@ -35,8 +35,8 @@ module.exports.server=function(serv,options) { object.terminalvelocity = new Vec3(27*32, 27*32, 27*32); object.friction = new Vec3(15*32, 0, 15*32); object.size = new Vec3(0.25*32, 0.25*32, 0.25*32); // Hardcoded, will be dependent on type! - object.deathTime = 60*1000; // 60 seconds - object.pickupTime = 200; + object.deathTime = deathTime; + object.pickupTime = pickupTime; object.itemId = itemId; object.itemDamage = itemDamage; From 483269db4c0bd0562526ce5b913081504d74ba69 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Mon, 23 Nov 2015 19:27:35 -0800 Subject: [PATCH 09/10] Added forceCancelDig and some other stuff --- doc/api.md | 10 +++++++++- src/lib/behavior.js | 1 + src/lib/plugins/digging.js | 14 ++++++++++++-- src/lib/plugins/entities.js | 3 ++- src/lib/plugins/external.js | 7 +++++-- src/lib/plugins/placeBlock.js | 6 +++--- 6 files changed, 32 insertions(+), 9 deletions(-) diff --git a/doc/api.md b/doc/api.md index 8cb8012..0ed2b98 100644 --- a/doc/api.md +++ b/doc/api.md @@ -109,6 +109,7 @@ - ["dig"](#dig) - ["dug"](#dug) - ["cancelDig"](#canceldig) + - ["forceCancelDig"](#forcecanceldig) - ["breakAnimation"](#breakanimation) - ["placeBlock"](#placeblock) - ["attack"](#attack) @@ -714,6 +715,13 @@ Default: Stop animation for all players, save stop digging Cancelled: Nothing +#### "forceCancelDig" + +Emitted when the server cancels a dig (currently only happens if the player mines too fast) +- stop: Whether the digging should be cancelled because they mined too fast (Default: true) +- start (u): Time mining started +- time (u): How long the player has been mining + ##### "breakAnimation" Emitted when the server believes the break animation should increase (not sent by client!) @@ -732,7 +740,7 @@ Cancelled: Nothing Emitted when a player places a block - position: Position they're attempting to place the block - id: Id of block being placed -- data: Data of block being placed +- damage: Data of block being placed - reference (u): Reference block that was placed on - direction (u): Direction vector from reference to position - playSound: Which sound to play (Default: true) diff --git a/src/lib/behavior.js b/src/lib/behavior.js index 118bbd7..e9a2b8e 100644 --- a/src/lib/behavior.js +++ b/src/lib/behavior.js @@ -20,5 +20,6 @@ module.exports = (obj) => { else if (cancelFunc && defaultCancel) await cancelFunc(data).catch((err)=> setTimeout(() => {throw err;},0)); await obj.emitThen(eventName + '_done', data, cancelled).catch((err)=> setTimeout(() => {throw err;},0)); + return data; } } \ No newline at end of file diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 5e0b29d..d9f7a62 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -95,7 +95,16 @@ module.exports.player=function(player,serv) { clearInterval(animationInterval); var diggingTime=new Date()-startDiggingTime; + var stop = false; if(expectedDiggingTime-diggingTime<100) { + stop = true; + stop = player.behavior('forceCancelDig', { + stop: true, + start: startDiggingTime, + time: diggingTime + }).stop; + } + if(!stop) { player.behavior('dug', { position: location, block: currentlyDugBlock, @@ -109,8 +118,9 @@ module.exports.player=function(player,serv) blockDropDeath: 60*5*1000 }, (data) => { player.changeBlock(data.position,0,0); + console.log('dropping',data.dropBlock); if (data.dropBlock) dropBlock(data); - }) + }, cancelDig) } else { @@ -148,7 +158,7 @@ module.exports.player=function(player,serv) }, (data) => { player.changeBlock(data.position,0,0); if (data.dropBlock) dropBlock(data); - }); + }, cancelDig); } }; \ No newline at end of file diff --git a/src/lib/plugins/entities.js b/src/lib/plugins/entities.js index f1638fa..4fa85e3 100644 --- a/src/lib/plugins/entities.js +++ b/src/lib/plugins/entities.js @@ -259,7 +259,8 @@ module.exports.entity=function(entity,serv){ entity.collect = (collectEntity) => { if (entity.type != 'player'){ - serv.emit('error', 'Non-player entity (ttype ' + entity.type + ') cannot collect another entity') + console.log('[ERROR] Non-player entity (type ' + entity.type + ') cannot collect another entity'); + console.log((new Error()).stack); return; } diff --git a/src/lib/plugins/external.js b/src/lib/plugins/external.js index 74c3893..c9a3109 100644 --- a/src/lib/plugins/external.js +++ b/src/lib/plugins/external.js @@ -1,3 +1,5 @@ +var fs = require('fs'); + module.exports.server = function(serv, settings) { serv.plugins = {}; serv.pluginCount = 0; @@ -23,11 +25,12 @@ module.exports.server = function(serv, settings) { try { serv.addPlugin(p, require(p), settings.plugins[p]); } catch (err) { - try { - serv.addPlugin(p, require('../../plugins/' + p), settings.plugins[p]); + try { // Throw error if cannot find plugin + fs.accessSync('./dist/plugins/' + p); } catch (err) { throw new Error('Cannot find plugin "' + p + '"'); } + serv.addPlugin(p, require('../../plugins/' + p), settings.plugins[p]); } } diff --git a/src/lib/plugins/placeBlock.js b/src/lib/plugins/placeBlock.js index 4105e3a..daf0265 100644 --- a/src/lib/plugins/placeBlock.js +++ b/src/lib/plugins/placeBlock.js @@ -22,19 +22,19 @@ module.exports.player=function(player,serv) direction: directionVector, heldItem: heldItem, id: heldItem.blockId, - data: heldItem.itemDamage, + damage: heldItem.itemDamage, position: placedPosition, reference: referencePosition, playSound: true, sound: 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone'), - }, ({direction, heldItem, position, reference, playSound, sound}) => { + }, ({direction, heldItem, position, reference, playSound, sound, id, damage}) => { if (playSound) { serv.playSound(sound, player.world, placedPosition.clone().add(new Vec3(0.5, 0.5, 0.5)), { pitch: 0.8 }); } if(heldItem.blockId!=323){ - player.changeBlock(position,heldItem.blockId,heldItem.itemDamage); + player.changeBlock(position, id, damage); }else if(direction==1){ player.setBlock(position, 63, 0); player._client.write('open_sign_entity', { From 8d9eb3207310f557c832a97cae9a33c41ee31563 Mon Sep 17 00:00:00 2001 From: DemiPixel Date: Tue, 24 Nov 2015 14:35:07 -0800 Subject: [PATCH 10/10] Fix crashing for module plugins --- src/lib/plugins/external.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/plugins/external.js b/src/lib/plugins/external.js index c9a3109..f711957 100644 --- a/src/lib/plugins/external.js +++ b/src/lib/plugins/external.js @@ -23,15 +23,17 @@ module.exports.server = function(serv, settings) { for (var p in settings.plugins) { if (settings.plugins[p].disabled) continue; try { - serv.addPlugin(p, require(p), settings.plugins[p]); + require.resolve(p); // Check if it exists, if not do catch, otherwise jump to bottom } catch (err) { - try { // Throw error if cannot find plugin + try { // Throw error if cannot find plugin fs.accessSync('./dist/plugins/' + p); } catch (err) { throw new Error('Cannot find plugin "' + p + '"'); } serv.addPlugin(p, require('../../plugins/' + p), settings.plugins[p]); + return; } + serv.addPlugin(p, require(p), settings.plugins[p]); } for (var p in serv.plugins) {