Merge pull request #154 from demipixel/inCompSciRightNow

wats ur fav type of icecream
This commit is contained in:
Romain Beaumont 2015-12-11 00:23:08 +01:00
commit b679dd6325
14 changed files with 190 additions and 205 deletions

View file

@ -5,93 +5,67 @@
Directory architecture : Directory architecture :
* app.js: specific settings and actually start the server * app.js: specific settings and actually start the server
* index.js: contain the generic server implementation * dist/: Contains "compiled" code (go to current directory in console and type `gulp` to generate this)
* lib/: contain the classes and functions used in the plugins * src/: Source files for the project
* serverPlugins/: server plugins that do things general to the server, * src/index.js: contain the generic server implementation
properties and method are added to the server object in them * src/lib: contain the classes and functions used in the plugins
* playerPlugins/: player plugins that do things for each player, properties and method are added to the player object in them * plugins/: All of the default plugins made to simulate vanilla
* worldGeneraions/: Contains default world generations, however plugins can use their own
Structure of a server plugin: ## Structure of a plugin
```js ```js
module.exports=inject;
function inject(serv) // Each of these are called an "inject" because you're injecting properties, events, methods, or data into the objects
{
// add methods and properties to serv module.exports.server = function(serv) { // Create your server events here
serv.spawnPoint = ...;
serv.on('...', ...);
} }
```
Structure of a player plugin : module.exports.entity = function(entity, serv) { // Called whenever an entity is created, do NOT do serv.on here
entity.health = 10; // Start with 10 health out of 20
```js entity.on('...', ...);
module.exports=inject; // serv.on('...', ...); NOOOO
function inject(serv,player)
{
// add methods and properties to player
// you can use serv, but you shouldn't add things to it here
} }
module.exports.player = function(player, serv) { // Player is a type of entity (entity inject is called first) with added properties and functions
player.setXp(100); // Example of a property player entities have but not other entities
player.on(',,,', ...);
// serv.on('...', . don't even think about it
}
``` ```
## Logs and event ## Logs and event
In order to keep logging independent from the rest of the server and to let people react in other ways than logging, In order to keep logging independent from the rest of the server and to let people react in other ways than logging,
server and player events should be emitting and the logging should only take place in response to these events logging uses methods and events from `log.js`. These include `serv.log(message)` and `serv.emit('error', err)`.
in log.js of playerPlugins or serverPlugins.
## Creating external plugins ## Creating external plugins
Create a new repo, which will be published to npm when ready to be used. Create a new repo, which will be published to npm when ready to be used. Create a file (probably `index.js`) in which you use a similar format as above (module.exports.xxxx).
Create a file in which you put an inject function like this : In these inject functions you can use everything documented in the [api.md](api.md).
```js Let's say you called your module fs-flying-horses and you published it to npm.
module.exports=init;
function init(flying-squid) { Now people can use install your plugin by simply typing:
return inject;
}
function inject(serv) ```npm install fs-flying-horses```
{
// add methods and properties to serv
}
```
In the init function, you can use anything flying-squid provide ### Testing your Plugin
(see [index.js](https://github.com/mhsjlw/flying-squid/blob/master/index.js#L11)).
In the inject function you can use everything documented in the [api.md](api.md) to add functionalities to the serv object. For your convenience, you can put your plugin inside /src/plugins. An example might look like:
- src/plugins/
- myPluginName/
- index.js
- package.json
- node_modules
- ...
- myPluginName2.js (direct files are allowed but are impossible to publish, so it's best only to use them for testing)
Let's say you called your module flying-horses and you published it to npm. ## Conclusion
Now people can use your plugin that way :
```js
var flyingSquid = require('flying-squid');
var flyingHorses = require('flying-horses')(flyingSquid);
var serv = flyingSquid.createMCServer(/* your options there */);
// install the plugin
flyingHorses(serv);
```
As explained in the first part of this file, flying-squid has 2 kinds of plugins: server plugins, and player plugins.
We've explained until now how to create a server plugin and to use it with flying-squid.
Within the same module, you can also create a player plugin. Here is the code you need to add to do that:
```js
serv.on("newPlayer",function(player){
injectPlayer(serv,player);
});
function injectPlayer(serv,player)
{
// add methods and properties to player
// you can use serv, but you shouldn't add things to it here
}
```
In this document, we explained how to create a simple plugin with just one file, but you can cut your code In this document, we explained how to create a simple plugin with just one file, but you can cut your code
in several files by having several inject function and putting them in different files, just like flying-squid does for its internal plugins. in several files by having several inject function and putting them in different files, just like flying-squid does for its internal plugins.

View file

@ -13,7 +13,8 @@ module.exports = {
Command:require("./lib/command"), Command:require("./lib/command"),
version:require("./lib/version"), version:require("./lib/version"),
generations:require("./lib/generations"), generations:require("./lib/generations"),
experience:require("./lib/experience") experience:require("./lib/experience"),
UserError:require("./lib/UserError")
}; };
function createMCServer(options) { function createMCServer(options) {
@ -39,5 +40,7 @@ class MCServer extends EventEmitter {
this._server.on('error', error => this.emit('error',error)); this._server.on('error', error => this.emit('error',error));
this._server.on('listening', () => this.emit('listening',this._server.socketServer.address().port)); this._server.on('listening', () => this.emit('listening',this._server.socketServer.address().port));
this.emit('asap'); this.emit('asap');
process.on('unhandledRejection', err => this.emit('error',err));
} }
} }

5
src/lib/UserError.js Normal file
View file

@ -0,0 +1,5 @@
class UserError extends Error {
}
module.exports = UserError;

View file

@ -13,13 +13,22 @@ module.exports = (obj) => {
defaultCancel = dC; defaultCancel = dC;
}; };
var resp;
await obj.emitThen(eventName + '_cancel', data, cancel).catch((err)=> setTimeout(() => {throw err;},0)); 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)); await obj.emitThen(eventName, data, cancelled, cancelCount).catch((err)=> setTimeout(() => {throw err;},0));
if (!hiddenCancelled && !cancelled) await func(data).catch((err)=> setTimeout(() => {throw err;},0)); if (!hiddenCancelled && !cancelled) {
else if (cancelFunc && defaultCancel) await cancelFunc(data).catch((err)=> setTimeout(() => {throw err;},0)); resp = func(data);
if (resp instanceof Promise) resp = await resp.catch((err)=> setTimeout(() => {throw err;},0));
if (typeof resp == 'undefined') resp = true;
} else if (cancelFunc && defaultCancel) {
resp = cancelFunc(data);
if (resp instanceof Promise) resp = await resp.catch((err)=> setTimeout(() => {throw err;},0));
if (typeof resp == 'undefined') resp = false;
}
await obj.emitThen(eventName + '_done', data, cancelled).catch((err)=> setTimeout(() => {throw err;},0)); await obj.emitThen(eventName + '_done', data, cancelled).catch((err)=> setTimeout(() => {throw err;},0));
return data;
return resp;
} }
}; };

