diff --git a/.gitignore b/.gitignore index 3522fed..1dbd325 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ node_modules dist/ modpePlugins/ config/settings.json -logs/ \ No newline at end of file +logs/ +src/plugins/* +!src/plugins/README.md diff --git a/app.js b/app.js index 195a10f..a2e6fcc 100644 --- a/app.js +++ b/app.js @@ -21,7 +21,8 @@ var options = { generation:settings.generation, 'modpe': settings.modpe, kickTimeout: settings.kickTimeout ? settings.kickTimeout : 10*1000, - regionFolder: settings.regionFolder + regionFolder: settings.regionFolder, + plugins: settings.plugins }; module.exports=mcServer.createMCServer(options); diff --git a/config/default-settings.json b/config/default-settings.json index 44aa848..8098b6e 100644 --- a/config/default-settings.json +++ b/config/default-settings.json @@ -10,6 +10,9 @@ "options":{ "worldHeight":80 } + }, + "plugins": { + }, "modpe": false } diff --git a/src/index.js b/src/index.js index 7e05e75..8cc72f0 100644 --- a/src/index.js +++ b/src/index.js @@ -32,5 +32,6 @@ class MCServer extends EventEmitter { if(options.logging == true) this.createLog(); this._server.on('error', error => this.emit('error',error)); this._server.on('listening', () => this.emit('listening',this._server.socketServer.address().port)); + this.emit('asap'); } } \ No newline at end of file diff --git a/src/lib/behavior.js b/src/lib/behavior.js index 73ee180..2ae13b4 100644 --- a/src/lib/behavior.js +++ b/src/lib/behavior.js @@ -1,16 +1,21 @@ module.exports = (obj) => { - return (eventName, data, func, opt) => { + return async (eventName, data, func, opt) => { 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 if (hidden) hiddenCancelled = true; - else cancelled = true; + else { + cancelled = true; + cancelCount++; + } } - obj.emit(eventName + '_cancel', data, cancel); - obj.emit(eventName, data, cancelled); + + await obj.emit(eventName + '_cancel', data, cancel); + await obj.emit(eventName, data, cancelled, cancelCount); - if (!hiddenCancelled && !cancelled) func(data); + if (!hiddenCancelled && !cancelled) await func(data); - obj.emit(eventName + '_done', data, cancelled); + await obj.emit(eventName + '_done', data, cancelled); } } \ No newline at end of file diff --git a/src/lib/command.js b/src/lib/command.js index 46b5996..fafc62a 100644 --- a/src/lib/command.js +++ b/src/lib/command.js @@ -24,7 +24,7 @@ class Command { return res; } - use(command) { + async use(command) { var res = this.find(command); if(res) { @@ -42,7 +42,7 @@ class Command { res[1].shift(); } - res = res[0].params.action(res[1]); + res = await res[0].params.action(res[1]); if(res) return '' + res; } else { return 'Command not found'; diff --git a/src/lib/plugins/behavior.js b/src/lib/plugins/behavior.js new file mode 100644 index 0000000..3e25155 --- /dev/null +++ b/src/lib/plugins/behavior.js @@ -0,0 +1,9 @@ +var Behavior = require('../behavior'); + +module.exports.server = function(serv) { + serv.behavior = new Behavior(serv); +} + +module.exports.entity = function(entity, serv) { + entity.behavior = new Behavior(entity); +} \ No newline at end of file diff --git a/src/lib/plugins/commands.js b/src/lib/plugins/commands.js index 0b62452..c0f26cb 100644 --- a/src/lib/plugins/commands.js +++ b/src/lib/plugins/commands.js @@ -75,7 +75,6 @@ module.exports.player=function(player) { player.handleCommand = (str) => { - var res = player.commands.use(str); - if(res) player.chat('' + res); + player.commands.use(str); }; }; diff --git a/src/lib/plugins/digging.js b/src/lib/plugins/digging.js index 54a252c..378e28e 100644 --- a/src/lib/plugins/digging.js +++ b/src/lib/plugins/digging.js @@ -6,18 +6,45 @@ module.exports.player=function(player,serv) var pos=new Vec3(location.x,location.y,location.z); player.world.getBlock(pos) .then(block => { - currentlyDugBlock=block; - if(currentlyDugBlock.type==0) return; - if(status==0 && player.gameMode!=1) - startDigging(pos); - else if(status==2) - completeDigging(pos); - else if(status==1) - cancelDigging(pos); - else if(status==0 && player.gameMode==1) - creativeDigging(pos); - }) - .catch((err)=> setTimeout(() => {throw err;},0)); + 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); + }); + else if(status==2) + player.behavior('finishDig', { // Finish dig survival + position: position, + block: block + }, ({position}) => { + return completeDigging(position); + }); + 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('dig', { // Start/finish dig creative + position: position, + block: block + }, ({position}) => { + return creativeDigging(position); + }); + } + )} + ) + .catch((err)=> setTimeout(() => {throw err;},0)) }); function diggingTime(location) diff --git a/src/lib/plugins/external.js b/src/lib/plugins/external.js new file mode 100644 index 0000000..09faa89 --- /dev/null +++ b/src/lib/plugins/external.js @@ -0,0 +1,62 @@ +module.exports.server = function(serv, settings) { + serv.plugins = {}; + serv.pluginCount = 0; + serv.externalPluginsLoaded = false; + + serv.addPlugin = (name, plugin, set) => { + if (!name || !plugin) throw new Error('You need a name and object for your plugin!'); + serv.plugins[name] = { + id: serv.pluginCount, + name: name, + player: plugin.player, + entity: plugin.entity, + server: plugin.server, + settings: set, + enabled: true + }; + serv.pluginCount++; + if (serv.externalPluginsLoaded && plugin.server) serv.plugins[name].server.call(p, serv, settings); + } + + for (var p in settings.plugins) { + try { + serv.addPlugin(p, require(p), settings.plugins[p]); + } catch (err) { + try { + serv.addPlugin(p, require('../../plugins/' + p), settings.plugins[p]); + } catch (err) { + throw new Error('Cannot find plugin "' + p + '"'); + } + } + } + + for (var p in serv.plugins) { + if (serv.plugins[p].server) serv.plugins[p].server.call(serv.plugins[p], serv, settings); + } + serv.externalPluginsLoaded = true; +}; + +module.exports.player = function(player, serv) { + Object.keys(serv.plugins).forEach(p => { + var plugin = serv.plugins[p]; + if (plugin.player) plugin.player.call(plugin, player, serv); + }); +}; + +module.exports.entity = function(entity, serv) { + entity.pluginData = {}; + + Object.keys(serv.plugins).forEach(p => { + entity.pluginData[p] = {}; + }); + + entity.getData = (pluginName) => { + if (typeof pluginName == 'object') pluginName = pluginName.name; + return entity.pluginData[pluginName] || null; + } + + Object.keys(serv.plugins).forEach(p => { + var plugin = serv.plugins[p]; + if (plugin.entity) plugin.entity.call(plugin, entity, serv); + }); +} \ No newline at end of file diff --git a/src/lib/plugins/login.js b/src/lib/plugins/login.js index 7406cd2..ff3cd2d 100644 --- a/src/lib/plugins/login.js +++ b/src/lib/plugins/login.js @@ -12,15 +12,16 @@ module.exports.server=function(serv,options) client.on('error',error => serv.emit('clientError',client,error))); serv._server.on('login', async (client) => { - var player = serv.initEntity('player', null, serv.overworld, new Vec3(0,0,0)); - player._client=client; - player.commands = new Command({}); - Object.keys(plugins) - .filter(pluginName => plugins[pluginName].player!=undefined) - .forEach(pluginName => plugins[pluginName].player(player, serv, options)); - - serv.emit("newPlayer",player); try { + var player = serv.initEntity('player', null, serv.overworld, new Vec3(0,0,0)); + player._client=client; + player.commands = new Command({}); + Object.keys(plugins) + .filter(pluginName => plugins[pluginName].player!=undefined) + .forEach(pluginName => plugins[pluginName].player(player, serv, options)); + + serv.emit("newPlayer",player); + player.emit('asap'); await player.login(); } catch(err){ diff --git a/src/lib/plugins/particle.js b/src/lib/plugins/particle.js index 53216c7..0b7cbe3 100644 --- a/src/lib/plugins/particle.js +++ b/src/lib/plugins/particle.js @@ -1,13 +1,12 @@ var Vec3 = require("vec3").Vec3; module.exports.server=function(serv) { - serv.emitParticle = (particle, world, position, {whitelist,blacklist=[],radius=32*32,longDistance=true,size,count=1}={}) => { + serv.emitParticle = (particle, world, position, {whitelist,blacklist=[],radius=32*32,longDistance=true,size=new Vec3(1, 1, 1),count=1}={}) => { var players = (typeof whitelist != 'undefined' ? (typeof whitelist == 'array' ? whitelist : [whitelist]) : serv.getNearby({ world: world, position: position.scaled(32).floored(), radius: radius // 32 blocks, fixed position })); - if (!size) size = new Vec3(1.0, 1.0, 1.0); serv._writeArray('world_particles', { particleId: particle, diff --git a/src/lib/plugins/placeBlock.js b/src/lib/plugins/placeBlock.js index 0ff52c1..ab7e6df 100644 --- a/src/lib/plugins/placeBlock.js +++ b/src/lib/plugins/placeBlock.js @@ -18,26 +18,35 @@ module.exports.player=function(player,serv) var referencePosition=new Vec3(location.x,location.y,location.z); var directionVector=directionToVector[direction]; var placedPosition=referencePosition.plus(directionVector); - player.world.getBlockType(referencePosition).then((id) => { - if([25].indexOf(id) != -1) return; - var sound = 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone'); - serv.playSound(sound, player.world, placedPosition.clone().add(new Vec3(0.5, 0.5, 0.5)), { - pitch: 0.8 - }); + player.behavior('placeBlock', { + direction: directionVector, + heldItem: heldItem, + id: heldItem.blockId, + damage: heldItem.itemDamage, + position: placedPosition, + reference: referencePosition, + playSound: true, + sound: 'dig.' + (materialToSound[blocks[heldItem.blockId].material] || 'stone') + }, ({direction, heldItem, position, reference, playSound, sound}) => { + 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(placedPosition,heldItem.blockId,heldItem.itemDamage); + player.changeBlock(position,heldItem.blockId,heldItem.itemDamage); }else if(direction==1){ - player.setBlock(placedPosition, 63, 0); + player.setBlock(position, 63, 0); player._client.write('open_sign_entity', { - location:placedPosition + location:position }); }else{ - player.setBlock(placedPosition, 68, 0); + player.setBlock(position, 68, 0); player._client.write('open_sign_entity', { - location:placedPosition + location:position }); } - }).catch((err)=> setTimeout(() => {throw err;},0)); + }); }); }; diff --git a/src/lib/plugins/sound.js b/src/lib/plugins/sound.js index a194532..fd4f281 100644 --- a/src/lib/plugins/sound.js +++ b/src/lib/plugins/sound.js @@ -21,12 +21,14 @@ module.exports.server=function(serv) { }); }; - serv.playNoteBlock = (world, position, pitch) => { - serv.emitParticle(23, world, position.clone().add(new Vec3(0.5, 1.5, 0.5)), { - count: 1, - size: new Vec3(0, 0, 0) - }); - serv.playSound('note.harp', world, position, { pitch: serv.getNote(pitch) }); + serv.playNoteBlock = (pitch, world, position, {instrument='harp', particle=true}={}) => { + if (particle) { + serv.emitParticle(23, world, position.clone().add(new Vec3(0.5, 1.5, 0.5)), { + count: 1, + size: new Vec3(0, 0, 0) + }); + } + serv.playSound('note.' + instrument, world, position, { pitch: serv.getNote(pitch) }); }; serv.getNote = note => 0.5 * Math.pow(Math.pow(2, 1/12), note); @@ -48,7 +50,7 @@ module.exports.player=function(player,serv) { if (typeof data.note == 'undefined') data.note = -1; data.note++; data.note %= 25; - serv.playNoteBlock(player.world, pos, data.note); + serv.playNoteBlock(data.note, player.world, pos); }).catch((err)=> setTimeout(() => {throw err;},0)); }); @@ -60,7 +62,7 @@ module.exports.player=function(player,serv) { 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; - serv.playNoteBlock(player.world, pos, data.note); + serv.playNoteBlock(data.not,player.world, pos, data.note); }).catch((err)=> setTimeout(() => {throw err;},0)); }); diff --git a/src/plugins/README.md b/src/plugins/README.md new file mode 100644 index 0000000..9e4ee74 --- /dev/null +++ b/src/plugins/README.md @@ -0,0 +1,21 @@ +## Usage + +Drag plugins in this folder. + +## Creating Plugin + +1) Create folder. + +2) Create your index.js inside the folder. + +3) `cd` to the folder and type `npm init` and follow instructions. + +4) index.js should return in module.exports. player, server, or entity. All are optional. + +5.1) If you don't have config/settings.json, create it and copy everything from config/default-settings.json inside. + +5.2) Add into plugins `"plugin_name": {}`. Add any options you'd like. + +## Publish plugin + +Use `npm publish` and follow instructions. \ No newline at end of file