-High Level protocol optimization (should be smaller)

-Ability to set compression to ENet packets (check API)
-Fixed small bug in StringDB that lead to duplicate empty strings
-Added a new class, StreamPeerBuffer, useful to create your own tightly packed data
This commit is contained in:
Juan Linietsky 2016-08-22 01:14:08 -03:00
parent 61cb8fd76c
commit cbbcf72703
12 changed files with 515 additions and 85 deletions

View file

@ -60,6 +60,10 @@ int Compression::compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,M
strm.avail_in=p_src_size;
int aout = deflateBound(&strm,p_src_size);;
/*if (aout>p_src_size) {
deflateEnd(&strm);
return -1;
}*/
strm.avail_out=aout;
strm.next_in=(Bytef*)p_src;
strm.next_out=p_dst;
@ -107,19 +111,21 @@ int Compression::get_max_compressed_buffer_size(int p_src_size,Mode p_mode){
void Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode){
int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode){
switch(p_mode) {
case MODE_FASTLZ: {
int ret_size=0;
if (p_dst_max_size<16) {
uint8_t dst[16];
fastlz_decompress(p_src,p_src_size,dst,16);
ret_size = fastlz_decompress(p_src,p_src_size,dst,16);
copymem(p_dst,dst,p_dst_max_size);
} else {
fastlz_decompress(p_src,p_src_size,p_dst,p_dst_max_size);
ret_size = fastlz_decompress(p_src,p_src_size,p_dst,p_dst_max_size);
}
return;
return ret_size;
} break;
case MODE_DEFLATE: {
@ -130,7 +136,7 @@ void Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *
strm.avail_in= 0;
strm.next_in=Z_NULL;
int err = inflateInit(&strm);
ERR_FAIL_COND(err!=Z_OK);
ERR_FAIL_COND_V(err!=Z_OK,-1);
strm.avail_in=p_src_size;
strm.avail_out=p_dst_max_size;
@ -138,11 +144,12 @@ void Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *
strm.next_out=p_dst;
err = inflate(&strm,Z_FINISH);
int total = strm.total_out;
inflateEnd(&strm);
ERR_FAIL_COND(err!=Z_STREAM_END);
return;
ERR_FAIL_COND_V(err!=Z_STREAM_END,-1);
return total;
} break;
}
ERR_FAIL();
ERR_FAIL_V(-1);
}

View file

@ -43,7 +43,7 @@ public:
static int compress(uint8_t *p_dst, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ);
static int get_max_compressed_buffer_size(int p_src_size,Mode p_mode=MODE_FASTLZ);
static void decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ);
static int decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p_src, int p_src_size,Mode p_mode=MODE_FASTLZ);
Compression();
};

View file