View file

@ -3,6 +3,9 @@ class Command {
this.params = params; this.params = params;
this.parent = parent; this.parent = parent;
this.hash = parent ? parent.hash : {}; this.hash = parent ? parent.hash : {};
this.uniqueHash = parent ? parent.uniqueHash : {};
this.parentBase = (this.parent && this.parent.base && this.parent.base + ' ') || '';
this.base = this.parentBase + (this.params.base || '');
this.updateHistory(); this.updateHistory();
} }
@ -44,9 +47,9 @@ class Command {
updateHistory() { updateHistory() {
var all = '(.+?)'; var all = '(.+?)';
var list = [this.params.base]; var list = [this.base];
if(this.params.aliases && this.params.aliases.length) { if(this.params.aliases && this.params.aliases.length) {
this.params.aliases.forEach(al => list.unshift(al)); this.params.aliases.forEach(al => list.unshift(this.parentBase + al));
} }
list.forEach((command) => { list.forEach((command) => {
@ -56,6 +59,7 @@ class Command {
if(this.path) this.hash[this.path] = this; if(this.path) this.hash[this.path] = this;
}); });
this.uniqueHash[this.base] = this;
} }
add(params) { add(params) {

View file

@ -30,24 +30,18 @@ module.exports.player=function(player,serv)
player.commands.add({ player.commands.add({
base: 'setblock', base: 'setblock',
info: 'to put a block', info: 'set a block at a position',
usage: '/setblock <x> <y> <z> <id> <data>', usage: '/setblock <x> <y> <z> <id> [data]',
op: true, op: true,
parse(str) { parse(str) {
var results = str.match(/^(~|~?-?[0-9]*) (~|~?-?[0-9]*) (~|~?-?[0-9]*) ([0-9]{1,3}) ([0-9]{1,3})/); var results = str.match(/^(~|~?-?[0-9]+) (~|~?-?[0-9]+) (~|~?-?[0-9]+) ([0-9]{1,3})(?: ([0-9]{1,3}))?/);
if(!results) return false; if(!results) return false;
return results; return results;
}, },
action(params) { action(params) {
var res = params.map((num, i) => { // parseInt parameters var res = params.slice(1, 4);
if (num.indexOf('~') == 0) { res = res.map((val, i) => serv.posFromString(val, player.position[['x','y','z'][i]] / 32))
return (player.position[['', 'x', 'y', 'z'][i]] >> 5) + parseInt(num.slice(1) || 0); player.setBlock(new Vec3(res[0], res[1], res[2]).floored(), params[4], params[5] || 0);
} else {
return parseInt(num); // return parseInt>>5 if position, not id
}
});
player.setBlock(new Vec3(res[1], res[2], res[3]), res[4],res[5]);
} }
}); });
}; };

View file

@ -1,32 +1,55 @@
var Vec3 = require("vec3").Vec3;
var UserError = require('flying-squid').UserError;
module.exports.player=function(player, serv) { module.exports.player=function(player, serv) {
player.commands.add({ player.commands.add({
base: 'help', base: 'help',
info: 'to show all commands', info: 'to show all commands',
usage: '/help [command]', usage: '/help [command]',
action(params) { parse(str) {
var c = params[0]; var params = str.split(' ');
var hash = player.commands.hash; var page = parseInt(params[params.length-1]);
var search = '';
if (page) {
params.pop();
}
search = params.join(' ');
return { search: search, page: (page && page - 1) || 0 };
},
action({search, page}) {
if (page < 0) return 'Page # must be >= 1';
var hash = player.commands.uniqueHash;
if(c) { var PAGE_LENGTH = 8;
var f=player.commands.find(c);
if(f==undefined || f.length==0) return 'Command '+c+' not found';
return f[0].params.usage + ' ' + f[0].params.info;
} else {
var used = [];
for(var key in hash) {
if(used.indexOf(hash[key]) > -1) continue;
used.push(hash[key]);
if(hash[key].params.info && (player.op || !hash[key].params.op)) { var found = Object.keys(hash).filter(h => (h + ' ').indexOf((search && search + ' ') || '') == 0);
var str = hash[key].params.usage + ' ' + hash[key].params.info;
if(hash[key].params.aliases && hash[key].params.aliases.length) {
str += ' (aliases: ' + hash[key].params.aliases.join(', ') + ')';
}
player.chat(str); if (found.length == 0) { // None found
} return 'Could not find any matches';
} else if (found.length == 1) { // Single command found, giev info on command
var cmd = hash[found[0]];
var usage = (cmd.params && cmd.params.usage) || cmd.base;
var info = (cmd.params && cmd.params.info) || 'No info';
player.chat(usage + ': ' + info);
} else { // Multiple commands found, give list with pages
var totalPages = Math.ceil((found.length-1) / PAGE_LENGTH);
if (page >= totalPages) return 'There are only' + totalPages + ' help pages';
found = found.sort();
if (found.indexOf('search') != -1) {
var baseCmd = hash[search];
player.chat(baseCmd.base + ' -' + ((baseCmd.params && baseCmd.params.info && ' ' + baseCmd.params.info) || '=-=-=-=-=-=-=-=-'));
} else {
player.chat('Help -=-=-=-=-=-=-=-=-');
} }
for (var i = PAGE_LENGTH*page; i < Math.min(PAGE_LENGTH*(page + 1), found.length); i++) {
if (i == search) continue;
var cmd = hash[found[i]];
var usage = (cmd.params && cmd.params.usage) || cmd.base;
var info = (cmd.params && cmd.params.info) || 'No info';
player.chat(usage + ': ' + info);
}
player.chat('--=[Page ' + (page + 1) + ' of ' + totalPages + ']=--')
} }
} }
}); });
@ -83,8 +106,7 @@ module.exports.player=function(player, serv) {
}, },
action(sel) { action(sel) {
var arr = serv.selectorString(sel, player.position.scaled(1/32), player.world); var arr = serv.selectorString(sel, player.position.scaled(1/32), player.world);
if (arr instanceof Error) return arr.toString(); if (arr == null) return 'Could not find player';
else if (arr == null) return 'Could not find player';
else player.chat(JSON.stringify(arr.map(a => a.id))); else player.chat(JSON.stringify(arr.map(a => a.id)));
} }
}); });
@ -96,11 +118,16 @@ module.exports.player=function(player, serv) {
if (res) player.chat('' + res); if (res) player.chat('' + res);
} }
catch(err) { catch(err) {
setTimeout(() => {throw err;}, 0); if (err instanceof UserError) player.chat('Error: ' + err.toString());
else setTimeout(() => {throw err;}, 0);
} }
} }
}; };
module.exports.entity = function(entity, serv) {
entity.selectorString = (str) => serv.selectorString(str, entity.position.scaled(1/32), entity.world);
}
module.exports.server = function(serv) { module.exports.server = function(serv) {
function shuffleArray(array) { function shuffleArray(array) {
@ -126,7 +153,7 @@ module.exports.server = function(serv) {
serv.selector = (type, opt) => { serv.selector = (type, opt) => {
if (['all', 'random', 'near', 'entity'].indexOf(type) == -1) if (['all', 'random', 'near', 'entity'].indexOf(type) == -1)
return new Error('serv.selector(): type must be either [all, random, near, or entity]'); throw new UserError('serv.selector(): type must be either [all, random, near, or entity]');
var count = typeof opt.count != 'undefined' ? var count = typeof opt.count != 'undefined' ?
count : count :
@ -209,12 +236,13 @@ module.exports.server = function(serv) {
else return sample.slice(count); // Negative, returns from end else return sample.slice(count); // Negative, returns from end
} }
serv.selectorString = (str, pos, world) => { serv.selectorString = (str, pos, world, allowUser=true) => {
pos = pos.clone(); pos = pos.clone();
var player = serv.getPlayer(str); var player = serv.getPlayer(str);
if (!player && str[0] != '@') return null; if (!player && str[0] != '@') return null;
else if (player) return allowUser ? [player] : null;
var match = str.match(/^@([a,r,p,e])(?:\[([^\]]+)\])?$/); var match = str.match(/^@([a,r,p,e])(?:\[([^\]]+)\])?$/);
if (match == null) return new Error('Invalid selector format'); if (match == null) throw new UserError('Invalid selector format');
var typeConversion = { var typeConversion = {
a: 'all', a: 'all',
r: 'random', r: 'random',
@ -227,10 +255,10 @@ module.exports.server = function(serv) {
var err; var err;
opt.forEach(o => { opt.forEach(o => {
var match = o.match(/^([^=]+)=([^=]+)$/); var match = o.match(/^([^=]+)=([^=]+)$/);
if (match == null) err = new Error('Invalid selector option format: "' + o + '"'); if (match == null) err = new UserError('Invalid selector option format: "' + o + '"');
else optPair.push({key: match[1], val: match[2]}); else optPair.push({key: match[1], val: match[2]});
}); });
if (err) return err; if (err) throw err;
var optConversion = { var optConversion = {
type: 'type', type: 'type',
@ -269,4 +297,11 @@ module.exports.server = function(serv) {
return serv.selector(type, data); return serv.selector(type, data);
} }
serv.posFromString = (str, pos) => {
if (parseInt(str)) return parseInt(str);
if (str.match(/~-?\d+/)) return parseInt(str.slice(1)) + pos;
else if (str == '~') return pos;
else throw new UserError('Invalid position');
};
} }

View file

@ -19,9 +19,8 @@ module.exports.server=function(serv,options) {
} }
} }
if (!entity.velocity || !entity.size) return; if (!entity.velocity || !entity.size) return;
var oldPosAndOnGround = await entity.calculatePhysics(delta); var posAndOnGround = await entity.calculatePhysics(delta);
if (!oldPosAndOnGround.oldPos.equals(new Vec3(0,0,0))) if (entity.type == 'mob') entity.sendPosition(posAndOnGround.position, posAndOnGround.onGround);
if (entity.type == 'mob') entity.sendPosition(oldPosAndOnGround);
}) })
).catch((err)=> setTimeout(() => {throw err;},0)); ).catch((err)=> setTimeout(() => {throw err;},0));
}); });

