mirror of
https://github.com/danbulant/flying-squid
synced 2026-06-15 12:31:09 +00:00
Merge pull request #49 from demipixel/plugins
The start of plugins with World Guard example
This commit is contained in:
commit
b2d189b9d2
18 changed files with 575 additions and 8 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1 +1,3 @@
|
|||
node_modules
|
||||
plugins/*
|
||||
!plugins/README.md
|
||||
101
doc/api.md
101
doc/api.md
|
|
@ -39,8 +39,17 @@
|
|||
- ["spawned"](#spawned)
|
||||
- ["disconnected"](#disconnected)
|
||||
- ["error" (error)](#error-error-1)
|
||||
- ["chat" (message)](#chat-message)
|
||||
- ["kicked" (kicker,reason)](#kicked-kickerreason)
|
||||
- [Cancelable Behaviors](#cancelable-behaviors)
|
||||
- ["chatMessage"](#chatmessage)
|
||||
- ["chat"](#chat)
|
||||
- ["command"](#command)
|
||||
- ["startDig"](#startdig)
|
||||
- ["stopDig"](#stopdig)
|
||||
- ["finishDig"](#finishdig)
|
||||
- ["placeBlock"](#placeblock)
|
||||
- ["attackPlayer"](#attackplayer)
|
||||
- ["animation_arm"](#animation_arm)
|
||||
- [Methods](#methods-1)
|
||||
- [player.login()](#playerlogin)
|
||||
- [player.ban(reason)](#playerbanreason)
|
||||
|
|
@ -202,14 +211,96 @@ Fires when the player disconnected
|
|||
|
||||
Fires when there is an error.
|
||||
|
||||
#### "chat" (message)
|
||||
|
||||
Fires when the player says `message`.
|
||||
|
||||
#### "kicked" (kicker,reason)
|
||||
|
||||
`kicker` kick the player with `reason`
|
||||
|
||||
### Cancelable Behaviors
|
||||
|
||||
This type of event is emitted by the the player with the option to cancel a default. It is primarily used by external plugins.
|
||||
This type of event is emitted twice. For example, if a player digs a block, both digBlock\_cancel and digBlock are emitted.
|
||||
digBlock\_cancel has the ability to cancel the default action. digBlock allows plugins to check if the default has been cancelled before it runs. An example with finishDig:
|
||||
|
||||
```js
|
||||
player.on("finishDig_cancel", function(event, cancel) {
|
||||
if (event.block.id == 1) { // If player mined stone (id == 1)
|
||||
cancel(); // Do not break the block in the world, do not send block change to others
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
```js
|
||||
player.on("finishDig", function(event, cancelled) {
|
||||
if (!cancelled) { // Make sure another plugin has not cancelled the default response
|
||||
if (event.block.id == 1) player.chat("You broke stone!");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
For these, the cancel event is always originalName_cancel with arguments (event, cancel)
|
||||
|
||||
The "check cancel" event is always originalName with arguments (event, cancelled)
|
||||
|
||||
#### "chatMessage"
|
||||
|
||||
Fires when a user sends any message to the server (even a command)
|
||||
|
||||
- message: String sent by player
|
||||
|
||||
#### "chat"
|
||||
|
||||
Fires when a user sends a message that does not start with a `/` (i.e. not a command).
|
||||
|
||||
- message: String sent by the player
|
||||
|
||||
#### "command"
|
||||
|
||||
Fires when a user starts a message with a `/`.
|
||||
|
||||
- message: String sent by player but without the `/`
|
||||
|
||||
#### "startDig"
|
||||
|
||||
Fires when a player begins to break a blog (even in creative)
|
||||
|
||||
- position: Position block is being mined in the world
|
||||
- block: Block at that position in world
|
||||
|
||||
#### "stopDig"
|
||||
|
||||
Fires when a player choses to stop breaking a block
|
||||
|
||||
- position: Position block is being mined in the world
|
||||
- block: Block at that position in world
|
||||
|
||||
#### "finishDig"
|
||||
|
||||
Fires when a player has finished mining a block. If the player is in creative, this will be called immediately after `startDig`.
|
||||
|
||||
- time: Time it took to mine block (0 if player is in creative)
|
||||
- position: Position block is being mined in the world
|
||||
- block: Block at that position in world
|
||||
|
||||
#### "placeBlock"
|
||||
|
||||
Fires when a user places a block
|
||||
|
||||
- reference: Position that the player right-clicked on to place the block
|
||||
- position: Position the user wishes to place the block
|
||||
- id: Id of the block they are placing
|
||||
|
||||
`position` and `id` will soon be replaced by `block` which will contain a Block object.
|
||||
|
||||
#### "attackPlayer"
|
||||
|
||||
Fires when one player attacks another
|
||||
|
||||
- attacked: Player who was attacked
|
||||
|
||||
#### "animation_arm"
|
||||
|
||||
Fires when a player wants to "punch" (including anything they're holding).
|
||||
|
||||
### Methods
|
||||
|
||||
#### player.login()
|
||||
|
|
|
|||
|
|
@ -42,6 +42,31 @@ in log.js of playerPlugins or serverPlugins.
|
|||
|
||||
## Creating external plugins
|
||||
|
||||
When you're making an external plugin, create a repo and publish to NPM your code so others can use it.
|
||||
|
||||
However, if you simply want to fool around, create a folder, use `npm init`, and drag it into the "plugins" folder.
|
||||
|
||||
Your file's base should look like this:
|
||||
|
||||
```js
|
||||
module.exports = inject;
|
||||
|
||||
function inject(serv, player, self, opt) {
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
- serv is the Server object. Use this to broadcast messages, set blocks, etc
|
||||
- player is a Player object. You can make changes to the player or check for events from them.
|
||||
- self is your plugin. You may need your plugin id, so you'll use `self.id`.
|
||||
- opt is any options the server has while running.
|
||||
|
||||
Since the plugin is its own node module, you can install any other modules inside of it!
|
||||
|
||||
Checks the API.md for information about what events you can check for on the server or player!
|
||||
|
||||
## Creating external plugins OLD
|
||||
|
||||
Create a new repo, which will be published to npm when ready to be used.
|
||||
|
||||
Create a file in which you put an inject function like this :
|
||||
|
|
|
|||
13
examples/plugins/noplanks/index.js
Normal file
13
examples/plugins/noplanks/index.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module.exports=inject;
|
||||
|
||||
function inject(serv, player, self) {
|
||||
serv.broadcast('Hey ' + player.username + '!');
|
||||
player.setGameMode(1);
|
||||
|
||||
player.on('block_place_cancel', function(e, cancel) { // Users can't place any wood planks!
|
||||
if (e.id == '5') {
|
||||
cancel();
|
||||
player.sendBlock(e.position, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
11
examples/plugins/noplanks/package.json
Normal file
11
examples/plugins/noplanks/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "flying-squid-test",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "DemiPixel <luke5227@gmail.com>",
|
||||
"license": "ISC"
|
||||
}
|
||||
158
examples/plugins/world-guard/index.js
Normal file
158
examples/plugins/world-guard/index.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
module.exports = inject;
|
||||
|
||||
function inject(serv, player, self) {
|
||||
|
||||
player.plugins[self.id].pos = {
|
||||
1: null,
|
||||
2: null
|
||||
};
|
||||
player.plugins[self.id].op = false;
|
||||
self.areas = [];
|
||||
|
||||
player = player;
|
||||
|
||||
player.on('command_cancel', function(e, cancel) {
|
||||
var player = this;
|
||||
var text = e.message;
|
||||
var split = text.split(' ');
|
||||
if (split[0] != 'wg') return;
|
||||
else cancel();
|
||||
var opped = player.plugins[self.id].op;
|
||||
|
||||
if (split[1] == 'pos1' && opped) {
|
||||
self.setPosition(player, 1, player.entity.position.x>>5, player.entity.position.z>>5);
|
||||
player.chat('Set pos1 at ' + (player.entity.position.x>>5) + ',' + (player.entity.position.z>>5));
|
||||
}
|
||||
else if (split[1] == 'pos2' && opped) {
|
||||
self.setPosition(player, 2, player.entity.position.x>>5, player.entity.position.z>>5);
|
||||
player.chat('Set pos2 at ' + (player.entity.position.x>>5) + ',' + (player.entity.position.z>>5));
|
||||
}
|
||||
else if (split[1] == 'set' && opped) {
|
||||
var success = self.setArea(player.plugins[self.id].pos);
|
||||
if (!success) {
|
||||
player.chat('You need to set two positions to set an area. Move to the location you want and use:');
|
||||
player.chat('"/wg pos1" or "/wg pos2"');
|
||||
} else {
|
||||
player.chat('Successfully set area! (Size: ' + success.width + 'x' + success.length + ')');
|
||||
self.clearPosition(player, 1);
|
||||
self.clearPosition(player, 2);
|
||||
}
|
||||
} else if (split[1] == 'clear' && opped) {
|
||||
self.clearPosition(player, 1);
|
||||
self.clearPosition(player, 2);
|
||||
player.chat('Cleared positions');
|
||||
} else if (split[1] == 'op') {
|
||||
var playerTarget = serv.getPlayer(split[2]);
|
||||
if (!playerTarget) {
|
||||
player.chat('No such player "' + split[2] + '"');
|
||||
} else {
|
||||
playerTarget.plugins[self.id].op = true;
|
||||
player.chat('WG Opped ' + split[2]);
|
||||
}
|
||||
} else if (split[1] == 'deop' && opped) {
|
||||
var playerTarget = serv.getPlayer(split[2]);
|
||||
if (!playerTarget) {
|
||||
player.chat('No such player "' + split[2] + '"');
|
||||
} else {
|
||||
playerTarget.plugins[self.id].op = false;
|
||||
player.chat('WG Deopped ' + split[2]);
|
||||
}
|
||||
} else if (split[1] == 'help') {
|
||||
var messages = [
|
||||
'World Guard is used to restrict building areas!',
|
||||
'Use /wg pos1 or /wg pos2 to set your two positions.',
|
||||
'Use /wg set to confirm the positions.',
|
||||
'Use /wg op <user> to allow a user to set positions or build/mine in restricted areas',
|
||||
'Use /wg deop <user> to remove op.',
|
||||
'Use /wg clear to clear positions',
|
||||
'Use /wg list to list all restricted areas you are in (and their IDs)',
|
||||
'Use /wg delete <id> to delete a restricted area'
|
||||
];
|
||||
for (var m in messages) {
|
||||
player.chat(messages[m]);
|
||||
}
|
||||
} else if (split[1] == 'list' && opped) {
|
||||
var areas = self.areasInPosition(player.entity.position.x>>5, player.entity.position.z>>5);
|
||||
if (!areas.length) {
|
||||
player.chat('You are not in any restricted areas!');
|
||||
} else player.chat('==== LIST OF AREAS ====');
|
||||
for (var a in areas) {
|
||||
var ar = areas[a];
|
||||
var str = ar[0] + '] ' + ar[1][1].x + ',' + ar[1][1].z + ' to ' + ar[1][2].x + ',' + ar[1][2].z;
|
||||
str += ' (' + (Math.abs(ar[1][1].x - ar[1][2].x)+1) + 'x' + (Math.abs(ar[1][1].z - ar[1][2].z)+1) + ')';
|
||||
player.chat(str);
|
||||
}
|
||||
} else if (split[1] == 'delete' && opped) {
|
||||
if (!self.areas[split[2]]) {
|
||||
player.chat('Area with id ' + split[2] + ' does not exist!');
|
||||
} else {
|
||||
self.deleteArea(split[2]);
|
||||
player.chat('Deleted area (id ' + split[2] + ')');
|
||||
}
|
||||
} else {
|
||||
player.chat('Not a valid World Guard command! Use /wg help');
|
||||
}
|
||||
});
|
||||
|
||||
player.on('placeBlock_cancel', function(e, cancel) {
|
||||
if (player.plugins[self.id].op === true) return;
|
||||
if (self.areasInPosition(e.position.x, e.position.z).length) {
|
||||
cancel();
|
||||
player.sendBlock(e.position, 0);
|
||||
}
|
||||
});
|
||||
|
||||
player.on('finishDig_cancel', function(e, cancel) {
|
||||
if (player.plugins[self.id].op === true) return;
|
||||
if (self.areasInPosition(e.position.x, e.position.z).length) {
|
||||
cancel();
|
||||
player.sendBlock(e.position, e.block.type);
|
||||
}
|
||||
});
|
||||
|
||||
self.setPosition = function(player, which, x, z) {
|
||||
if (which != 1 && which != 2) return;
|
||||
|
||||
player.plugins[self.id].pos[which] = { x: x, z: z}
|
||||
};
|
||||
|
||||
self.clearPosition = function(player, which) {
|
||||
if (which != 1 && which != 2) return;
|
||||
player.plugins[self.id].pos[which] = null;
|
||||
}
|
||||
|
||||
self.setArea = function(pos) {
|
||||
if (!pos[1] || !pos[2]) return false;
|
||||
|
||||
self.areas.push({
|
||||
1: {
|
||||
x: pos[1].x,
|
||||
z: pos[1].z
|
||||
},
|
||||
2: {
|
||||
x: pos[2].x,
|
||||
z: pos[2].z
|
||||
}
|
||||
});
|
||||
return {
|
||||
width: Math.abs(pos[1].x-pos[2].x) + 1,
|
||||
length: Math.abs(pos[1].z-pos[2].z) + 1
|
||||
}
|
||||
}
|
||||
|
||||
self.deleteArea = function(id) {
|
||||
self.areas[id] = null;
|
||||
}
|
||||
|
||||
self.areasInPosition = function(x, z) {
|
||||
var inside = [];
|
||||
for (var a in self.areas) {
|
||||
if (!self.areas[a]) continue;
|
||||
var x1 = self.areas[a][1].x, x2 = self.areas[a][2].x, z1 = self.areas[a][1].z, z2 = self.areas[a][2].z;
|
||||
if (x >= Math.min(x1,x2) && x <= Math.max(x1,x2) && z >= Math.min(z1,z2) && z <= Math.max(z1, z2)) {
|
||||
inside.push([a, self.areas[a]]);
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
}
|
||||
11
examples/plugins/world-guard/package.json
Normal file
11
examples/plugins/world-guard/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "world-guard",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "DemiPixel <luke5227@gmail.com>",
|
||||
"license": "ISC"
|
||||
}
|
||||
21
lib/cancelEvent.js
Normal file
21
lib/cancelEvent.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
module.exports = emit;
|
||||
|
||||
function emit(target, eventName, args, defaultFunc) {
|
||||
var hiddenCancelled = false;
|
||||
var cancelled = false;
|
||||
var cancel = function(hidden) { // Hidden shouldn't be used often but it's not hard to implement so meh
|
||||
if (hidden) hiddenCancelled = true;
|
||||
else cancelled = true;
|
||||
}
|
||||
|
||||
target.emit(eventName + '_cancel', args, cancel);
|
||||
|
||||
target.emit(eventName, args, cancelled);
|
||||
|
||||
if (!hiddenCancelled && !cancelled) {
|
||||
if (defaultFunc) defaultFunc();
|
||||
return true;
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,13 @@
|
|||
var cancelEmit = require("../cancelEvent");
|
||||
|
||||
module.exports=inject;
|
||||
|
||||
function inject(serv, player)
|
||||
{
|
||||
player._client.on("arm_animation", function(packet) {
|
||||
var doDefault = cancelEmit(player, "animation_arm", {});
|
||||
if (!doDefault) return;
|
||||
|
||||
player._writeOthers("animation", {
|
||||
entityId: player.entity.id,
|
||||
animation: 0
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
var cancelEmit = require("../cancelEvent");
|
||||
|
||||
module.exports=inject;
|
||||
|
||||
function inject(serv, player)
|
||||
{
|
||||
player._client.on('chat', function (packet) {
|
||||
var doDefault = cancelEmit(player, "chatMessage", {
|
||||
message: packet.message
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
if(packet.message[0]=="/") {
|
||||
var doDefault = cancelEmit(player, "command", {
|
||||
message: packet.message.slice(1)
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
var command = packet.message.slice(1);
|
||||
player.handleCommand(command);
|
||||
}
|
||||
else {
|
||||
var doDefault = cancelEmit(player, "chat", {
|
||||
message: packet.message
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
serv.broadcast('<' + player.username + '>' + ' ' + packet.message);
|
||||
player.emit("chat",packet.message);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,10 @@ function inject(serv, player, options) {
|
|||
});
|
||||
}
|
||||
}
|
||||
else if (results = command.match(/^reload/)) {
|
||||
serv.reloadPlugins();
|
||||
player.chat('Reloaded ' + serv.plugins.length + ' plugins.');
|
||||
}
|
||||
else
|
||||
player.chat("Invalid command.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
var Vec3 = require("vec3");
|
||||
var cancelEmit = require("../cancelEvent");
|
||||
|
||||
module.exports=inject;
|
||||
|
||||
|
|
@ -6,6 +7,13 @@ function inject(serv,player)
|
|||
{
|
||||
player._client.on("block_dig",function(packet){
|
||||
var pos=new Vec3(packet.location);
|
||||
|
||||
var doDefault = cancelEmit(player, "startDig", {
|
||||
position: pos,
|
||||
block: serv.world.getBlock(pos)
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
currentlyDugBlock=serv.world.getBlock(pos);
|
||||
if(currentlyDugBlock.type==0) return;
|
||||
if(packet.status==0 && player.gameMode!=1)
|
||||
|
|
@ -46,6 +54,13 @@ function inject(serv,player)
|
|||
newDestroyState=newDestroyState>9 ? 9 : newDestroyState;
|
||||
if(newDestroyState!=lastDestroyState)
|
||||
{
|
||||
var doDefault = cancelEmit(player, "breakAnimation", {
|
||||
lastState: lastDestroyState,
|
||||
position: location,
|
||||
block: currentlyDugBlock
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
lastDestroyState=newDestroyState;
|
||||
player._writeOthers("block_break_animation",{
|
||||
"entityId":currentAnimationId,
|
||||
|
|
@ -59,6 +74,13 @@ function inject(serv,player)
|
|||
function cancelDigging(location)
|
||||
{
|
||||
clearInterval(animationInterval);
|
||||
|
||||
var doDefault = cancelEmit(player, "stopDig", {
|
||||
position: pos,
|
||||
block: serv.world.getBlock(pos)
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
player._writeOthers("block_break_animation",{
|
||||
"entityId":currentAnimationId,
|
||||
"location":location,
|
||||
|
|
@ -70,8 +92,15 @@ function inject(serv,player)
|
|||
{
|
||||
clearInterval(animationInterval);
|
||||
var diggingTime=new Date()-startDiggingTime;
|
||||
if(expectedDiggingTime-diggingTime<100)
|
||||
if(expectedDiggingTime-diggingTime<100) {
|
||||
var doDefault = cancelEmit(player, "finishDig", {
|
||||
time: diggingTime,
|
||||
position: pos,
|
||||
block: serv.world.getBlock(pos)
|
||||
});
|
||||
if (!doDefault) return;
|
||||
player.changeBlock(location,0);
|
||||
}
|
||||
else
|
||||
{
|
||||
player._client.write("block_change",{
|
||||
|
|
@ -84,6 +113,12 @@ function inject(serv,player)
|
|||
|
||||
function creativeDigging(location)
|
||||
{
|
||||
var doDefault = cancelEmit(player, "finishDig", {
|
||||
time: 0,
|
||||
position: location,
|
||||
block: serv.world.getBlock(location)
|
||||
});
|
||||
if (!doDefault) return;
|
||||
player.changeBlock(location,0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
var vec3 = require("vec3");
|
||||
var cancelEmit = require("../cancelEvent");
|
||||
|
||||
module.exports=inject;
|
||||
|
||||
|
|
@ -9,6 +10,14 @@ function inject(serv,player)
|
|||
var referencePosition=new vec3(packet.location.x,packet.location.y,packet.location.z);
|
||||
var directionVector=directionToVector[packet.direction];
|
||||
var placedPosition=referencePosition.plus(directionVector);
|
||||
|
||||
var doDefault = cancelEmit(player, "placeBlock", { // TODO, make block object and send it (instead of ID)
|
||||
reference: referencePosition,
|
||||
position: placedPosition,
|
||||
id: packet.heldItem.blockId
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
player.changeBlock(placedPosition,packet.heldItem.blockId);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
var cancelEmit = require("../cancelEvent");
|
||||
|
||||
module.exports=inject;
|
||||
|
||||
function inject(serv, player)
|
||||
|
|
@ -15,6 +17,12 @@ function inject(serv, player)
|
|||
function attackEntity(entityId)
|
||||
{
|
||||
var attackedPlayer = serv.entities[entityId].player;
|
||||
|
||||
var doDefault = cancelEmit(player, "attackPlayer", {
|
||||
attacked: attackedPlayer
|
||||
});
|
||||
if (!doDefault) return;
|
||||
|
||||
if(attackedPlayer.gameMode!=0) return;
|
||||
attackedPlayer.updateHealth(attackedPlayer.entity.health - 1);
|
||||
|
||||
|
|
@ -32,7 +40,7 @@ function inject(serv, player)
|
|||
|
||||
player._client.on("use_entity", function(packet) {
|
||||
if(packet.mouse == 1) {
|
||||
attackEntity(packet.target);
|
||||
if (packet.target.player) attackEntity(packet.target);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -15,5 +15,12 @@ function inject(serv,options)
|
|||
}
|
||||
serv.emit("newPlayer",player);
|
||||
player.login();
|
||||
|
||||
player.plugins = Array();
|
||||
for(var pluginName in serv.plugins) { // External plugins
|
||||
player.plugins[serv.plugins[pluginName].id] = {}; // Give object to save data per plugin per player, referenced by plugin ID
|
||||
var plug = require(serv.plugins[pluginName].path)(serv, player, serv.plugins[pluginName], options);
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
125
lib/serverPlugins/plugins.js
Normal file
125
lib/serverPlugins/plugins.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
module.exports = inject;
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
function inject(serv) {
|
||||
|
||||
serv.loadPlugins = loadPlugins;
|
||||
|
||||
serv.loadPlugins();
|
||||
|
||||
function resetPlayers() {
|
||||
for (var p in serv.players) {
|
||||
var player = serv.players[p]
|
||||
player.plugins = Array();
|
||||
for (var pl in serv.plugins) {
|
||||
var plugin = serv.plugins[pl];
|
||||
player.plugins[plugin.id] = {};
|
||||
require(plugin.path)(serv, player, plugin);
|
||||
}
|
||||
console.log(serv.players[p].plugins);
|
||||
}
|
||||
}
|
||||
|
||||
serv.reloadPlugins = function() {
|
||||
console.log('RELOADING DOES NOT WORK');
|
||||
return;
|
||||
serv.emit('pluginend');
|
||||
resetPlayers();
|
||||
}
|
||||
|
||||
serv.fullReloadPlugins = function(cb) {
|
||||
console.log('RELOADING DOES NOT WORK');
|
||||
return;
|
||||
serv.emit('pluginend');
|
||||
serv.loadPlugins(function() {
|
||||
resetPlayers();
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
serv.getPlugin = function(name) {
|
||||
return serv.plugins[name] || null;
|
||||
}
|
||||
}
|
||||
|
||||
function loadPlugins(cb) {
|
||||
var serv = this;
|
||||
serv.plugins = Array();
|
||||
|
||||
loadCount = 0;
|
||||
allPlugins = null;
|
||||
|
||||
getNodeModules(serv, setPluginsFromModules, cb);
|
||||
|
||||
var pluginPath = __dirname.match(/(.*?)\/lib/)[1] + '/plugins'; // Prob a cleaner way to do this
|
||||
fs.readdir(pluginPath, function(err, arr) {
|
||||
if (!arr) setPlugins([], serv);
|
||||
else {
|
||||
var plugins = Array();
|
||||
for (var a in arr) {
|
||||
if (arr[a].indexOf('.') == 0 || arr[a] == 'README.md') continue;
|
||||
plugins.push({
|
||||
name: arr[a],
|
||||
path: pluginPath + '/' + arr[a]
|
||||
});
|
||||
}
|
||||
setPlugins(plugins, serv, cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setPluginsFromModules(err, modules, serv, cb) {
|
||||
if (err) {
|
||||
console.log('ERROR: Error loading node_modules; Cannot load external plugins! /lib/serverPlugins/plugins.js');
|
||||
serv.emit('error',err);
|
||||
return;
|
||||
}
|
||||
|
||||
var plugins = Array();
|
||||
for (var m in modules) {
|
||||
if (m.indexOf('flying-squid-') == 0) {
|
||||
var pluginName = m.replace('flying-squid-','');
|
||||
plugins.push({
|
||||
name: pluginName,
|
||||
path: pluginName
|
||||
});
|
||||
}
|
||||
}
|
||||
setPlugins(plugins, serv, cb);
|
||||
}
|
||||
|
||||
var loadCount = 0;
|
||||
var allPlugins;
|
||||
function setPlugins(plugins, serv, cb) {
|
||||
loadCount++;
|
||||
if (loadCount < 2) { // Wait for both plugins folder and node_modules to load
|
||||
allPlugins = plugins;
|
||||
return;
|
||||
} else {
|
||||
plugins = plugins.concat(allPlugins).sort(); // Sorting makes it easy to check duplicates
|
||||
}
|
||||
|
||||
var id = 0;
|
||||
for (var p in plugins) {
|
||||
serv.plugins[plugins[p].name] = { // Other info about plugin here, TODO: Add events (i.e. ".on"), allow cancels?
|
||||
id: id,
|
||||
path: plugins[p].path,
|
||||
name: plugins[p].name
|
||||
};
|
||||
console.log('Loaded plugin: ' + plugins[p].name);
|
||||
id++;
|
||||
if (p < plugins.length-1 && plugins[p].name == plugins[parseInt(p)+1].name) { // Only checks for two duplicates, TODO: check for 3+ duplicates
|
||||
p++;
|
||||
}
|
||||
}
|
||||
console.log('Loaded ' + id + ' Plugin' + (id != 1 ? 's' : '') );
|
||||
if (cb) cb();
|
||||
}
|
||||
|
||||
function getNodeModules(serv, cb, cb2) {
|
||||
require('child_process').exec('npm ls --json', function(err, stdout, stderr) {
|
||||
if (err) return cb(err, null, serv, cb2);
|
||||
cb(null, JSON.parse(stdout).dependencies, serv, cb2);
|
||||
});
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@
|
|||
"prismarine-chunk": "git://github.com/rom1504/prismarine-chunk.git#use-prismarine-block",
|
||||
"prismarine-entity": "0.1.0",
|
||||
"prismarine-world": "git://github.com/rom1504/prismarine-world.git#implementation",
|
||||
"random-seed": "^0.2.0",
|
||||
"request": "^2.61.0",
|
||||
"requireindex": "~1.0.0",
|
||||
"vec3": "0.1.3"
|
||||
|
|
|
|||
25
plugins/README.md
Normal file
25
plugins/README.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
## Do you really need to use this folder??
|
||||
|
||||
If you know of a plugin on npm or git, simply use in console
|
||||
|
||||
```
|
||||
npm install --save flying-squid-plugin-name
|
||||
```
|
||||
|
||||
Or for a git repository:
|
||||
|
||||
```
|
||||
npm install --save git+https://git@github.com/yourname/repo.git
|
||||
```
|
||||
|
||||
## Using /plugins
|
||||
|
||||
Simply create a folder inside of /plugins with the name of your plugin. Inside, do `npm init` and create your index.js!
|
||||
|
||||
You need this because npm complains about modules inside of node_modules that are not inside package.json.
|
||||
|
||||
**USE THIS SPARINGLY!**
|
||||
|
||||
## Contributors
|
||||
|
||||
.gitignore ignores everything in this folder except for README.md. Don't worry about removing contents in order to push!
|
||||
Loading…
Reference in a new issue