diff --git a/doc/CONTRIBUTE.md b/doc/CONTRIBUTE.md index df0847a..1f93ccf 100644 --- a/doc/CONTRIBUTE.md +++ b/doc/CONTRIBUTE.md @@ -5,93 +5,67 @@ Directory architecture : * app.js: specific settings and actually start the server -* index.js: contain the generic server implementation -* lib/: contain the classes and functions used in the plugins - * serverPlugins/: server plugins that do things general to the server, - properties and method are added to the server object in them - * playerPlugins/: player plugins that do things for each player, properties and method are added to the player object in them +* dist/: Contains "compiled" code (go to current directory in console and type `gulp` to generate this) +* src/: Source files for the project +* src/index.js: contain the generic server implementation +* src/lib: contain the classes and functions used in the plugins + * 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 -module.exports=inject; -function inject(serv) -{ - // add methods and properties to serv +// Each of these are called an "inject" because you're injecting properties, events, methods, or data into the objects + +module.exports.server = function(serv) { // Create your server events here + serv.spawnPoint = ...; + serv.on('...', ...); } -``` -Structure of a player plugin : - -```js -module.exports=inject; - -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.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 + entity.on('...', ...); + // serv.on('...', ...); NOOOO } + +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 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 -in log.js of playerPlugins or serverPlugins. +logging uses methods and events from `log.js`. These include `serv.log(message)` and `serv.emit('error', err)`. ## 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 -module.exports=init; +Let's say you called your module fs-flying-horses and you published it to npm. -function init(flying-squid) { - return inject; -} +Now people can use install your plugin by simply typing: -function inject(serv) -{ - // add methods and properties to serv -} -``` +```npm install fs-flying-horses``` -In the init function, you can use anything flying-squid provide -(see [index.js](https://github.com/mhsjlw/flying-squid/blob/master/index.js#L11)). +### Testing your Plugin -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. - -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 -} -``` +## Conclusion 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. diff --git a/src/index.js b/src/index.js index 5a676d6..dd54436 100644 --- a/src/index.js +++ b/src/index.js @@ -13,7 +13,8 @@ module.exports = { Command:require("./lib/command"), version:require("./lib/version"), generations:require("./lib/generations"), - experience:require("./lib/experience") + experience:require("./lib/experience"), + UserError:require("./lib/UserError") }; function createMCServer(options) { @@ -39,5 +40,7 @@ class MCServer extends EventEmitter { this._server.on('error', error => this.emit('error',error)); this._server.on('listening', () => this.emit('listening',this._server.socketServer.address().port)); this.emit('asap'); + + process.on('unhandledRejection', err => this.emit('error',err)); } } \ No newline at end of file diff --git a/src/lib/UserError.js b/src/lib/UserError.js new file mode 100644 index 0000000..c111340 --- /dev/null +++ b/src/lib/UserError.js @@ -0,0 +1,5 @@ +class UserError extends Error { + +} + +module.exports = UserError; \ No newline at end of file diff --git a/src/lib/behavior.js b/src/lib/behavior.js index 6469d95..f0274fd 100644 --- a/src/lib/behavior.js +++ b/src/lib/behavior.js @@ -13,13 +13,22 @@ module.exports = (obj) => { defaultCancel = dC; }; + var resp; + 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).catch((err)=> setTimeout(() => {throw err;},0)); - else if (cancelFunc && defaultCancel) await cancelFunc(data).catch((err)=> setTimeout(() => {throw err;},0)); - + if (!hiddenCancelled && !cancelled) { + 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)); - return data; + + return resp; } }; \ No newline at end of file diff --git a/src/lib/command.js b/src/lib/command.js index 2e827ba..463c7f7 100644 --- a/src/lib/command.js +++ b/src/lib/command.js @@ -3,6 +3,9 @@ class Command { this.params = params; this.parent = parent; 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(); } @@ -44,9 +47,9 @@ class Command { updateHistory() { var all = '(.+?)'; - var list = [this.params.base]; + var list = [this.base]; 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) => { @@ -56,6 +59,7 @@ class Command { if(this.path) this.hash[this.path] = this; }); + this.uniqueHash[this.base] = this; } add(params) { diff --git a/src/lib/plugins/blocks.js b/src/lib/plugins/blocks.js index d47c383..7a77158 100644 --- a/src/lib/plugins/blocks.js +++ b/src/lib/plugins/blocks.js @@ -30,24 +30,18 @@ module.exports.player=function(player,serv) player.commands.add({ base: 'setblock', - info: 'to put a block', - usage: '/setblock ', + info: 'set a block at a position', + usage: '/setblock [data]', op: true, 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; return results; }, action(params) { - var res = params.map((num, i) => { // parseInt parameters - if (num.indexOf('~') == 0) { - return (player.position[['', 'x', 'y', 'z'][i]] >> 5) + parseInt(num.slice(1) || 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]); + var res = params.slice(1, 4); + res = res.map((val, i) => serv.posFromString(val, player.position[['x','y','z'][i]] / 32)) + player.setBlock(new Vec3(res[0], res[1], res[2]).floored(), params[4], params[5] || 0); } }); }; \ No newline at end of file diff --git a/src/lib/plugins/commands.js b/src/lib/plugins/commands.js index 0999d31..9b3fe2e 100644 --- a/src/lib/plugins/commands.js +++ b/src/lib/plugins/commands.js @@ -1,32 +1,55 @@ +var Vec3 = require("vec3").Vec3; +var UserError = require('flying-squid').UserError; + module.exports.player=function(player, serv) { player.commands.add({ base: 'help', info: 'to show all commands', usage: '/help [command]', - action(params) { - var c = params[0]; - var hash = player.commands.hash; + parse(str) { + var params = str.split(' '); + 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 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]); + var PAGE_LENGTH = 8; - if(hash[key].params.info && (player.op || !hash[key].params.op)) { - 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(', ') + ')'; - } + var found = Object.keys(hash).filter(h => (h + ' ').indexOf((search && search + ' ') || '') == 0); - 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) { var arr = serv.selectorString(sel, player.position.scaled(1/32), player.world); - if (arr instanceof Error) return arr.toString(); - else if (arr == null) return 'Could not find player'; + if (arr == null) return 'Could not find player'; 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); } 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) { function shuffleArray(array) { @@ -126,7 +153,7 @@ module.exports.server = function(serv) { serv.selector = (type, opt) => { 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' ? count : @@ -209,12 +236,13 @@ module.exports.server = function(serv) { else return sample.slice(count); // Negative, returns from end } - serv.selectorString = (str, pos, world) => { + serv.selectorString = (str, pos, world, allowUser=true) => { pos = pos.clone(); var player = serv.getPlayer(str); if (!player && str[0] != '@') return null; + else if (player) return allowUser ? [player] : null; 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 = { a: 'all', r: 'random', @@ -227,10 +255,10 @@ module.exports.server = function(serv) { var err; opt.forEach(o => { 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]}); }); - if (err) return err; + if (err) throw err; var optConversion = { type: 'type', @@ -269,4 +297,11 @@ module.exports.server = function(serv) { 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'); + }; } \ No newline at end of file diff --git a/src/lib/plugins/entities.js b/src/lib/plugins/entities.js index b0b580b..c373992 100644 --- a/src/lib/plugins/entities.js +++ b/src/lib/plugins/entities.js @@ -19,9 +19,8 @@ module.exports.server=function(serv,options) { } } if (!entity.velocity || !entity.size) return; - var oldPosAndOnGround = await entity.calculatePhysics(delta); - if (!oldPosAndOnGround.oldPos.equals(new Vec3(0,0,0))) - if (entity.type == 'mob') entity.sendPosition(oldPosAndOnGround); + var posAndOnGround = await entity.calculatePhysics(delta); + if (entity.type == 'mob') entity.sendPosition(posAndOnGround.position, posAndOnGround.onGround); }) ).catch((err)=> setTimeout(() => {throw err;},0)); }); diff --git a/src/lib/plugins/login.js b/src/lib/plugins/login.js index b685927..cbf721d 100644 --- a/src/lib/plugins/login.js +++ b/src/lib/plugins/login.js @@ -158,7 +158,7 @@ module.exports.player=function(player,serv) sendLogin(); await player.sendMap(); player.sendSpawnPosition(); - player.sendPosition(); + player.sendSelfPosition(); player.updateHealth(player.health); diff --git a/src/lib/plugins/physics.js b/src/lib/plugins/physics.js index ba3b183..1a1a70d 100644 --- a/src/lib/plugins/physics.js +++ b/src/lib/plugins/physics.js @@ -27,14 +27,14 @@ module.exports.entity=function(entity){ 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); - entity.position.y += getMoveAmount('y', yBlock, entity, delta, sizeSigned.y); - entity.position.z += getMoveAmount('z', zBlock, entity, delta, sizeSigned.z); + newPos.x += getMoveAmount('x', xBlock, entity, delta, sizeSigned.x); + newPos.y += getMoveAmount('y', yBlock, entity, delta, sizeSigned.y); + 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) }); - return { oldPos: oldPos, onGround: yBlock} + return { position: newPos, onGround: yBlock} }; diff --git a/src/lib/plugins/respawn.js b/src/lib/plugins/respawn.js index a1bdc89..c6ce56e 100644 --- a/src/lib/plugins/respawn.js +++ b/src/lib/plugins/respawn.js @@ -9,7 +9,7 @@ module.exports.player=function(player) gamemode:player.gameMode, levelType:'default' }); - player.sendPosition(); + player.sendSelfPosition(); player.updateHealth(20); player.nearbyEntities=[]; player.updateAndSpawn(); diff --git a/src/lib/plugins/tp.js b/src/lib/plugins/tp.js index 0c0895d..3dc2191 100644 --- a/src/lib/plugins/tp.js +++ b/src/lib/plugins/tp.js @@ -2,11 +2,6 @@ var Vec3 = require("vec3").Vec3; 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({ base: 'teleport', aliases: ['tp'], @@ -14,34 +9,29 @@ module.exports.player = (player, serv) => { usage: '/teleport [target player] [y] [z]', op: true, 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) { - if(args.length === 2 && args[0] !== args[1]) { - let player_from; - let player_to; + if(args.length === 2) { + let entities_from = player.selectorString(args[0]); + let entity_to = player.selectorString(args[1])[0]; - if(!(player_from = serv.getPlayer(args[0])) || !(player_to = serv.getPlayer(args[1]))) - return false; - - player_from.teleport(player_to.position.clone()); + entities_from.forEach(e => e.teleport(entity_to.position.scaled(1/32))); } else if(args.length === 3) { - let x = getPos(args[0], 'x'); - let y = getPos(args[1], 'y'); - let z = getPos(args[2], 'z'); + let x = serv.posFromString(args[0], player.position.x / 32); + let y = serv.posFromString(args[1], player.position.y / 32); + let z = serv.posFromString(args[2], player.position.z / 32); player.teleport(new Vec3(x, y, z)); + } else if(args.length === 4) { - let player_from; + let entities_from = player.selectorString(args[0]); - if(!(player_from = serv.getPlayer(args[0]))) - return false; - - let x = getPos(args[1], 'x', player_from); - let y = getPos(args[2], 'y', player_from); - let z = getPos(args[3], 'z', player_from); - - player_from.teleport(new Vec3(x, y, z)); + entities_from.forEach(e => e.teleport(new Vec3( + serv.posFromString(args[1], e.position.x / 32), + serv.posFromString(args[2], e.position.y / 32), + serv.posFromString(args[3], e.position.z / 32) + ))); } } }); diff --git a/src/lib/plugins/updatePositions.js b/src/lib/plugins/updatePositions.js index f10283e..55fe0af 100644 --- a/src/lib/plugins/updatePositions.js +++ b/src/lib/plugins/updatePositions.js @@ -39,55 +39,20 @@ module.exports.player=function(player) headYaw: convYaw }); }, () => { - player.sendPosition(); + player.sendSelfPosition(); }); } - player._client.on('position', ({x,y,z,onGround} = {}) => - player.sendRelativePositionChange((new Vec3(x, y, z)).toFixedPosition(), onGround)); + player._client.on('position', ({x,y,z,onGround} = {}) => { + player.sendPosition((new Vec3(x, y, z)).toFixedPosition(), onGround); + }); 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); }); - player.sendRelativePositionChange = (newPosition, onGround) => { - 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.sendSelfPosition = () => { player._client.write('position', { x: player.position.x/32, y: player.position.y/32, @@ -99,43 +64,46 @@ module.exports.player=function(player) }; player.teleport = async (position) => { - await player.sendRelativePositionChange(position, false); - player.sendPosition(); + var notCancelled = await player.sendPosition(position.scaled(32).floored(), false, true); + if (notCancelled) player.sendSelfPosition(); } }; module.exports.entity=function(entity,serv){ - entity.sendPosition = ({oldPos,onGround}) => { - entity.behavior('move', { - old: oldPos, - onGround: onGround - }, ({old,onGround}) => { - var diff = entity.position.minus(old); - + entity.sendPosition = (position, onGround, teleport=false) => { + if (typeof position == 'undefined') throw new Error('undef'); + if (entity.position.equals(position) && entity.onGround == onGround) return Promise.resolve(); + return entity.behavior('move', { + position: position, + 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) entity._writeOthersNearby('entity_teleport', { entityId: entity.id, - x: entity.position.x, - y: entity.position.y, - z: entity.position.z, + x: position.x, + y: position.y, + z: 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', { + else if (diff.distanceTo(new Vec3(0, 0, 0)) != 0) entity._writeOthersNearby('rel_entity_move', { entityId: entity.id, dX: diff.x, dY: diff.y, dZ: diff.z, onGround: onGround - }, entity); + }); + + entity.position = position; + entity.onGround = onGround; }, () => { - entity.position = oldPos; + if (entity.type == 'player') player.sendSelfPosition(); }); }; - - entity.sendVelocity = (vel, maxVel) => { var velocity = vel.scaled(32).floored(); // Make fixed point 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) { var x, y, z; if (current.x > max.x || current.x < -max.x) x = current.x; diff --git a/src/lib/plugins/world.js b/src/lib/plugins/world.js index 907b444..3dd9f9e 100644 --- a/src/lib/plugins/world.js +++ b/src/lib/plugins/world.js @@ -159,7 +159,7 @@ module.exports.player=function(player,serv,settings) { await player.sendMap(); - player.sendPosition(); + player.sendSelfPosition(); player.emit('change_world'); await player.waitPlayerLogin();