View file

@ -158,7 +158,7 @@ module.exports.player=function(player,serv)
sendLogin(); sendLogin();
await player.sendMap(); await player.sendMap();
player.sendSpawnPosition(); player.sendSpawnPosition();
player.sendPosition(); player.sendSelfPosition();
player.updateHealth(player.health); player.updateHealth(player.health);

View file

@ -27,14 +27,14 @@ module.exports.entity=function(entity){
entity.velocity.z = getFriction(entity.velocity.z, entity.friction.z, delta); entity.velocity.z = getFriction(entity.velocity.z, entity.friction.z, delta);
} }
var oldPos = entity.position.clone(); var newPos = entity.position.clone();
entity.position.x += getMoveAmount('x', xBlock, entity, delta, sizeSigned.x); newPos.x += getMoveAmount('x', xBlock, entity, delta, sizeSigned.x);
entity.position.y += getMoveAmount('y', yBlock, entity, delta, sizeSigned.y); newPos.y += getMoveAmount('y', yBlock, entity, delta, sizeSigned.y);
entity.position.z += getMoveAmount('z', zBlock, entity, delta, sizeSigned.z); newPos.z += getMoveAmount('z', zBlock, entity, delta, sizeSigned.z);
//serv.emitParticle(30, serv.overworld, entity.position.scaled(1/32), { size: new Vec3(0, 0, 0) }); //serv.emitParticle(30, serv.overworld, entity.position.scaled(1/32), { size: new Vec3(0, 0, 0) });
return { oldPos: oldPos, onGround: yBlock} return { position: newPos, onGround: yBlock}
}; };

