diff options
author | Juan Linietsky <reduzio@gmail.com> | 2016-08-19 16:48:08 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2016-08-19 16:48:41 -0300 |
commit | 1add52b55e038a4fa789c64f3919f7fcab808e91 (patch) | |
tree | dd7878449dd1d2301684a5e5aa34b8466744b168 /modules/enet | |
parent | 56fa741b7a729bd5a00cff17e4382af547de92ad (diff) |
Brand new networked multiplayer
Diffstat (limited to 'modules/enet')
-rw-r--r-- | modules/enet/networked_multiplayer_enet.cpp | 382 | ||||
-rw-r--r-- | modules/enet/networked_multiplayer_enet.h | 33 | ||||
-rw-r--r-- | modules/enet/protocol.c | 1 |
3 files changed, 340 insertions, 76 deletions
diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp index aebdfe03a5..e9a3a46e75 100644 --- a/modules/enet/networked_multiplayer_enet.cpp +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -1,48 +1,38 @@ #include "networked_multiplayer_enet.h" +#include "os/os.h" +#include "io/marshalls.h" void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) { transfer_mode=p_mode; } -void NetworkedMultiplayerENet::set_target_peer(const StringName &p_peer){ +void NetworkedMultiplayerENet::set_target_peer(int p_peer){ target_peer=p_peer; } -void NetworkedMultiplayerENet::set_channel(int p_channel){ - - send_channel=p_channel; -} +int NetworkedMultiplayerENet::get_packet_peer() const{ -StringName NetworkedMultiplayerENet::get_packet_peer() const{ - - ERR_FAIL_COND_V(!active,StringName()); - ERR_FAIL_COND_V(incoming_packets.size()==0,StringName()); + ERR_FAIL_COND_V(!active,1); + ERR_FAIL_COND_V(incoming_packets.size()==0,1); return incoming_packets.front()->get().from; } -int NetworkedMultiplayerENet::get_packet_channel() const{ - - ERR_FAIL_COND_V(!active,0); - ERR_FAIL_COND_V(incoming_packets.size()==0,0); - return incoming_packets.front()->get().from_channel; -} - -Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth){ +Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int p_in_bandwidth, int p_out_bandwidth){ ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE); ENetAddress address; address.host = ENET_HOST_ANY; - /* Bind the server to port 1234. */ - address.port = 1234; + + address.port = p_port; host = enet_host_create (& address /* the address to bind the server host to */, p_max_clients /* allow up to 32 clients and/or outgoing connections */, - p_max_channels /* allow up to 2 channels to be used, 0 and 1 */, + 2 /* allow up to 2 channels to be used, 0 and 1 */, p_in_bandwidth /* assume any amount of incoming bandwidth */, p_out_bandwidth /* assume any amount of outgoing bandwidth */); @@ -50,16 +40,18 @@ Error NetworkedMultiplayerENet::create_server(int p_port, int p_max_clients, int active=true; server=true; + refuse_connections=false; + unique_id=1; connection_status=CONNECTION_CONNECTED; return OK; } -Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip,int p_port, int p_max_channels, int p_in_bandwidth, int p_out_bandwidth){ +Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth, int p_out_bandwidth){ ERR_FAIL_COND_V(active,ERR_ALREADY_IN_USE); host = enet_host_create (NULL /* create a client host */, 1 /* only allow 1 outgoing connection */, - p_max_channels /* allow up 2 channels to be used, 0 and 1 */, + 2 /* allow up 2 channels to be used, 0 and 1 */, p_in_bandwidth /* 56K modem with 56 Kbps downstream bandwidth */, p_out_bandwidth /* 56K modem with 14 Kbps upstream bandwidth */); @@ -70,8 +62,13 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip,int p_port, address.host=p_ip.host; address.port=p_port; + //enet_address_set_host (& address, "localhost"); + //address.port = p_port; + + unique_id=_gen_unique_id(); + /* Initiate the connection, allocating the two channels 0 and 1. */ - ENetPeer *peer = enet_host_connect (host, & address, p_max_channels, 0); + ENetPeer *peer = enet_host_connect (host, & address, 2, unique_id); if (peer == NULL) { enet_host_destroy(host); @@ -83,6 +80,7 @@ Error NetworkedMultiplayerENet::create_client(const IP_Address& p_ip,int p_port, connection_status=CONNECTION_CONNECTING; active=true; server=false; + refuse_connections=false; return OK; } @@ -95,18 +93,41 @@ void NetworkedMultiplayerENet::poll(){ ENetEvent event; /* Wait up to 1000 milliseconds for an event. */ - while (enet_host_service (host, & event, 1000) > 0) - { + while (true) { + + if (!host || !active) //might have been disconnected while emitting a notification + return; + + int ret = enet_host_service (host, & event, 1); + + if (ret<0) { + //error, do something? + break; + } else if (ret==0) { + break; + } + switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { /* Store any relevant client information here. */ + if (server && refuse_connections) { + enet_peer_reset(event.peer); + break; + } + IP_Address ip; ip.host=event.peer -> address.host; - StringName *new_id = memnew( StringName ); - *new_id = String(ip) +":"+ itos(event.peer -> address.port); + int *new_id = memnew( int ); + *new_id = event.data; + + if (*new_id==0) { //data zero is sent by server (enet won't let you configure this). Server is always 1 + *new_id=1; + } + + event.peer->data=new_id; peer_map[*new_id]=event.peer; @@ -114,29 +135,170 @@ void NetworkedMultiplayerENet::poll(){ emit_signal("peer_connected",*new_id); + if (server) { + //someone connected, let it know of all the peers available + for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) { + + if (E->key()==*new_id) + continue; + //send existing peers to new peer + ENetPacket * packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]); + encode_uint32(E->key(),&packet->data[4]); + enet_peer_send(event.peer,1,packet); + //send the new peer to existing peers + packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_ADD_PEER,&packet->data[0]); + encode_uint32(*new_id,&packet->data[4]); + enet_peer_send(E->get(),1,packet); + } + } else { + + emit_signal("connection_succeeded"); + } + } break; case ENET_EVENT_TYPE_DISCONNECT: { /* Reset the peer's client information. */ - StringName *id = (StringName*)event.peer -> data; + int *id = (int*)event.peer -> data; + + + + if (!id) { + if (!server) { + emit_signal("connection_failed"); + } + } else { + + if (server) { + //someone disconnected, let it know to everyone else + for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) { + + if (E->key()==*id) + continue; + //send the new peer to existing peers + ENetPacket* packet = enet_packet_create (NULL,8,ENET_PACKET_FLAG_RELIABLE); + encode_uint32(SYSMSG_REMOVE_PEER,&packet->data[0]); + encode_uint32(*id,&packet->data[4]); + enet_peer_send(E->get(),1,packet); + } + } else if (!server) { + emit_signal("server_disconnected"); + close_connection(); + return; + } + + emit_signal("peer_disconnected",*id); + peer_map.erase(*id); + memdelete( id ); + + } - emit_signal("peer_disconnected",*id); - peer_map.erase(*id); - memdelete( id ); } break; case ENET_EVENT_TYPE_RECEIVE: { - Packet packet; - packet.packet = event.packet; - StringName *id = (StringName*)event.peer -> data; - packet.from_channel=event.channelID; - packet.from=*id; + if (event.channelID==1) { + //some config message + ERR_CONTINUE( event.packet->dataLength < 8); + + int msg = decode_uint32(&event.packet->data[0]); + int id = decode_uint32(&event.packet->data[4]); + + switch(msg) { + case SYSMSG_ADD_PEER: { + + peer_map[id]=NULL; + emit_signal("peer_connected",id); + + } break; + case SYSMSG_REMOVE_PEER: { + + peer_map.erase(id); + emit_signal("peer_disconnected",id); + } break; + } + + enet_packet_destroy(event.packet); + } else if (event.channelID==0){ + + Packet packet; + packet.packet = event.packet; + + int *id = (int*)event.peer -> data; + + ERR_CONTINUE(event.packet->dataLength<12) + + + uint32_t source = decode_uint32(&event.packet->data[0]); + int target = decode_uint32(&event.packet->data[4]); + uint32_t flags = decode_uint32(&event.packet->data[8]); + + packet.from=source; + + if (server) { + + packet.from=*id; + + if (target==0) { + //re-send the everyone but sender :| + + incoming_packets.push_back(packet); + //and make copies for sending + for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) { + + if (uint32_t(E->key())==source) //do not resend to self + continue; + + ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags); + + enet_peer_send(E->get(),0,packet2); + } + + } else if (target<0) { + //to all but one + + //and make copies for sending + for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) { + + if (uint32_t(E->key())==source || E->key()==-target) //do not resend to self, also do not send to excluded + continue; + + ENetPacket* packet2 = enet_packet_create (packet.packet->data,packet.packet->dataLength,flags); + + enet_peer_send(E->get(),0,packet2); + } + + if (-target != 1) { + //server is not excluded + incoming_packets.push_back(packet); + } else { + //server is excluded, erase packet + enet_packet_destroy(packet.packet); + } + + } else if (target==1) { + //to myself and only myself + incoming_packets.push_back(packet); + } else { + //to someone else, specifically + ERR_CONTINUE(!peer_map.has(source)); + enet_peer_send(peer_map[source],0,packet.packet); + } + } else { + + incoming_packets.push_back(packet); + } + + + //destroy packet later.. + } else { + ERR_CONTINUE(true); + } - incoming_packets.push_back(packet); - //destroy packet later.. }break; case ENET_EVENT_TYPE_NONE: { @@ -154,14 +316,29 @@ bool NetworkedMultiplayerENet::is_server() const { void NetworkedMultiplayerENet::close_connection() { - ERR_FAIL_COND(!active); + if (!active) + return; _pop_current_packet(); + bool peers_disconnected=false; + for (Map<int,ENetPeer*>::Element *E=peer_map.front();E;E=E->next()) { + if (E->get()) { + enet_peer_disconnect_now(E->get(),unique_id); + peers_disconnected=true; + } + } + + if (peers_disconnected) { + enet_host_flush(host); + OS::get_singleton()->delay_usec(100); //wait 100ms for disconnection packets to send + + } + enet_host_destroy(host); active=false; incoming_packets.clear(); - + unique_id=1; //server is 1 connection_status=CONNECTION_DISCONNECTED; } @@ -178,26 +355,18 @@ Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer,int &r_buffe current_packet = incoming_packets.front()->get(); incoming_packets.pop_front(); - r_buffer=(const uint8_t**)¤t_packet.packet->data; + *r_buffer=(const uint8_t*)(¤t_packet.packet->data[12]); r_buffer_size=current_packet.packet->dataLength; return OK; } Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_size){ - ERR_FAIL_COND_V(incoming_packets.size()==0,ERR_UNAVAILABLE); - - Map<StringName,ENetPeer*>::Element *E=NULL; - - if (target_peer!=StringName()) { - peer_map.find(target_peer); - if (!E) { - ERR_EXPLAIN("Invalid Target Peer: "+String(target_peer)); - ERR_FAIL_V(ERR_INVALID_PARAMETER); - } - } + ERR_FAIL_COND_V(!active,ERR_UNCONFIGURED); + ERR_FAIL_COND_V(connection_status!=CONNECTION_CONNECTED,ERR_UNCONFIGURED); int packet_flags=0; + switch(transfer_mode) { case TRANSFER_MODE_UNRELIABLE: { packet_flags=ENET_PACKET_FLAG_UNSEQUENCED; @@ -210,13 +379,53 @@ Error NetworkedMultiplayerENet::put_packet(const uint8_t *p_buffer,int p_buffer_ } break; } - /* Create a reliable packet of size 7 containing "packet\0" */ - ENetPacket * packet = enet_packet_create (p_buffer,p_buffer_size,packet_flags); + Map<int,ENetPeer*>::Element *E=NULL; + + if (target_peer!=0) { + + E = peer_map.find(ABS(target_peer)); + if (!E) { + ERR_EXPLAIN("Invalid Target Peer: "+itos(target_peer)); + ERR_FAIL_V(ERR_INVALID_PARAMETER); + } + } + + ENetPacket * packet = enet_packet_create (NULL,p_buffer_size+12,packet_flags); + encode_uint32(unique_id,&packet->data[0]); //source ID + encode_uint32(target_peer,&packet->data[4]); //dest ID + encode_uint32(packet_flags,&packet->data[8]); //dest ID + copymem(&packet->data[12],p_buffer,p_buffer_size); + + if (server) { + + if (target_peer==0) { + enet_host_broadcast(host,0,packet); + } else if (target_peer<0) { + //send to all but one + //and make copies for sending + + int exclude=-target_peer; + + for (Map<int,ENetPeer*>::Element *F=peer_map.front();F;F=F->next()) { + + if (F->key()==exclude) // exclude packet + continue; - if (target_peer==StringName()) { - enet_host_broadcast(host,send_channel,packet); + ENetPacket* packet2 = enet_packet_create (packet->data,packet->dataLength,packet_flags); + + enet_peer_send(F->get(),0,packet2); + } + + enet_packet_destroy(packet); //original packet no longer needed + } else { + enet_peer_send (E->get(), 0, packet); + + } } else { - enet_peer_send (E->get(), send_channel, packet); + + ERR_FAIL_COND_V(!peer_map.has(1),ERR_BUG); + enet_peer_send (peer_map[1], 0, packet); //send to server for broadcast.. + } enet_host_flush(host); @@ -234,7 +443,7 @@ void NetworkedMultiplayerENet::_pop_current_packet() const { if (current_packet.packet) { enet_packet_destroy(current_packet.packet); current_packet.packet=NULL; - current_packet.from=StringName(); + current_packet.from=0; } } @@ -244,11 +453,54 @@ NetworkedMultiplayerPeer::ConnectionStatus NetworkedMultiplayerENet::get_connect return connection_status; } +uint32_t NetworkedMultiplayerENet::_gen_unique_id() const { + + uint32_t hash = 0; + + while (hash==0 || hash==1) { + + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_ticks_usec() ); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_unix_time(), hash ); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_data_dir().hash64(), hash ); + //hash = hash_djb2_one_32( + // (uint32_t)OS::get_singleton()->get_unique_ID().hash64(), hash ); + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)this), hash ); //rely on aslr heap + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)&hash), hash ); //rely on aslr stack + + hash=hash&0x7FFFFFFF; // make it compatible with unsigned, since negatie id is used for exclusion + } + + return hash; +} + +int NetworkedMultiplayerENet::get_unique_id() const { + + ERR_FAIL_COND_V(!active,0); + return unique_id; +} + +void NetworkedMultiplayerENet::set_refuse_new_connections(bool p_enable) { + + refuse_connections=p_enable; +} + +bool NetworkedMultiplayerENet::is_refusing_new_connections() const { + + return refuse_connections; +} + + void NetworkedMultiplayerENet::_bind_methods() { - ObjectTypeDB::bind_method(_MD("create_server","port","max_clients","max_channels","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_server,DEFVAL(32),DEFVAL(1),DEFVAL(0),DEFVAL(0)); - ObjectTypeDB::bind_method(_MD("create_client","ip","port","max_channels","in_bandwidth","out_bandwidth"),&NetworkedMultiplayerENet::create_client,DEFVAL(1),DEFVAL(0),DEFVAL(0)); - ObjectTypeDB::bind_method(_MD("disconnect"),&NetworkedMultiplayerENet::disconnect); + 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); + } @@ -257,7 +509,9 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet(){ active=false; server=false; - send_channel=0; + refuse_connections=false; + unique_id=0; + target_peer=0; current_packet.packet=NULL; transfer_mode=TRANSFER_MODE_ORDERED; connection_status=CONNECTION_DISCONNECTED; @@ -265,7 +519,5 @@ NetworkedMultiplayerENet::NetworkedMultiplayerENet(){ NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){ - if (active) { - close_connection(); - } + close_connection(); } diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h index ec6b084d66..235686d143 100644 --- a/modules/enet/networked_multiplayer_enet.h +++ b/modules/enet/networked_multiplayer_enet.h @@ -8,32 +8,40 @@ class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer { OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer) + enum { + SYSMSG_ADD_PEER, + SYSMSG_REMOVE_PEER + }; + bool active; bool server; - int send_channel; - StringName target_peer; + uint32_t unique_id; + + int target_peer; TransferMode transfer_mode; ENetEvent event; ENetPeer *peer; ENetHost *host; + bool refuse_connections; + ConnectionStatus connection_status; - Map<StringName,ENetPeer*> peer_map; + Map<int,ENetPeer*> peer_map; struct Packet { ENetPacket *packet; - int from_channel; - StringName from; + int from; }; mutable List<Packet> incoming_packets; mutable Packet current_packet; + uint32_t _gen_unique_id() const; void _pop_current_packet() const; protected: @@ -41,16 +49,14 @@ protected: public: virtual void set_transfer_mode(TransferMode p_mode); - virtual void set_target_peer(const StringName& p_peer); - virtual void set_channel(int p_channel); + virtual void set_target_peer(int p_peer); - virtual StringName get_packet_peer() const; - virtual int get_packet_channel() const; + virtual int get_packet_peer() const; - Error create_server(int p_port, int p_max_clients=32, int p_max_channels=1, int p_in_bandwidth=0, int p_out_bandwidth=0); - Error create_client(const IP_Address& p_ip,int p_port, int p_max_channels=1, int p_in_bandwidth=0, int p_out_bandwidth=0); + Error create_server(int p_port, int p_max_peers=32, int p_in_bandwidth=0, int p_out_bandwidth=0); + Error create_client(const IP_Address& p_ip, int p_port, int p_in_bandwidth=0, int p_out_bandwidth=0); void close_connection(); @@ -66,6 +72,11 @@ public: virtual ConnectionStatus get_connection_status() const; + virtual void set_refuse_new_connections(bool p_enable); + virtual bool is_refusing_new_connections() const; + + virtual int get_unique_id() const; + NetworkedMultiplayerENet(); ~NetworkedMultiplayerENet(); }; diff --git a/modules/enet/protocol.c b/modules/enet/protocol.c index 29d648732d..5225147942 100644 --- a/modules/enet/protocol.c +++ b/modules/enet/protocol.c @@ -9,6 +9,7 @@ #include "enet/time.h" #include "enet/enet.h" + static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = { 0, |