Jelajahi Sumber

-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

Juan Linietsky 9 tahun lalu
induk
melakukan
cbbcf72703

+ 15 - 8
core/io/compression.cpp

@@ -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;
 			strm.avail_in=p_src_size;
 			int aout = deflateBound(&strm,p_src_size);;
 			int aout = deflateBound(&strm,p_src_size);;
+			/*if (aout>p_src_size) {
+				deflateEnd(&strm);
+				return -1;
+			}*/
 			strm.avail_out=aout;
 			strm.avail_out=aout;
 			strm.next_in=(Bytef*)p_src;
 			strm.next_in=(Bytef*)p_src;
 			strm.next_out=p_dst;
 			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) {
 	switch(p_mode) {
 		case MODE_FASTLZ: {
 		case MODE_FASTLZ: {
 
 
+			int ret_size=0;
+
 			if (p_dst_max_size<16) {
 			if (p_dst_max_size<16) {
 				uint8_t dst[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);
 				copymem(p_dst,dst,p_dst_max_size);
 			} else {
 			} 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;
 		} break;
 		case MODE_DEFLATE: {
 		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.avail_in= 0;
 			strm.next_in=Z_NULL;
 			strm.next_in=Z_NULL;
 			int err = inflateInit(&strm);
 			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_in=p_src_size;
 			strm.avail_out=p_dst_max_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;
 			strm.next_out=p_dst;
 
 
 			err = inflate(&strm,Z_FINISH);
 			err = inflate(&strm,Z_FINISH);
+			int total = strm.total_out;
 			inflateEnd(&strm);
 			inflateEnd(&strm);
-			ERR_FAIL_COND(err!=Z_STREAM_END);
-			return;
+			ERR_FAIL_COND_V(err!=Z_STREAM_END,-1);
+			return total;
 		} break;
 		} break;
 	}
 	}
 
 
-	ERR_FAIL();
+	ERR_FAIL_V(-1);
 }
 }

+ 1 - 1
core/io/compression.h

@@ -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 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 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();
 	Compression();
 };
 };

+ 137 - 6
core/io/stream_peer.cpp