@ -222,6 +222,7 @@ void StreamPeer::put_double(double p_val){
void StreamPeer::put_utf8_string(const String& p_string) {
CharString cs=p_string.utf8();
put_u32(p_string.length());
put_data((const uint8_t*)cs.get_data(),cs.length());
}
@ -348,8 +349,10 @@ String StreamPeer::get_string(int p_bytes){
ERR_FAIL_COND_V(p_bytes<0,String());
Vector<char> buf;
buf.resize(p_bytes+1);
get_data((uint8_t*)&buf[0],p_bytes);
Error err = buf.resize(p_bytes+1);
ERR_FAIL_COND_V(err!=OK,String());
err = get_data((uint8_t*)&buf[0],p_bytes);
ERR_FAIL_COND_V(err!=OK,String());
buf[p_bytes]=0;
return buf.ptr();
@ -359,8 +362,10 @@ String StreamPeer::get_utf8_string(int p_bytes){
ERR_FAIL_COND_V(p_bytes<0,String());
Vector<uint8_t> buf;
buf.resize(p_bytes);
get_data(buf.ptr(),p_bytes);
Error err = buf.resize(p_bytes);
ERR_FAIL_COND_V(err!=OK,String());
err = get_data(buf.ptr(),p_bytes);
ERR_FAIL_COND_V(err!=OK,String());
String ret;
ret.parse_utf8((const char*)buf.ptr(),buf.size());
@ -371,8 +376,10 @@ Variant StreamPeer::get_var(){
int len = get_32();
Vector<uint8_t> var;
var.resize(len);
get_data(var.ptr(),len);
Error err = var.resize(len);
ERR_FAIL_COND_V(err!=OK,Variant());
err = get_data(var.ptr(),len);
ERR_FAIL_COND_V(err!=OK,Variant());
Variant ret;
decode_variant(ret,var.ptr(),len);
@ -420,3 +427,127 @@ void StreamPeer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("get_utf8_string","bytes"),&StreamPeer::get_utf8_string);
ObjectTypeDB::bind_method(_MD("get_var:Variant"),&StreamPeer::get_var);
}
////////////////////////////////
void StreamPeerBuffer::_bind_methods() {
ObjectTypeDB::bind_method(_MD("seek","pos"),&StreamPeerBuffer::seek);
ObjectTypeDB::bind_method(_MD("get_size"),&StreamPeerBuffer::get_size);
ObjectTypeDB::bind_method(_MD("get_pos"),&StreamPeerBuffer::get_pos);
ObjectTypeDB::bind_method(_MD("resize","size"),&StreamPeerBuffer::resize);
ObjectTypeDB::bind_method(_MD("set_data_array","data"),&StreamPeerBuffer::set_data_array);
ObjectTypeDB::bind_method(_MD("get_data_array"),&StreamPeerBuffer::get_data_array);
ObjectTypeDB::bind_method(_MD("clear"),&StreamPeerBuffer::clear);
ObjectTypeDB::bind_method(_MD("duplicate:StreamPeerBuffer"),&StreamPeerBuffer::duplicate);
}
Error StreamPeerBuffer::put_data(const uint8_t* p_data,int p_bytes) {
if (p_bytes<=0)
return OK;
if (pointer+p_bytes > data.size()) {
data.resize(pointer+p_bytes);
}
DVector<uint8_t>::Write w = data.write();
copymem(&w[pointer],p_data,p_bytes);
pointer+=p_bytes;
return OK;
}
Error StreamPeerBuffer::put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent){
r_sent=p_bytes;
return put_data(p_data,p_bytes);
}
Error StreamPeerBuffer::get_data(uint8_t* p_buffer, int p_bytes){
int recv;
get_partial_data(p_buffer,p_bytes,recv);
if (recv!=p_bytes)
return ERR_INVALID_PARAMETER;
return OK;
}
Error StreamPeerBuffer::get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received){
if (pointer+p_bytes > data.size()) {
r_received=data.size()-pointer;
if (r_received<=0) {
r_received=0;
return OK; //you got 0
}
} else {
r_received=p_bytes;
}
DVector<uint8_t>::Read r = data.read();
copymem(p_buffer,r.ptr(),r_received);
}
int StreamPeerBuffer::get_available_bytes() const {
return data.size()-pointer;
}
void StreamPeerBuffer::seek(int p_pos){
ERR_FAIL_COND(p_pos < 0);
ERR_FAIL_COND(p_pos > data.size());
pointer=p_pos;
}
int StreamPeerBuffer::get_size() const{
return data.size();
}
int StreamPeerBuffer::get_pos() const {
return pointer;
}
void StreamPeerBuffer::resize(int p_size){
data.resize(p_size);
}
void StreamPeerBuffer::set_data_array(const DVector<uint8_t> & p_data){
data=p_data;
pointer=0;
}
DVector<uint8_t> StreamPeerBuffer::get_data_array() const{
return data;
}
void StreamPeerBuffer::clear() {
data.resize(0);
pointer=0;
}
Ref<StreamPeerBuffer> StreamPeerBuffer::duplicate() const {
Ref<StreamPeerBuffer> spb;
spb.instance();
spb->data=data;
}
StreamPeerBuffer::StreamPeerBuffer() {
pointer=0;
}

