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! Documentation for how to operate and how to customize your server are coming soon!
## Dev Documentation ## Dev Documentation
For development see [api.md](docs/api.md) For development see [api.md](doc/api.md)
## Contributors ## 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 path = require('path');
var requireIndex = require('requireindex'); var requireIndex = require('requireindex');
var serverPlugins = requireIndex(path.join(__dirname, 'lib', 'serverPlugins')); 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 = { module.exports = {
createMCServer:createMCServer createMCServer:createMCServer
@ -46,9 +46,11 @@ MCServer.prototype.connect = function(options) {
}); });
self._server.on('login', function (client) { self._server.on('login', function (client) {
for(var pluginName in clientPlugins) { var player={};
clientPlugins[pluginName](self, client, options); 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) function inject(serv)
{ {
serv.entityMaxId=0; serv.entityMaxId=0;
serv.playersConnected=[]; serv.players=[];
serv.uuidToPlayer={}; serv.uuidToPlayer={};
} }