Continue refactoring : client now in a player object, methods added to that player object, progress on #3

This commit is contained in:
Romain Beaumont 2015-08-26 20:12:39 +02:00
parent 3070acff44
commit 2e371e741f
13 changed files with 460 additions and 334 deletions

View file

@ -19,7 +19,7 @@ Before running or building it is recommended that you configure the server in co
Documentation for how to operate and how to customize your server are coming soon!
## Dev Documentation
For development see [api.md](docs/api.md)
For development see [api.md](doc/api.md)
## Contributors

151
doc/api.md Normal file
View file

@ -0,0 +1,151 @@
# API
## Classes
### CraftyJS.Entity
Entities represent players, mobs, and objects. They are emitted
in many events, and you can access your own entity with `bot.entity`.
#### entity.id
#### entity.type
Choices:
* `player`
* `mob`
* `object`
* `global` - lightning
* `orb` - experience orb.
* `other` - introduced with a recent Minecraft update and not yet recognized or used by a third-party mod
#### entity.username
If the entity type is `player`, this field will be set.
#### entity.mobType
If the entity type is `mob`, this field will be set.
#### entity.displayName
Field set for mob and object. A long name in multiple words.
#### entity.entityType
Field set for mob and object. The numerical type of the entity (1,2,...)
#### entity.kind
Field set for mob and object. The kind of entity (for example Hostile mobs, Passive mobs, NPCs).
#### entity.name
Field set for mob and object. A short name for the entity.
#### entity.objectType
If the entity type is `object`, this field will be set.
#### entity.count
If the entity type is `orb`, this field will be how much experience you
get from collecting the orb.
#### entity.position
#### entity.velocity
#### entity.yaw
#### entity.pitch
#### entity.height
#### entity.onGround
#### entity.equipment[5]
* `0` - held item
* `1` - shoes
* `2` - legging
* `3` - torso
* `4` - head
#### entity.heldItem
Equivalent to `entity.equipment[0]`.
#### entity.metadata
See http://wiki.vg/Entities#Entity_Metadata_Format for more details.
## MCServer
### CraftyJS.createMCServer(options)
Create and return an instance of the class MCServer.
options is an object containing the settings
### Properties
#### serv.entityMaxId
Current maximum entity id
#### serv.players
Array of connected players
#### serv.uuidToPlayer
Object uuid to players
#### serv.world
The map
### Methods
#### serv.createLog()
create the log file
#### serv.log(message)
logs a `message`
## Player
### Properties
#### player.entity
The entity of the player, of type `CraftyJS.Entity`
### Methods
#### player.login()
login
### player.others(client)
return the other players than `player`
### Low level properties
#### player._client
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`

View file

@ -1,50 +0,0 @@
# API
## MCServer
### CraftyJS.createMCServer(options)
Create and return an instance of the class MCServer.
options is an object containing the settings
### Properties
#### serv.entityMaxId
Current maximum entity id
#### serv.playersConnected
Array of connected players
#### serv.uuidToPlayer
Object uuid to players
#### serv.world
The map
### Methods
### serv.login(client)
login `client`
### serv.createLog()
create the log file
### serv.log(message)
logs a `message`
### serv.otherClients(client)
return the other clients than `client`
### serv.writeOthers(client,packetName, packetFields)
write to other clients than `client` the packet `packetName` with fields `packetFields`

View file

@ -4,7 +4,7 @@ var util = require('util');
var path = require('path');
var requireIndex = require('requireindex');
var serverPlugins = requireIndex(path.join(__dirname, 'lib', 'serverPlugins'));
var clientPlugins = requireIndex(path.join(__dirname, 'lib', 'clientPlugins'));
var playerPlugins = requireIndex(path.join(__dirname, 'lib', 'playerPlugins'));
module.exports = {
createMCServer:createMCServer
@ -46,9 +46,11 @@ MCServer.prototype.connect = function(options) {
});
self._server.on('login', function (client) {
for(var pluginName in clientPlugins) {
clientPlugins[pluginName](self, client, options);
var player={};
player._client=client;
for(var pluginName in playerPlugins) {
playerPlugins[pluginName](self, player, options);
}
self.login(client);
player.login();
});
};

View file

@ -1,35 +0,0 @@
var vec3 = require("vec3");
module.exports=inject;
function toFixedPosition(p)
{
return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32))
}
function inject(serv,client)
{
client.on('position', function (packet) {
var position = new vec3(packet.x, packet.y, packet.z);
var onGround = packet.onGround;
sendRelativePositionChange(client, toFixedPosition(position), onGround);
});
function sendRelativePositionChange(client, newPosition, onGround) {
if (serv.uuidToPlayer[client.uuid].position) {
var diff = newPosition.minus(serv.uuidToPlayer[client.uuid].position);
if (diff.distanceTo(new vec3(0, 0, 0)) != 0) {
serv.writeOthers(client,'rel_entity_move', {
entityId: client.id,
dX: diff.x,
dY: diff.y,
dZ: diff.z,
onGround: onGround
});
}
}
serv.uuidToPlayer[client.uuid].position = newPosition;
serv.uuidToPlayer[client.uuid].onGround = onGround;
}
}

24
lib/entity.js Normal file
View file

@ -0,0 +1,24 @@
var Vec3 = require('vec3').Vec3;
module.exports = Entity;
function Entity(id) {
this.id = id;
this.type = null;
this.position = new Vec3(0, 0, 0);
this.velocity = new Vec3(0, 0, 0);
this.yaw = 0;
this.pitch = 0;
this.onGround = true;
this.height = 0;
this.effects = {};
// 0 = held item, 1-4 = armor slot
this.equipment = new Array(5);
this.heldItem = this.equipment[0]; // shortcut to equipment[0]
this.isValid = true;
}
Entity.prototype.setEquipment = function(index, item) {
this.equipment[index] = item;
this.heldItem = this.equipment[0];
};

View file

@ -0,0 +1,16 @@
module.exports=inject;
function inject(serv,player)
{
player._writeOthers=function(packetName, packetFields) {
player.getOthers().forEach(function (otherPlayer) {
otherPlayer._client.write(packetName, packetFields);
});
};
player.getOthers = function() {
return serv.players.filter(function (otherPlayer) {
return otherPlayer != player
});
};
}

227
lib/playerPlugins/login.js Normal file
View file

@ -0,0 +1,227 @@
var Entity=require("../entity");
module.exports=inject;
function transformUuid(s)
{
return s.split("-").map(function(item) { return parseInt(item, 16); });
}
function inject(serv,player)
{
player.login=login;
function addPlayer()
{
serv.entityMaxId++;
player.entity=new Entity(serv.entityMaxId);
serv.players.push(player);
serv.uuidToPlayer[player._client.uuid] = player;
}
function sendLogin()
{
// send init data so client will start rendering world
player._client.write('login', {
entityId: player.entity.id,
levelType: 'default',
gameMode: 0,
dimension: 0,
difficulty: 0,
reducedDebugInfo: false,
maxPlayers: serv._server.maxPlayers
});
}
function sendMap()
{
player._client.write('map_chunk', {
x: 0,
z: 0,
groundUp: true,
bitMap: 0xffff,
chunkData: serv.world.dump()
});
}
function sendInitialPosition()
{
player._client.write('position', {
x: 6,
y: 53,
z: 6,
yaw: 0,
pitch: 0,
flags: 0x00
});
}
function updateTime()
{
player._client.write('update_time', {
age: [0, 0],
time: [0, 1]
});
}
function updateGameState()
{
player._client.write('game_state_change', {
reason: 3,
gameMode: 0
});
}
function announceLogin()
{
player._client.on('chat', function (data) {
var message = '<' + client.username + '>' + ' ' + data.message;
playerChat(message, client.username);
console.log("[INFO] " + message);
serv.log("[INFO] " + message);
});
}
function fillTabList()
{
player._writeOthers('player_info',{
action: 0,
data: [{
UUID: transformUuid(player._client.uuid),
name: player._client.username,
properties: [],
gamemode: 0,
ping: 1,
hasDisplayName: true,
displayName: player._client.username
}]
});
player._client.write('player_info', {
action: 0,
data: serv.players
.map(function (otherPlayer) {
return {
UUID: transformUuid(otherPlayer._client.uuid),
name: otherPlayer._client.username,
properties: [],
gamemode: 0,
ping: 1,
hasDisplayName: true,
displayName: otherPlayer._client.username
};
})
});
}
function spawn()
{
player.getOthers().forEach(function (otherPlayer) {
var pos = otherPlayer.entity.position;
player._client.write('named_entity_spawn', {
entityId: otherPlayer.entity.id,
playerUUID: transformUuid(otherPlayer._client.uuid),
x: pos ? pos.x : 6 * 32,
y: pos ? pos.y : 53 * 32,
z: pos ? pos.z : 6 * 32,
yaw: 0,
pitch: 0,
currentItem: 0,
metadata: []
});
});
player._writeOthers('named_entity_spawn',{
entityId: player.entity.id,
playerUUID: transformUuid(player._client.uuid),
x: 6 * 32,
y: 53 * 32,
z: 6 * 32,
yaw: 0,
pitch: 0,
currentItem: 0,
metadata: []
});
}
function announceJoin()
{
broadcast(player._client.username + ' joined the game.', "yellow");
var addr = player._client.socket.remoteAddress + ':' + player._client.socket.remotePort;
console.log("[INFO]: " + player._client.username + ' connected', '(' + addr + ')');
serv.log("[INFO]: " + player._client.username + ' connected', '(' + addr + ')');
}
function playerChat(message, exclude, username) {
var client;
//translate = username ? 'chat.type.text' : 'chat.type.text';
username = username || '';
for(var clientId in server.clients) {
if(!serv._server.clients.hasOwnProperty(clientId)) continue;
client = serv._server.clients[clientId];
if(client !== exclude) {
var msg = {
"text": username + message
};
client.write('chat', { message: JSON.stringify(msg), position: 0 });
}
}
}
function broadcast(message, color) {
var client;
for(var clientId in serv._server.clients) {
if(!serv._server.clients.hasOwnProperty(clientId)) continue;
client = serv._server.clients[clientId];
var msg = {
"text": message,
"color": color
};
client.write('chat', { message: JSON.stringify(msg), position: 0 });
}
}
function login()
{
if (serv.uuidToPlayer[player._client.uuid]) {
player._client.end("You are already connected");
return;
}
addPlayer();
sendLogin();
sendMap();
sendInitialPosition();
console.log("[INFO]: position written, player spawning...");
serv.log("[INFO]: position written, player spawning...");
updateTime();
updateGameState();
announceLogin();
fillTabList();
spawn();
announceJoin();
var addr = player._client.socket.remoteAddress + ':' + player._client.socket.remotePort;
player._client.on('end', function () {
broadcast(player._client.username + ' joined the game.', "yellow");
console.log("[INFO]: " + player._client.username + ' disconnected', '(' + addr + ')');
serv.log("[INFO]: " + player._client.username + ' disconnected', '(' + addr + ')');
});
player._client.on('error', function (error) {
console.log('[ERR] ' + error.stack);
serv.log('[ERR]: Client: ' + error.stack);
});
}
}

View file

@ -0,0 +1,34 @@
var vec3 = require("vec3");
module.exports=inject;
function toFixedPosition(p)
{
return new vec3(Math.floor(p.x*32),Math.floor(p.y*32),Math.floor(p.z*32))
}
function inject(serv,player)
{
player._client.on('position', function (packet) {
var position = new vec3(packet.x, packet.y, packet.z);
var onGround = packet.onGround;
sendRelativePositionChange(toFixedPosition(position), onGround);
});
function sendRelativePositionChange(newPosition, onGround) {
if (player.entity.position.distanceTo(new vec3(0, 0, 0)) != 0) {
var diff = newPosition.minus(player.entity.position);
if (diff.distanceTo(new vec3(0, 0, 0)) != 0) {
player._writeOthers('rel_entity_move', {
entityId: player.entity.id,
dX: diff.x,
dY: diff.y,
dZ: diff.z,
onGround: onGround
});
}
}
player.entity.position = newPosition;
player.entity.onGround = onGround;
}
}

View file

@ -1,16 +0,0 @@
module.exports=inject;
function inject(serv)
{
serv.writeOthers=function(client,packetName, packetFields) {
serv.otherClients(client).forEach(function (otherClient) {
otherClient.write(packetName, packetFields);
});
};
serv.otherClients = function(client) {
return serv.playersConnected.filter(function (otherClient) {
return otherClient != client
});
};
}

View file

@ -1,227 +0,0 @@
module.exports=inject;
function transformUuid(s)
{
return s.split("-").map(function(item) { return parseInt(item, 16); });
}
function inject(serv)
{
serv.login=login;
function addPlayer(client)
{
serv.entityMaxId++;
client.id = serv.entityMaxId;
serv.playersConnected.push(client);
console.log(client.uuid);
serv.uuidToPlayer[client.uuid] = client;
}
function sendLogin(client)
{
// send init data so client will start rendering world
client.write('login', {
entityId: client.id,
levelType: 'default',
gameMode: 0,
dimension: 0,
difficulty: 0,
reducedDebugInfo: false,
maxPlayers: serv._server.maxPlayers
});
}
function sendMap(client)
{
client.write('map_chunk', {
x: 0,
z: 0,
groundUp: true,
bitMap: 0xffff,
chunkData: serv.world.dump()
});
}
function sendInitialPosition(client)
{
client.write('position', {
x: 6,
y: 53,
z: 6,
yaw: 0,
pitch: 0,
flags: 0x00
});
}
function updateTime(client)
{
client.write('update_time', {
age: [0, 0],
time: [0, 1]
});
}
function updateGameState(client)
{
client.write('game_state_change', {
reason: 3,
gameMode: 0
});
}
function announceLogin(client)
{
client.on('chat', function (data) {
var message = '<' + client.username + '>' + ' ' + data.message;
playerChat(message, client.username);
console.log("[INFO] " + message);
serv.log("[INFO] " + message);
});
}
function fillTabList(client)
{
serv.otherClients(client).forEach(function (otherClient) {
otherClient.write('player_info', {
action: 0,
data: [{
UUID: transformUuid(client.uuid),
name: client.username,
properties: [],
gamemode: 0,
ping: 1,
hasDisplayName: true,
displayName: client.username
}]
});
});
client.write('player_info', {
action: 0,
data: serv.playersConnected
.map(function (otherClient) {
return {
UUID: transformUuid(otherClient.uuid),
name: otherClient.username,
properties: [],
gamemode: 0,
ping: 1,
hasDisplayName: true,
displayName: otherClient.username
};
})
});
}
function spawn(client)
{
serv.otherClients(client).forEach(function (otherClient) {
var pos = serv.uuidToPlayer[otherClient.uuid].position;
client.write('named_entity_spawn', {
entityId: otherClient.id,
playerUUID: transformUuid(otherClient.uuid),
x: pos ? pos.x : 6 * 32,
y: pos ? pos.y : 53 * 32,
z: pos ? pos.z : 6 * 32,
yaw: 0,
pitch: 0,
currentItem: 0,
metadata: []
});
otherClient.write('named_entity_spawn', {
entityId: client.id,
playerUUID: transformUuid(client.uuid),
x: 6 * 32,
y: 53 * 32,
z: 6 * 32,
yaw: 0,
pitch: 0,
currentItem: 0,
metadata: []
});
});
}
function announceJoin(client)
{
broadcast(client.username + ' joined the game.', "yellow");
var addr = client.socket.remoteAddress + ':' + client.socket.remotePort;
console.log("[INFO]: " + client.username + ' connected', '(' + addr + ')');
serv.log("[INFO]: " + client.username + ' connected', '(' + addr + ')');
}
function playerChat(message, exclude, username) {
var client;
//translate = username ? 'chat.type.text' : 'chat.type.text';
username = username || '';
for(var clientId in server.clients) {
if(!serv._server.clients.hasOwnProperty(clientId)) continue;
client = serv._server.clients[clientId];
if(client !== exclude) {
var msg = {
"text": username + message
};
client.write('chat', { message: JSON.stringify(msg), position: 0 });
}
}
}
function broadcast(message, color) {
var client;
for(var clientId in serv._server.clients) {
if(!serv._server.clients.hasOwnProperty(clientId)) continue;
client = serv._server.clients[clientId];
var msg = {
"text": message,
"color": color
};
client.write('chat', { message: JSON.stringify(msg), position: 0 });
}
}
function login(client)
{
if (serv.uuidToPlayer[client.uuid]) {
client.end("You are already connected");
return;
}
addPlayer(client);
sendLogin(client);
sendMap(client);
sendInitialPosition(client);
console.log("[INFO]: position written, player spawning...");
serv.log("[INFO]: position written, player spawning...");
updateTime(client);
updateGameState(client);
announceLogin(client);
fillTabList(client);
spawn(client);
announceJoin(client);
var addr = client.socket.remoteAddress + ':' + client.socket.remotePort;
client.on('end', function () {
broadcast(client.username + ' joined the game.', "yellow");
console.log("[INFO]: " + client.username + ' disconnected', '(' + addr + ')');
serv.log("[INFO]: " + client.username + ' disconnected', '(' + addr + ')');
});
client.on('error', function (error) {
console.log('[ERR] ' + error.stack);
serv.log('[ERR]: Client: ' + error.stack);
});
}
}

View file

@ -3,6 +3,6 @@ module.exports=inject;
function inject(serv)
{
serv.entityMaxId=0;
serv.playersConnected=[];
serv.players=[];
serv.uuidToPlayer={};
}