View file

@ -91,4 +91,40 @@ public:
StreamPeer() { big_endian=false; }
};
class StreamPeerBuffer : public StreamPeer {
OBJ_TYPE(StreamPeerBuffer,StreamPeer);
DVector<uint8_t> data;
int pointer;
protected:
static void _bind_methods();
public:
Error put_data(const uint8_t* p_data,int p_bytes);
Error put_partial_data(const uint8_t* p_data,int p_bytes, int &r_sent);
Error get_data(uint8_t* p_buffer, int p_bytes);
Error get_partial_data(uint8_t* p_buffer, int p_bytes,int &r_received);
virtual int get_available_bytes() const;
void seek(int p_pos);
int get_size() const;
int get_pos() const;
void resize(int p_size);
void set_data_array(const DVector<uint8_t> & p_data);
DVector<uint8_t> get_data_array() const;
void clear();
Ref<StreamPeerBuffer> duplicate() const;
StreamPeerBuffer();
};
#endif // STREAM_PEER_H

View file

@ -363,6 +363,7 @@ NodePath::NodePath(const String& p_path) {
from=i+1;
}
}
path=path.substr(0,subpath_pos);
@ -380,6 +381,8 @@ NodePath::NodePath(const String& p_path) {
last_is_slash=false;
}
}
if (slices==0 && !absolute && !property)
@ -413,6 +416,7 @@ NodePath::NodePath(const String& p_path) {
} else {
last_is_slash=false;
}
}

View file