View file

@ -9,7 +9,7 @@ module.exports.player=function(player)
gamemode:player.gameMode, gamemode:player.gameMode,
levelType:'default' levelType:'default'
}); });
player.sendPosition(); player.sendSelfPosition();
player.updateHealth(20); player.updateHealth(20);
player.nearbyEntities=[]; player.nearbyEntities=[];
player.updateAndSpawn(); player.updateAndSpawn();

View file

@ -2,11 +2,6 @@ var Vec3 = require("vec3").Vec3;
module.exports.player = (player, serv) => { module.exports.player = (player, serv) => {
var getPos = (num, dir='x', p=player) => {
if (num[0] == '~') return p.position[dir] + parseInt(num.slice(1, num.length) || 0)*32;
else return parseInt(num)*32;
}
player.commands.add({ player.commands.add({
base: 'teleport', base: 'teleport',
aliases: ['tp'], aliases: ['tp'],
@ -14,34 +9,29 @@ module.exports.player = (player, serv) => {
usage: '/teleport [target player] <destination player or x> [y] [z]', usage: '/teleport [target player] <destination player or x> [y] [z]',
op: true, op: true,
parse(str) { parse(str) {
return str.match(/^(((\w* )?~?-?\d* ~?-?\d* ~?-?\d*)|(\w* \w*))$/) ? str.split(' ') : false; return str.match(/^(((.* )?~?-?\d* ~?-?\d* ~?-?\d*)|(.+ .+))$/) ? str.split(' ') : false;
}, },
action(args) { action(args) {
if(args.length === 2 && args[0] !== args[1]) { if(args.length === 2) {
let player_from; let entities_from = player.selectorString(args[0]);
let player_to; let entity_to = player.selectorString(args[1])[0];
if(!(player_from = serv.getPlayer(args[0])) || !(player_to = serv.getPlayer(args[1]))) entities_from.forEach(e => e.teleport(entity_to.position.scaled(1/32)));
return false;
player_from.teleport(player_to.position.clone());
} else if(args.length === 3) { } else if(args.length === 3) {
let x = getPos(args[0], 'x'); let x = serv.posFromString(args[0], player.position.x / 32);
let y = getPos(args[1], 'y'); let y = serv.posFromString(args[1], player.position.y / 32);
let z = getPos(args[2], 'z'); let z = serv.posFromString(args[2], player.position.z / 32);
player.teleport(new Vec3(x, y, z)); player.teleport(new Vec3(x, y, z));
} else if(args.length === 4) { } else if(args.length === 4) {
let player_from; let entities_from = player.selectorString(args[0]);
if(!(player_from = serv.getPlayer(args[0]))) entities_from.forEach(e => e.teleport(new Vec3(
return false; serv.posFromString(args[1], e.position.x / 32),
serv.posFromString(args[2], e.position.y / 32),
let x = getPos(args[1], 'x', player_from); serv.posFromString(args[3], e.position.z / 32)
let y = getPos(args[2], 'y', player_from); )));
let z = getPos(args[3], 'z', player_from);
player_from.teleport(new Vec3(x, y, z));
} }
} }
}); });

View file

@ -39,55 +39,20 @@ module.exports.player=function(player)
headYaw: convYaw headYaw: convYaw
}); });
}, () => { }, () => {
player.sendPosition(); player.sendSelfPosition();
}); });
} }
player._client.on('position', ({x,y,z,onGround} = {}) => player._client.on('position', ({x,y,z,onGround} = {}) => {
player.sendRelativePositionChange((new Vec3(x, y, z)).toFixedPosition(), onGround)); player.sendPosition((new Vec3(x, y, z)).toFixedPosition(), onGround);
});
player._client.on('position_look', ({x,y,z,onGround,yaw,pitch} = {}) => { player._client.on('position_look', ({x,y,z,onGround,yaw,pitch} = {}) => {
player.sendRelativePositionChange((new Vec3(x, y, z)).toFixedPosition(), onGround); player.sendPosition((new Vec3(x, y, z)).toFixedPosition(), onGround);
sendLook(yaw,pitch,onGround); sendLook(yaw,pitch,onGround);
}); });
player.sendRelativePositionChange = (newPosition, onGround) => { player.sendSelfPosition = () => {
return player.behavior('move', {
onGround: onGround,
position: newPosition
}, async ({onGround, position}) => {
if (player.position.distanceTo(new Vec3(0, 0, 0)) != 0) {
var diff = position.minus(player.position);
if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127)
{
player._writeOthersNearby('entity_teleport', {
entityId:player.id,
x: position.x,
y: position.y,
z: position.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
});
}
}
player.position = position;
player.onGround = onGround;
}, () => {
player.sendPosition();
});
};
player.sendPosition = () => {
player._client.write('position', { player._client.write('position', {
x: player.position.x/32, x: player.position.x/32,
y: player.position.y/32, y: player.position.y/32,
@ -99,43 +64,46 @@ module.exports.player=function(player)
}; };
player.teleport = async (position) => { player.teleport = async (position) => {
await player.sendRelativePositionChange(position, false); var notCancelled = await player.sendPosition(position.scaled(32).floored(), false, true);
player.sendPosition(); if (notCancelled) player.sendSelfPosition();
} }
}; };
module.exports.entity=function(entity,serv){ module.exports.entity=function(entity,serv){
entity.sendPosition = ({oldPos,onGround}) => { entity.sendPosition = (position, onGround, teleport=false) => {
entity.behavior('move', { if (typeof position == 'undefined') throw new Error('undef');
old: oldPos, if (entity.position.equals(position) && entity.onGround == onGround) return Promise.resolve();
onGround: onGround return entity.behavior('move', {
}, ({old,onGround}) => { position: position,
var diff = entity.position.minus(old); onGround: onGround,
teleport: teleport
}, ({position,onGround}) => {
var diff = position.minus(entity.position);
if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127) if(diff.abs().x>127 || diff.abs().y>127 || diff.abs().z>127)
entity._writeOthersNearby('entity_teleport', { entity._writeOthersNearby('entity_teleport', {
entityId: entity.id, entityId: entity.id,
x: entity.position.x, x: position.x,
y: entity.position.y, y: position.y,
z: entity.position.z, z: position.z,
yaw: entity.yaw, yaw: entity.yaw,
pitch: entity.pitch, pitch: entity.pitch,
onGround: onGround onGround: onGround
}); });
else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) serv._writeNearby('rel_entity_move', { else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) entity._writeOthersNearby('rel_entity_move', {
entityId: entity.id, entityId: entity.id,
dX: diff.x, dX: diff.x,
dY: diff.y, dY: diff.y,
dZ: diff.z, dZ: diff.z,
onGround: onGround onGround: onGround
}, entity); });
entity.position = position;
entity.onGround = onGround;
}, () => { }, () => {
entity.position = oldPos; if (entity.type == 'player') player.sendSelfPosition();
}); });
}; };
entity.sendVelocity = (vel, maxVel) => { entity.sendVelocity = (vel, maxVel) => {
var velocity = vel.scaled(32).floored(); // Make fixed point var velocity = vel.scaled(32).floored(); // Make fixed point
var maxVelocity = maxVel.scaled(32).floored(); var maxVelocity = maxVel.scaled(32).floored();
@ -152,6 +120,10 @@ module.exports.entity=function(entity,serv){
} }
}; };
entity.teleport = (pos) => { // Overwritten in players inject above
entity.sendPosition(pos.scaled(32), false, true);
}
function addVelocityWithMax(current, newVel, max) { function addVelocityWithMax(current, newVel, max) {
var x, y, z; var x, y, z;
if (current.x > max.x || current.x < -max.x) x = current.x; if (current.x > max.x || current.x < -max.x) x = current.x;

View file

@ -159,7 +159,7 @@ module.exports.player=function(player,serv,settings) {
await player.sendMap(); await player.sendMap();
player.sendPosition(); player.sendSelfPosition();
player.emit('change_world'); player.emit('change_world');
await player.waitPlayerLogin(); await player.waitPlayerLogin();