@@ -222,6 +222,7 @@ void StreamPeer::put_double(double p_val){
 void StreamPeer::put_utf8_string(const String& p_string) {
 void StreamPeer::put_utf8_string(const String& p_string) {
 
 
 	CharString cs=p_string.utf8();
 	CharString cs=p_string.utf8();
+	put_u32(p_string.length());
 	put_data((const uint8_t*)cs.get_data(),cs.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());
 	ERR_FAIL_COND_V(p_bytes<0,String());
 
 
 	Vector<char> buf;
 	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;
 	buf[p_bytes]=0;
 	return buf.ptr();
 	return buf.ptr();
 
 
@@ -359,8 +362,10 @@ String StreamPeer::get_utf8_string(int p_bytes){
 	ERR_FAIL_COND_V(p_bytes<0,String());
 	ERR_FAIL_COND_V(p_bytes<0,String());
 
 
 	Vector<uint8_t> buf;
 	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;
 	String ret;
 	ret.parse_utf8((const char*)buf.ptr(),buf.size());
 	ret.parse_utf8((const char*)buf.ptr(),buf.size());
@@ -371,8 +376,10 @@ Variant StreamPeer::get_var(){
 
 
 	int len = get_32();
 	int len = get_32();
 	Vector<uint8_t> var;
 	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;
 	Variant ret;
 	decode_variant(ret,var.ptr(),len);
 	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_utf8_string","bytes"),&StreamPeer::get_utf8_string);
 	ObjectTypeDB::bind_method(_MD("get_var:Variant"),&StreamPeer::get_var);
 	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;
+}

+ 36 - 0
core/io/stream_peer.h

@@ -91,4 +91,40 @@ public:
 	StreamPeer() { big_endian=false; }
 	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
 #endif // STREAM_PEER_H

+ 4 - 0
core/path_db.cpp

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

+ 1 - 0
core/register_core_types.cpp

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

+ 5 - 1
core/string_db.cpp

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

+ 120 - 0
modules/enet/networked_multiplayer_enet.cpp

@@ -38,6 +38,7 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int
 
 
 	ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
 	ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
 
 
+	_setup_compressor();
 	active=true;
 	active=true;
 	server=true;
 	server=true;
 	refuse_connections=false;
 	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);
 	ERR_FAIL_COND_V(!host,ERR_CANT_CREATE);
 
 
 
 
+	_setup_compressor();
+
 	ENetAddress address;
 	ENetAddress address;
 	address.host=p_ip.host;
 	address.host=p_ip.host;
 	address.port=p_port;
 	address.port=p_port;
@@ -494,13 +497,124 @@ bool NetworkedMultiplayerENet::is_refusing_new_connections() const {
 	return refuse_connections;
 	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() {
 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_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("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("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;
 	current_packet.packet=NULL;
 	transfer_mode=TRANSFER_MODE_RELIABLE;
 	transfer_mode=TRANSFER_MODE_RELIABLE;
 	connection_status=CONNECTION_DISCONNECTED;
 	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(){
 NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){

+ 26 - 0
modules/enet/networked_multiplayer_enet.h

@@ -3,10 +3,20 @@
 
 
 #include "io/networked_multiplayer_peer.h"
 #include "io/networked_multiplayer_peer.h"
 #include "enet/enet.h"
 #include "enet/enet.h"
+#include "io/compression.h"
 
 
 class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
 class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
 
 
 	OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer)
 	OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer)
+public:
+	enum CompressionMode {
+		COMPRESS_NONE,
+		COMPRESS_RANGE_CODER,
+		COMPRESS_FASTLZ,
+		COMPRESS_ZLIB
+	};
+private:
+
 
 
 	enum {
 	enum {
 		SYSMSG_ADD_PEER,
 		SYSMSG_ADD_PEER,
@@ -37,6 +47,8 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
 		int from;
 		int from;
 	};
 	};
 
 
+	CompressionMode compression_mode;
+
 	mutable List<Packet> incoming_packets;
 	mutable List<Packet> incoming_packets;
 
 
 	mutable Packet current_packet;
 	mutable Packet current_packet;
@@ -44,6 +56,15 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer {
 	uint32_t _gen_unique_id() const;
 	uint32_t _gen_unique_id() const;
 	void _pop_current_packet() 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:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();
 public:
 public:
@@ -77,9 +98,14 @@ public:
 
 
 	virtual int get_unique_id() const;
 	virtual int get_unique_id() const;
 
 
+	void set_compression_mode(CompressionMode p_mode);
+	CompressionMode get_compression_mode() const;
+
 	NetworkedMultiplayerENet();
 	NetworkedMultiplayerENet();
 	~NetworkedMultiplayerENet();
 	~NetworkedMultiplayerENet();
 };
 };
 
 
+VARIANT_ENUM_CAST(NetworkedMultiplayerENet::CompressionMode);
+
 
 
 #endif // NETWORKED_MULTIPLAYER_ENET_H
 #endif // NETWORKED_MULTIPLAYER_ENET_H

+ 1 - 1
modules/enet/protocol.c

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

+ 166 - 67
scene/main/scene_main_loop.cpp

@@ -44,7 +44,7 @@
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/packed_scene.h"
 #include "scene/resources/material.h"
 #include "scene/resources/material.h"
 #include "scene/resources/mesh.h"
 #include "scene/resources/mesh.h"
-
+#include "io/marshalls.h"
 
 
 void SceneTreeTimer::_bind_methods() {
 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();
 		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!=0 && !connected_peers.has(ABS(p_to))) {
 		if (p_to==get_network_unique_id()) {
 		if (p_to==get_network_unique_id()) {
 			ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: "+itos(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();
 	NodePath from_path = p_from->get_path();
 	ERR_FAIL_COND(from_path.is_empty());
 	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
 	//see if the path is cached
 	PathSentCache *psc = path_send_cache.getptr(from_path);
 	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)
 	//see if all peers have cached path (is so, call can be fast)
 	bool has_all_peers=true;
 	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()) {
 	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_target_peer(E->get()); //to all of you
 		network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE);
 		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
 		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) {
 	if (has_all_peers) {
 
 
 		//they all have verified paths, so send fast
 		//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->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 {
 	} else {
 		//not all verified path, so send one by one
 		//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()) {
 		for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) {
 
 
 			if (p_to<0 && E->get()==-p_to)
 			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) {
 			if (F->get()==true) {
 				//this one confirmed path, so use id
 				//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 {
 			} else {
 				//this one did not confirm path yet, so use entire path (sorry!)
 				//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) {
 	switch(packet_type) {
 
 
 		case NETWORK_COMMAND_REMOTE_CALL:
 		case NETWORK_COMMAND_REMOTE_CALL:
 		case NETWORK_COMMAND_REMOTE_SET: {
 		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);
 				node = get_root()->get_node(np);
 				if (node==NULL) {
 				if (node==NULL) {
 					ERR_EXPLAIN("Failed to get path from RPC: "+String(np));
 					ERR_EXPLAIN("Failed to get path from RPC: "+String(np));
 					ERR_FAIL_COND(node==NULL);
 					ERR_FAIL_COND(node==NULL);
 				}
 				}
-			} else if (target.get_type()==Variant::INT) {
+			} else {
 
 
 				int id = target;
 				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 (packet_type==NETWORK_COMMAND_REMOTE_CALL) {
 
 
 				if (!node->can_call_rpc(name))
 				if (!node->can_call_rpc(name))
 					return;
 					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<Variant> args;
 				Vector<const Variant*> argp;
 				Vector<const Variant*> argp;
 				args.resize(argc);
 				args.resize(argc);
 				argp.resize(argc);
 				argp.resize(argc);
 
 
+				ofs++;
+
 				for(int i=0;i<argc;i++) {
 				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;
 				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))
 				if (!node->can_call_rset(name))
 					return;
 					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;
 				bool valid;
 
 
 				node->set(name,value,&valid);
 				node->set(name,value,&valid);
@@ -1979,9 +2064,13 @@ void SceneTree::_network_process_packet(int p_from, const Array& p_packet) {
 		} break;
 		} break;
 		case NETWORK_COMMAND_SIMPLIFY_PATH: {
 		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)) {
 			if (!path_get_cache.has(p_from)) {
 				path_get_cache[p_from]=PathGetCache();
 				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;
 			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;
 		} break;
 		case NETWORK_COMMAND_CONFIRM_PATH: {
 		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);
 			PathSentCache *psc = path_send_cache.getptr(path);
 			ERR_FAIL_COND(!psc);
 			ERR_FAIL_COND(!psc);
@@ -2031,17 +2132,15 @@ void SceneTree::_network_poll() {
 	while(network_peer->get_available_packet_count()) {
 	while(network_peer->get_available_packet_count()) {
 
 
 		int sender = network_peer->get_packet_peer();
 		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) {
 		if (err!=OK) {
 			ERR_PRINT("Error getting packet!");
 			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()) {
 		if (!network_peer.is_valid()) {
 			break; //it's also possible that a packet or RPC caused a disconnection, so also check here
 			break; //it's also possible that a packet or RPC caused a disconnection, so also check here

+ 3 - 1
scene/main/scene_main_loop.h

@@ -217,7 +217,9 @@ private:
 
 
 	Map<int,PathGetCache> path_get_cache;
 	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();
 	void _network_poll();
 
 
 	static SceneTree *singleton;
 	static SceneTree *singleton;