@ -118,6 +118,7 @@ void register_core_types() {
ObjectTypeDB::register_type<Resource>();
ObjectTypeDB::register_type<FuncRef>();
ObjectTypeDB::register_virtual_type<StreamPeer>();
ObjectTypeDB::register_type<StreamPeerBuffer>();
ObjectTypeDB::register_create_type<StreamPeerTCP>();
ObjectTypeDB::register_create_type<TCP_Server>();
ObjectTypeDB::register_create_type<PacketPeerUDP>();

View file

@ -183,7 +183,8 @@ StringName::StringName(const char *p_name) {
ERR_FAIL_COND(!configured);
ERR_FAIL_COND( !p_name || !p_name[0]);
if (!p_name || p_name[0]==0)
return; //empty, ignore
_global_lock();
@ -288,6 +289,9 @@ StringName::StringName(const String& p_name) {
ERR_FAIL_COND(!configured);
if (p_name==String())
return;
_global_lock();
uint32_t hash = p_name.hash();

View file

@ -38,6 +38,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
_setup_compressor();
active=true;
server=true;
refuse_connections=false;
@ -58,6 +59,8 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port
ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
_setup_compressor();
ENetAddress address;
address.host=p_ip.host;
address.port=p_port;
@ -494,13 +497,124 @@ bool NetworkedMultiplayerENet::is_refusing_new_connections() const {
return refuse_connections;
}
void NetworkedMultiplayerENet::set_compression_mode(CompressionMode p_mode) {
compression_mode=p_mode;
}
NetworkedMultiplayerENet::CompressionMode NetworkedMultiplayerENet::get_compression_mode() const{
return compression_mode;
}
size_t NetworkedMultiplayerENet::enet_compress(void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) {
NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet*)(context);
if (size_t(enet->src_compressor_mem.size())<inLimit) {
enet->src_compressor_mem.resize( inLimit );
}
int total = inLimit;
int ofs=0;
while(total) {
for(size_t i=0;i<inBufferCount;i++) {
int to_copy = MIN(total,int(inBuffers[i].dataLength));
copymem(&enet->src_compressor_mem[ofs],inBuffers[i].data,to_copy);
ofs+=to_copy;
total-=to_copy;
}
}
Compression::Mode mode;
switch(enet->compression_mode) {
case COMPRESS_FASTLZ: {
mode=Compression::MODE_FASTLZ;
} break;
case COMPRESS_ZLIB: {
mode=Compression::MODE_DEFLATE;
} break;
default: { ERR_FAIL_V(0); }
}
int req_size = Compression::get_max_compressed_buffer_size(ofs,mode);
if (enet->dst_compressor_mem.size()<req_size) {
enet->dst_compressor_mem.resize(req_size);
}
int ret=Compression::compress(enet->dst_compressor_mem.ptr(),enet->src_compressor_mem.ptr(),ofs,mode);
if (ret<0)
return 0;
if (ret>int(outLimit))
return 0; //do not bother
copymem(outData,enet->dst_compressor_mem.ptr(),ret);
return ret;
}
size_t NetworkedMultiplayerENet::enet_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit){
NetworkedMultiplayerENet *enet = (NetworkedMultiplayerENet*)(context);
int ret = -1;
switch(enet->compression_mode) {
case COMPRESS_FASTLZ: {
ret=Compression::decompress(outData,outLimit,inData,inLimit,Compression::MODE_FASTLZ);
} break;
case COMPRESS_ZLIB: {
ret=Compression::decompress(outData,outLimit,inData,inLimit,Compression::MODE_DEFLATE);
} break;
default: {}
}
if (ret<0) {
return 0;
} else {
return ret;
}
}
void NetworkedMultiplayerENet::_setup_compressor() {
switch(compression_mode) {
case COMPRESS_NONE: {
enet_host_compress(host,NULL);
} break;
case COMPRESS_RANGE_CODER: {
enet_host_compress_with_range_coder(host);
} break;
case COMPRESS_FASTLZ:
case COMPRESS_ZLIB: {
enet_host_compress(host,&enet_compressor);
} break;
}
}
void NetworkedMultiplayerENet::enet_compressor_destroy(void * context){
//do none
}
void NetworkedMultiplayerENet::_bind_methods() {
ObjectTypeDB::bind_method(_MD("create_server","port","max_clients","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_server,DEFVAL(32),DEFVAL(0),DEFVAL(0));
ObjectTypeDB::bind_method(_MD("create_client","ip","port","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_client,DEFVAL(0),DEFVAL(0));
ObjectTypeDB::bind_method(_MD("close_connection"),&NetworkedMultiplayerENet::close_connection);
ObjectTypeDB::bind_method(_MD("set_compression_mode","mode"),&NetworkedMultiplayerENet::set_compression_mode);
ObjectTypeDB::bind_method(_MD("get_compression_mode"),&NetworkedMultiplayerENet::get_compression_mode);
BIND_CONSTANT( COMPRESS_NONE );
BIND_CONSTANT( COMPRESS_RANGE_CODER );
BIND_CONSTANT( COMPRESS_FASTLZ );
BIND_CONSTANT( COMPRESS_ZLIB );
}
@ -515,6 +629,12 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet(){
current_packet.packet=NULL;
transfer_mode=TRANSFER_MODE_RELIABLE;
connection_status=CONNECTION_DISCONNECTED;
compression_mode=COMPRESS_NONE;
enet_compressor.context=this;
enet_compressor.compress=enet_compress;
enet_compressor.decompress=enet_decompress;
enet_compressor.destroy=enet_compressor_destroy;
}
NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){

View file

@ -3,10 +3,20 @@
#include "io/networked_multiplayer_peer.h"
#include "enet/enet.h"
#include "io/compression.h"
class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer)
public:
enum CompressionMode {
COMPRESS_NONE,
COMPRESS_RANGE_CODER,
COMPRESS_FASTLZ,
COMPRESS_ZLIB
};
private:
enum {
SYSMSG_ADD_PEER,
@ -37,6 +47,8 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
int from;
};
CompressionMode compression_mode;
mutable List<Packet> incoming_packets;
mutable Packet current_packet;
@ -44,6 +56,15 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
uint32_t _gen_unique_id() const;
void _pop_current_packet() const;
Vector<uint8_t> src_compressor_mem;
Vector<uint8_t> dst_compressor_mem;
ENetCompressor enet_compressor;
static size_t enet_compress(void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit);
static size_t enet_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit);
static void enet_compressor_destroy(void * context);
void _setup_compressor();
protected:
static void _bind_methods();
public:
@ -77,9 +98,14 @@ public:
virtual int get_unique_id() const;
void set_compression_mode(CompressionMode p_mode);
CompressionMode get_compression_mode() const;
NetworkedMultiplayerENet();
~NetworkedMultiplayerENet();
};
VARIANT_ENUM_CAST(NetworkedMultiplayerENet::CompressionMode);
#endif // NETWORKED_MULTIPLAYER_ENET_H

View file

@ -1692,7 +1692,7 @@ enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int ch
& host -> buffers [1], host -> bufferCount - 1,
originalSize,
host -> packetData [1],
originalSize);
originalSize);
if (compressedSize > 0 && compressedSize < originalSize)
{
host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED;

View file

@ -44,7 +44,7 @@
#include "scene/resources/packed_scene.h"
#include "scene/resources/material.h"
#include "scene/resources/mesh.h"
#include "io/marshalls.h"
void SceneTreeTimer::_bind_methods() {
@ -1760,6 +1760,11 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
ERR_FAIL();
}
if (p_argcount>255) {
ERR_EXPLAIN("Too many arguments >255.");
ERR_FAIL();
}
if (p_to!=0 && !connected_peers.has(ABS(p_to))) {
if (p_to==get_network_unique_id()) {
ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: "+itos(get_network_unique_id()));
@ -1774,25 +1779,7 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
NodePath from_path = p_from->get_path();
ERR_FAIL_COND(from_path.is_empty());
//create base packet
Array message;
message.resize(3+p_argcount); //alloc size for args
//set message type
if (p_set) {
message[0]=NETWORK_COMMAND_REMOTE_SET;
} else {
message[0]=NETWORK_COMMAND_REMOTE_CALL;
}
//set message name
message[2]=p_name;
//set message args
for(int i=0;i<p_argcount;i++) {
message[3+i]=*p_arg[i];
}
//see if the path is cached
PathSentCache *psc = path_send_cache.getptr(from_path);
@ -1804,6 +1791,53 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
}
//create base packet, lots of harcode because it must be tight
int ofs=0;
#define MAKE_ROOM(m_amount) if (packet_cache.size() < m_amount) packet_cache.resize(m_amount);
//encode type
MAKE_ROOM(1);
packet_cache[0]=p_set ? NETWORK_COMMAND_REMOTE_SET : NETWORK_COMMAND_REMOTE_CALL;
ofs+=1;
//encode ID
MAKE_ROOM(ofs+4);
encode_uint32(psc->id,&packet_cache[ofs]);
ofs+=4;
//encode function name
CharString name = String(p_name).utf8();
int len = encode_cstring(name.get_data(),NULL);
MAKE_ROOM(ofs+len);
encode_cstring(name.get_data(),&packet_cache[ofs]);
ofs+=len;
if (p_set) {
//set argument
Error err = encode_variant(*p_arg[0],NULL,len);
ERR_FAIL_COND(err!=OK);
MAKE_ROOM(ofs+len);
encode_variant(*p_arg[0],&packet_cache[ofs],len);
ofs+=len;
} else {
//call arguments
MAKE_ROOM(ofs+1);
packet_cache[ofs]=p_argcount;
ofs+=1;
for(int i=0;i<p_argcount;i++) {
Error err = encode_variant(*p_arg[i],NULL,len);
ERR_FAIL_COND(err!=OK);
MAKE_ROOM(ofs+len);
encode_variant(*p_arg[i],&packet_cache[ofs],len);
ofs+=len;
}
}
//see if all peers have cached path (is so, call can be fast)
bool has_all_peers=true;
@ -1834,15 +1868,20 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
for (List<int>::Element *E=peers_to_add.front();E;E=E->next()) {
Array add_path_message;
add_path_message.resize(3);
add_path_message[0]=NETWORK_COMMAND_SIMPLIFY_PATH;
add_path_message[1]=from_path;
add_path_message[2]=psc->id;
//encode function name
CharString pname = String(from_path).utf8();
int len = encode_cstring(pname.get_data(),NULL);
Vector<uint8_t> packet;
packet.resize(1+4+len);
packet[0]=NETWORK_COMMAND_SIMPLIFY_PATH;
encode_uint32(psc->id,&packet[1]);
encode_cstring(pname.get_data(),&packet[5]);
network_peer->set_target_peer(E->get()); //to all of you
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->put_var(add_path_message); //a message with love
network_peer->put_packet(packet.ptr(),packet.size());
psc->confirmed_peers.insert(E->get(),false); //insert into confirmed, but as false since it was not confirmed
}
@ -1853,12 +1892,18 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
if (has_all_peers) {
//they all have verified paths, so send fast
message[1]=psc->id;
network_peer->set_target_peer(p_to); //to all of you
network_peer->put_var(message); //a message with love
network_peer->put_packet(packet_cache.ptr(),ofs); //a message with love
} else {
//not all verified path, so send one by one
//apend path at the end, since we will need it for some packets
CharString pname = String(from_path).utf8();
int path_len = encode_cstring(pname.get_data(),NULL);
MAKE_ROOM(ofs+path_len);
encode_cstring(pname.get_data(),&packet_cache[ofs]);
for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) {
if (p_to<0 && E->get()==-p_to)
@ -1874,41 +1919,52 @@ void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const St
if (F->get()==true) {
//this one confirmed path, so use id
message[1]=psc->id;
encode_uint32(psc->id,&packet_cache[1]);
network_peer->put_packet(packet_cache.ptr(),ofs);
} else {
//this one did not confirm path yet, so use entire path (sorry!)
message[1]=from_path;
encode_uint32(0x80000000|ofs,&packet_cache[1]); //offset to path and flag
network_peer->put_packet(packet_cache.ptr(),ofs+path_len);
}
network_peer->put_var(message);
}
}
}
void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
void SceneTree::_network_process_packet(int p_from, const uint8_t* p_packet, int p_packet_len) {
ERR_FAIL_COND(p_packet.empty());
ERR_FAIL_COND(p_packet_len<5);
int packet_type = p_packet[0];
uint8_t packet_type = p_packet[0];
switch(packet_type) {
case NETWORK_COMMAND_REMOTE_CALL:
case NETWORK_COMMAND_REMOTE_SET: {
ERR_FAIL_COND(p_packet.size()<3);
Variant target = p_packet[1];
Node* node=NULL;
ERR_FAIL_COND(p_packet_len<5);
uint32_t target = decode_uint32(&p_packet[1]);
Node *node=NULL;
if (target&0x80000000) {
int ofs = target&0x7FFFFFFF;
ERR_FAIL_COND(ofs>=p_packet_len);
String paths;
paths.parse_utf8((const char*)&p_packet[ofs],p_packet_len-ofs);
NodePath np = paths;
if (target.get_type()==Variant::NODE_PATH) {
NodePath np = target;
node = get_root()->get_node(np);
if (node==NULL) {
ERR_EXPLAIN("Failed to get path from RPC: "+String(np));
ERR_FAIL_COND(node==NULL);
}
} else if (target.get_type()==Variant::INT) {
} else {
int id = target;
@ -1928,26 +1984,51 @@ void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
}
} else {
ERR_FAIL();
}
StringName name = p_packet[2];
ERR_FAIL_COND(p_packet_len<6);
//detect cstring end
int len_end=5;
for(;len_end<p_packet_len;len_end++) {
if (p_packet[len_end]==0) {
break;
}
}
ERR_FAIL_COND(len_end>=p_packet_len);
StringName name = String::utf8((const char*)&p_packet[5]);
if (packet_type==NETWORK_COMMAND_REMOTE_CALL) {
if (!node->can_call_rpc(name))
return;
int argc = p_packet.size()-3;
int ofs = len_end+1;
ERR_FAIL_COND(ofs>=p_packet_len);
int argc = p_packet[ofs];
Vector<Variant> args;
Vector<const Variant*> argp;
args.resize(argc);
argp.resize(argc);
ofs++;
for(int i=0;i<argc;i++) {
args[i]=p_packet[3+i];
argp[i]=&args[i];
ERR_FAIL_COND(ofs>=p_packet_len);
int vlen;
Error err = decode_variant(args[i],&p_packet[ofs],p_packet_len-ofs,&vlen);
ERR_FAIL_COND(err!=OK);
//args[i]=p_packet[3+i];
argp[i]=&args[i];
ofs+=vlen;
}
Variant::CallError ce;
@ -1964,9 +2045,13 @@ void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
if (!node->can_call_rset(name))
return;
int ofs = len_end+1;
ERR_FAIL_COND(ofs>=p_packet_len);
Variant value;
decode_variant(value,&p_packet[ofs],p_packet_len-ofs);
ERR_FAIL_COND(p_packet.size()!=4);
Variant value = p_packet[3];
bool valid;
node->set(name,value,&valid);
@ -1979,9 +2064,13 @@ void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
} break;
case NETWORK_COMMAND_SIMPLIFY_PATH: {
ERR_FAIL_COND(p_packet.size()!=3);
NodePath path = p_packet[1];
int id = p_packet[2];
ERR_FAIL_COND(p_packet_len<5);
int id = decode_uint32(&p_packet[1]);
String paths;
paths.parse_utf8((const char*)&p_packet[5],p_packet_len-5);
NodePath path = paths;
if (!path_get_cache.has(p_from)) {
path_get_cache[p_from]=PathGetCache();
@ -1993,19 +2082,31 @@ void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
path_get_cache[p_from].nodes[id]=ni;
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from);
Array message;
message.resize(2);
message[0]=NETWORK_COMMAND_CONFIRM_PATH;
message[1]=path;
network_peer->put_var(message);
{
//send ack
//encode path
CharString pname = String(path).utf8();
int len = encode_cstring(pname.get_data(),NULL);
Vector<uint8_t> packet;
packet.resize(1+len);
packet[0]=NETWORK_COMMAND_CONFIRM_PATH;
encode_cstring(pname.get_data(),&packet[1]);
network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
network_peer->set_target_peer(p_from);
network_peer->put_packet(packet.ptr(),packet.size());
}
} break;
case NETWORK_COMMAND_CONFIRM_PATH: {
ERR_FAIL_COND(p_packet.size()!=2);
NodePath path = p_packet[1];
String paths;
paths.parse_utf8((const char*)&p_packet[1],p_packet_len-1);
NodePath path = paths;
PathSentCache *psc = path_send_cache.getptr(path);
ERR_FAIL_COND(!psc);
@ -2031,17 +2132,15 @@ void SceneTree::_network_poll() {
while(network_peer->get_available_packet_count()) {
int sender = network_peer->get_packet_peer();
Variant packet;
Error err = network_peer->get_var(packet);
const uint8_t *packet;
int len;
Error err = network_peer->get_packet(&packet,len);
if (err!=OK) {
ERR_PRINT("Error getting packet!");
}
if (packet.get_type()!=Variant::ARRAY) {
ERR_PRINT("Error getting packet! (not an array)");
}
_network_process_packet(sender,packet);
_network_process_packet(sender,packet,len);
if (!network_peer.is_valid()) {
break; //it's also possible that a packet or RPC caused a disconnection, so also check here

View file

@ -217,7 +217,9 @@ private:
Map<int,PathGetCache> path_get_cache;
void _network_process_packet(int p_from, const Array& p_packet);
Vector<uint8_t> packet_cache;
void _network_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len);
void _network_poll();
static SceneTree *singleton;