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 | |
parent | 56fa741b7a729bd5a00cff17e4382af547de92ad (diff) |
Brand new networked multiplayer
Diffstat (limited to 'modules')
-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 | ||||
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 6 | ||||
-rw-r--r-- | modules/gdscript/gd_editor.cpp | 28 | ||||
-rw-r--r-- | modules/gdscript/gd_function.cpp | 1 | ||||
-rw-r--r-- | modules/gdscript/gd_function.h | 12 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 99 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 8 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 46 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 101 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 10 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 4 | ||||
-rw-r--r-- | modules/visual_script/visual_script.cpp | 13 | ||||
-rw-r--r-- | modules/visual_script/visual_script.h | 5 | ||||
-rw-r--r-- | modules/visual_script/visual_script_func_nodes.cpp | 6 |
16 files changed, 611 insertions, 144 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, diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index 68c3dc98d3..ce8b6a6ea4 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -1297,8 +1297,10 @@ Error GDCompiler::_parse_function(GDScript *p_script,const GDParser::ClassNode * gdfunc = p_script->member_functions[func_name]; //} - if (p_func) + if (p_func) { gdfunc->_static=p_func->_static; + gdfunc->rpc_mode=p_func->rpc_mode; + } #ifdef TOOLS_ENABLED gdfunc->arg_names=argnames; @@ -1625,6 +1627,8 @@ Error GDCompiler::_parse_class(GDScript *p_script, GDScript *p_owner, const GDPa minfo.index = p_script->member_indices.size(); minfo.setter = p_class->variables[i].setter; minfo.getter = p_class->variables[i].getter; + minfo.rpc_mode=p_class->variables[i].rpc_mode; + p_script->member_indices[name]=minfo; p_script->members.insert(name); diff --git a/modules/gdscript/gd_editor.cpp b/modules/gdscript/gd_editor.cpp index 2e5fb82f37..5b74dab889 100644 --- a/modules/gdscript/gd_editor.cpp +++ b/modules/gdscript/gd_editor.cpp @@ -1463,7 +1463,7 @@ static void _make_function_hint(const GDParser::FunctionNode* p_func,int p_argid } -static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) { +static void _find_type_arguments(GDCompletionContext& context,const GDParser::Node*p_node,int p_line,const StringName& p_method,const GDCompletionIdentifier& id, int p_argidx, Set<String>& result, String& arghint) { //print_line("find type arguments?"); @@ -1700,9 +1700,31 @@ static void _find_type_arguments(const GDParser::Node*p_node,int p_line,const St if (p_argidx==0) { List<MethodInfo> sigs; ObjectTypeDB::get_signal_list(id.obj_type,&sigs); + + if (id.script.is_valid()) { + id.script->get_script_signal_list(&sigs); + } else if (id.value.get_type()==Variant::OBJECT) { + Object *obj = id.value; + if (obj && !obj->get_script().is_null()) { + Ref<Script> scr=obj->get_script(); + if (scr.is_valid()) { + scr->get_script_signal_list(&sigs); + } + } + } + for (List<MethodInfo>::Element *E=sigs.front();E;E=E->next()) { result.insert("\""+E->get().name+"\""); } + + } else if (p_argidx==2){ + + + if (context._class) { + for(int i=0;i<context._class->functions.size();i++) { + result.insert("\""+context._class->functions[i]->name+"\""); + } + } } /*if (p_argidx==2) { @@ -1944,7 +1966,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No if (!context._class->owner) ci.value=context.base; - _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint); //guess type.. /* List<MethodInfo> methods; @@ -1967,7 +1989,7 @@ static void _find_call_arguments(GDCompletionContext& context,const GDParser::No GDCompletionIdentifier ci; if (_guess_expression_type(context,op->arguments[0],p_line,ci)) { - _find_type_arguments(p_node,p_line,id->name,ci,p_argidx,result,arghint); + _find_type_arguments(context,p_node,p_line,id->name,ci,p_argidx,result,arghint); return; } diff --git a/modules/gdscript/gd_function.cpp b/modules/gdscript/gd_function.cpp index 47d8f0b40f..b2cc6341c1 100644 --- a/modules/gdscript/gd_function.cpp +++ b/modules/gdscript/gd_function.cpp @@ -1309,6 +1309,7 @@ GDFunction::GDFunction() : function_list(this) { _stack_size=0; _call_size=0; + rpc_mode=ScriptInstance::RPC_MODE_DISABLED; name="<anonymous>"; #ifdef DEBUG_ENABLED _func_cname=NULL; diff --git a/modules/gdscript/gd_function.h b/modules/gdscript/gd_function.h index e09c6509dd..f1c5b13ca1 100644 --- a/modules/gdscript/gd_function.h +++ b/modules/gdscript/gd_function.h @@ -7,6 +7,7 @@ #include "variant.h" #include "string_db.h" #include "reference.h" +#include "script_language.h" class GDInstance; class GDScript; @@ -64,6 +65,14 @@ public: ADDR_TYPE_NIL=8 }; + enum RPCMode { + RPC_DISABLED, + RPC_ENABLED, + RPC_SYNC, + RPC_SYNC_MASTER, + RPC_SYNC_SLAVE + }; + struct StackDebug { int line; @@ -91,6 +100,8 @@ friend class GDCompiler; int _call_size; int _initial_line; bool _static; + ScriptInstance::RPCMode rpc_mode; + GDScript *_script; StringName name; @@ -185,6 +196,7 @@ public: Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); + _FORCE_INLINE_ ScriptInstance::RPCMode get_rpc_mode() const { return rpc_mode; } GDFunction(); ~GDFunction(); }; diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index a6794564db..e5a8dc0152 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -2075,6 +2075,7 @@ void GDParser::_parse_class(ClassNode *p_class) { if (error_set) return; + if (indent_level>tab_level.back()->get()) { p_class->end_line=tokenizer->get_token_line(); return; //go back a level @@ -2371,6 +2372,9 @@ void GDParser::_parse_class(ClassNode *p_class) { function->_static=_static; function->line=fnline; + function->rpc_mode=rpc_mode; + rpc_mode=ScriptInstance::RPC_MODE_DISABLED; + if (_static) p_class->static_functions.push_back(function); @@ -2842,25 +2846,101 @@ void GDParser::_parse_class(ClassNode *p_class) { } - if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_ONREADY && tokenizer->get_token()!=GDTokenizer::TK_PR_REMOTE && tokenizer->get_token()!=GDTokenizer::TK_PR_MASTER && tokenizer->get_token()!=GDTokenizer::TK_PR_SLAVE && tokenizer->get_token()!=GDTokenizer::TK_PR_SYNC) { current_export=PropertyInfo(); - _set_error("Expected 'var'."); + _set_error("Expected 'var', 'onready', 'remote', 'master', 'slave' or 'sync'."); return; } - }; //fallthrough to var + continue; + } break; case GDTokenizer::TK_PR_ONREADY: { - if (token==GDTokenizer::TK_PR_ONREADY) { - //may be fallthrough from export, ignore if so - tokenizer->advance(); + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) { + _set_error("Expected 'var'."); + return; + } + + continue; + } break; + case GDTokenizer::TK_PR_REMOTE: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (current_export.type) { if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) { _set_error("Expected 'var'."); return; } + + } else { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) { + _set_error("Expected 'var' or 'func'."); + return; + } } - }; //fallthrough to var + rpc_mode=ScriptInstance::RPC_MODE_REMOTE; + + continue; + } break; + case GDTokenizer::TK_PR_MASTER: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (current_export.type) { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) { + _set_error("Expected 'var'."); + return; + } + + } else { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) { + _set_error("Expected 'var' or 'func'."); + return; + } + } + + rpc_mode=ScriptInstance::RPC_MODE_MASTER; + continue; + } break; + case GDTokenizer::TK_PR_SLAVE: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (current_export.type) { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR) { + _set_error("Expected 'var'."); + return; + } + + } else { + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) { + _set_error("Expected 'var' or 'func'."); + return; + } + } + + rpc_mode=ScriptInstance::RPC_MODE_SLAVE; + continue; + } break; + case GDTokenizer::TK_PR_SYNC: { + + //may be fallthrough from export, ignore if so + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PR_VAR && tokenizer->get_token()!=GDTokenizer::TK_PR_FUNCTION) { + if (current_export.type) + _set_error("Expected 'var'."); + else + _set_error("Expected 'var' or 'func'."); + return; + } + + rpc_mode=ScriptInstance::RPC_MODE_SYNC; + continue; + } break; case GDTokenizer::TK_PR_VAR: { //variale declaration and (eventual) initialization @@ -2884,8 +2964,12 @@ void GDParser::_parse_class(ClassNode *p_class) { member.expression=NULL; member._export.name=member.identifier; member.line=tokenizer->get_token_line(); + member.rpc_mode=rpc_mode; + tokenizer->advance(); + rpc_mode=ScriptInstance::RPC_MODE_DISABLED; + if (tokenizer->get_token()==GDTokenizer::TK_OP_ASSIGN) { #ifdef DEBUG_ENABLED @@ -3228,6 +3312,7 @@ void GDParser::clear() { current_class=NULL; completion_found=false; + rpc_mode=ScriptInstance::RPC_MODE_DISABLED; current_function=NULL; diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index 2d6b52c473..9e6f6e6765 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -33,6 +33,7 @@ #include "gd_functions.h" #include "map.h" #include "object.h" +#include "script_language.h" class GDParser { public: @@ -88,6 +89,7 @@ public: StringName getter; int line; Node *expression; + ScriptInstance::RPCMode rpc_mode; }; struct Constant { StringName identifier; @@ -119,12 +121,13 @@ public: struct FunctionNode : public Node { bool _static; + ScriptInstance::RPCMode rpc_mode; StringName name; Vector<StringName> arguments; Vector<Node*> default_values; BlockNode *body; - FunctionNode() { type=TYPE_FUNCTION; _static=false; } + FunctionNode() { type=TYPE_FUNCTION; _static=false; rpc_mode=ScriptInstance::RPC_MODE_DISABLED; } }; @@ -429,6 +432,9 @@ private: PropertyInfo current_export; + ScriptInstance::RPCMode rpc_mode; + + void _set_error(const String& p_error, int p_line=-1, int p_column=-1); bool _recover_from_completion(); diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index 2b8d6e86e2..144fbe2626 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -250,7 +250,7 @@ void GDScript::_update_placeholder(PlaceHolderScriptInstance *p_placeholder) { #endif -void GDScript::get_method_list(List<MethodInfo> *p_list) const { +void GDScript::get_script_method_list(List<MethodInfo> *p_list) const { for (const Map<StringName,GDFunction*>::Element *E=member_functions.front();E;E=E->next()) { MethodInfo mi; @@ -1300,6 +1300,46 @@ ScriptLanguage *GDInstance::get_language() { return GDScriptLanguage::get_singleton(); } +GDInstance::RPCMode GDInstance::get_rpc_mode(const StringName& p_method) const { + + const GDScript *cscript = script.ptr(); + + while(cscript) { + const Map<StringName,GDFunction*>::Element *E=cscript->member_functions.find(p_method); + if (E) { + + if (E->get()->get_rpc_mode()!=RPC_MODE_DISABLED) { + return E->get()->get_rpc_mode(); + } + + } + cscript=cscript->_base; + } + + return RPC_MODE_DISABLED; +} + +GDInstance::RPCMode GDInstance::get_rset_mode(const StringName& p_variable) const { + + const GDScript *cscript = script.ptr(); + + while(cscript) { + const Map<StringName,GDScript::MemberInfo>::Element *E=cscript->member_indices.find(p_variable); + if (E) { + + if (E->get().rpc_mode) { + return E->get().rpc_mode; + } + + } + cscript=cscript->_base; + } + + return RPC_MODE_DISABLED; +} + + + void GDInstance::reload_members() { #ifdef DEBUG_ENABLED @@ -1811,6 +1851,10 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "pass", "return", "while", + "remote", + "sync", + "master", + "slave", 0}; diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 28a0df1efd..8ec11c1e3e 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -64,6 +64,7 @@ class GDScript : public Script { int index; StringName setter; StringName getter; + ScriptInstance::RPCMode rpc_mode; }; friend class GDInstance; @@ -181,7 +182,7 @@ public: bool get_property_default_value(const StringName& p_property,Variant& r_value) const; - virtual void get_method_list(List<MethodInfo> *p_list) const; + virtual void get_script_method_list(List<MethodInfo> *p_list) const; virtual bool has_method(const StringName& p_method) const; virtual MethodInfo get_method_info(const StringName& p_method) const; @@ -236,6 +237,10 @@ public: void reload_members(); + virtual RPCMode get_rpc_mode(const StringName& p_method) const; + virtual RPCMode get_rset_mode(const StringName& p_variable) const; + + GDInstance(); ~GDInstance(); @@ -250,23 +255,23 @@ class GDScriptLanguage : public ScriptLanguage { Map<StringName,int> globals; - struct CallLevel { + struct CallLevel { - Variant *stack; - GDFunction *function; - GDInstance *instance; - int *ip; - int *line; + Variant *stack; + GDFunction *function; + GDInstance *instance; + int *ip; + int *line; - }; + }; - int _debug_parse_err_line; - String _debug_parse_err_file; - String _debug_error; - int _debug_call_stack_pos; - int _debug_max_call_stack; - CallLevel *_call_stack; + int _debug_parse_err_line; + String _debug_parse_err_file; + String _debug_error; + int _debug_call_stack_pos; + int _debug_max_call_stack; + CallLevel *_call_stack; void _add_global(const StringName& p_name,const Variant& p_value); @@ -288,54 +293,54 @@ public: int calls; - bool debug_break(const String& p_error,bool p_allow_continue=true); - bool debug_break_parse(const String& p_file, int p_line,const String& p_error); + bool debug_break(const String& p_error,bool p_allow_continue=true); + bool debug_break_parse(const String& p_file, int p_line,const String& p_error); - _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) { + _FORCE_INLINE_ void enter_function(GDInstance *p_instance,GDFunction *p_function, Variant *p_stack, int *p_ip, int *p_line) { - if (Thread::get_main_ID()!=Thread::get_caller_ID()) - return; //no support for other threads than main for now + if (Thread::get_main_ID()!=Thread::get_caller_ID()) + return; //no support for other threads than main for now - if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0) - ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 ); + if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0) + ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() +1 ); - if (_debug_call_stack_pos >= _debug_max_call_stack) { - //stack overflow - _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")"; - ScriptDebugger::get_singleton()->debug(this); - return; - } + if (_debug_call_stack_pos >= _debug_max_call_stack) { + //stack overflow + _debug_error="Stack Overflow (Stack Size: "+itos(_debug_max_call_stack)+")"; + ScriptDebugger::get_singleton()->debug(this); + return; + } - _call_stack[_debug_call_stack_pos].stack=p_stack; - _call_stack[_debug_call_stack_pos].instance=p_instance; - _call_stack[_debug_call_stack_pos].function=p_function; - _call_stack[_debug_call_stack_pos].ip=p_ip; - _call_stack[_debug_call_stack_pos].line=p_line; - _debug_call_stack_pos++; - } + _call_stack[_debug_call_stack_pos].stack=p_stack; + _call_stack[_debug_call_stack_pos].instance=p_instance; + _call_stack[_debug_call_stack_pos].function=p_function; + _call_stack[_debug_call_stack_pos].ip=p_ip; + _call_stack[_debug_call_stack_pos].line=p_line; + _debug_call_stack_pos++; + } - _FORCE_INLINE_ void exit_function() { + _FORCE_INLINE_ void exit_function() { - if (Thread::get_main_ID()!=Thread::get_caller_ID()) - return; //no support for other threads than main for now + if (Thread::get_main_ID()!=Thread::get_caller_ID()) + return; //no support for other threads than main for now - if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0) - ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 ); + if (ScriptDebugger::get_singleton()->get_lines_left()>0 && ScriptDebugger::get_singleton()->get_depth()>=0) + ScriptDebugger::get_singleton()->set_depth( ScriptDebugger::get_singleton()->get_depth() -1 ); - if (_debug_call_stack_pos==0) { + if (_debug_call_stack_pos==0) { - _debug_error="Stack Underflow (Engine Bug)"; - ScriptDebugger::get_singleton()->debug(this); - return; - } + _debug_error="Stack Underflow (Engine Bug)"; + ScriptDebugger::get_singleton()->debug(this); + return; + } - _debug_call_stack_pos--; - } + _debug_call_stack_pos--; + } virtual Vector<StackInfo> debug_get_current_stack_info() { - if (Thread::get_main_ID()!=Thread::get_caller_ID()) - return Vector<StackInfo>(); + if (Thread::get_main_ID()!=Thread::get_caller_ID()) + return Vector<StackInfo>(); Vector<StackInfo> csi; csi.resize(_debug_call_stack_pos); diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index 93863c4eb2..47e740b227 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -100,6 +100,10 @@ const char* GDTokenizer::token_names[TK_MAX]={ "yield", "signal", "breakpoint", +"rpc", +"sync", +"master", +"slave", "'['", "']'", "'{'", @@ -865,6 +869,10 @@ void GDTokenizerText::_advance() { {TK_PR_YIELD,"yield"}, {TK_PR_SIGNAL,"signal"}, {TK_PR_BREAKPOINT,"breakpoint"}, + {TK_PR_REMOTE,"remote"}, + {TK_PR_MASTER,"master"}, + {TK_PR_SLAVE,"slave"}, + {TK_PR_SYNC,"sync"}, {TK_PR_CONST,"const"}, //controlflow {TK_CF_IF,"if"}, @@ -1047,7 +1055,7 @@ void GDTokenizerText::advance(int p_amount) { ////////////////////////////////////////////////////////////////////////////////////////////////////// -#define BYTECODE_VERSION 10 +#define BYTECODE_VERSION 11 Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) { diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index aaff573090..1815f82894 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -107,6 +107,10 @@ public: TK_PR_YIELD, TK_PR_SIGNAL, TK_PR_BREAKPOINT, + TK_PR_REMOTE, + TK_PR_SYNC, + TK_PR_MASTER, + TK_PR_SLAVE, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, diff --git a/modules/visual_script/visual_script.cpp b/modules/visual_script/visual_script.cpp index 91219679db..469ceaac20 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -992,7 +992,7 @@ bool VisualScript::get_property_default_value(const StringName& p_property,Varia r_value=variables[ script_variable_remap[p_property] ].default_value; return true; } -void VisualScript::get_method_list(List<MethodInfo> *p_list) const { +void VisualScript::get_script_method_list(List<MethodInfo> *p_list) const { for (Map<StringName,Function>::Element *E=functions.front();E;E=E->next()) { @@ -1767,7 +1767,7 @@ Variant VisualScriptInstance::_call_internal(const StringName& p_method, void* p } -Variant VisualScriptInstance::call(const StringName& p_method,const Variant** p_args,int p_argcount,Variant::CallError &r_error){ +Variant VisualScriptInstance::call(const StringName& p_method, const Variant** p_args, int p_argcount, Variant::CallError &r_error){ r_error.error=Variant::CallError::CALL_OK; //ok by default @@ -1871,6 +1871,15 @@ Ref<Script> VisualScriptInstance::get_script() const{ return script; } +ScriptInstance::RPCMode VisualScriptInstance::get_rpc_mode(const StringName& p_method) const { + + return RPC_MODE_DISABLED; +} +ScriptInstance::RPCMode VisualScriptInstance::get_rset_mode(const StringName& p_variable) const { + + return RPC_MODE_DISABLED; +} + void VisualScriptInstance::create(const Ref<VisualScript>& p_script,Object *p_owner) { diff --git a/modules/visual_script/visual_script.h b/modules/visual_script/visual_script.h index 786b9b873e..398df95664 100644 --- a/modules/visual_script/visual_script.h +++ b/modules/visual_script/visual_script.h @@ -320,7 +320,7 @@ public: virtual void get_script_signal_list(List<MethodInfo> *r_signals) const; virtual bool get_property_default_value(const StringName& p_property,Variant& r_value) const; - virtual void get_method_list(List<MethodInfo> *p_list) const; + virtual void get_script_method_list(List<MethodInfo> *p_list) const; virtual bool has_method(const StringName& p_method) const; virtual MethodInfo get_method_info(const StringName& p_method) const; @@ -413,6 +413,9 @@ public: virtual ScriptLanguage *get_language(); + virtual RPCMode get_rpc_mode(const StringName& p_method) const; + virtual RPCMode get_rset_mode(const StringName& p_variable) const; + VisualScriptInstance(); ~VisualScriptInstance(); }; diff --git a/modules/visual_script/visual_script_func_nodes.cpp b/modules/visual_script/visual_script_func_nodes.cpp index 4006dab4a5..14bc0bc828 100644 --- a/modules/visual_script/visual_script_func_nodes.cpp +++ b/modules/visual_script/visual_script_func_nodes.cpp @@ -1951,7 +1951,7 @@ PropertyInfo VisualScriptScriptCall::get_input_value_port_info(int p_idx) const{ return PropertyInfo(); List<MethodInfo> functions; - script->get_method_list(&functions); + script->get_script_method_list(&functions); for (List<MethodInfo>::Element *E=functions.front();E;E=E->next()) { if (E->get().name==function) { if (p_idx<0 || p_idx>=E->get().arguments.size()) @@ -2118,7 +2118,7 @@ void VisualScriptScriptCall::_validate_property(PropertyInfo& property) const { Ref<VisualScript> vs = get_visual_script(); if (vs.is_valid()) { - vs->get_method_list(&methods); + vs->get_script_method_list(&methods); } } else { @@ -2130,7 +2130,7 @@ void VisualScriptScriptCall::_validate_property(PropertyInfo& property) const { if (!script.is_valid()) return; - script->get_method_list(&methods); + script->get_script_method_list(&methods); } |