diff options
76 files changed, 9351 insertions, 225 deletions
diff --git a/core/io/networked_multiplayer_peer.cpp b/core/io/networked_multiplayer_peer.cpp index 79f3e129e1..47e5f3729c 100644 --- a/core/io/networked_multiplayer_peer.cpp +++ b/core/io/networked_multiplayer_peer.cpp @@ -5,22 +5,34 @@ void NetworkedMultiplayerPeer::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_transfer_mode","mode"), &NetworkedMultiplayerPeer::set_transfer_mode ); ObjectTypeDB::bind_method(_MD("set_target_peer","id"), &NetworkedMultiplayerPeer::set_target_peer ); - ObjectTypeDB::bind_method(_MD("set_channel","id"), &NetworkedMultiplayerPeer::set_channel ); ObjectTypeDB::bind_method(_MD("get_packet_peer"), &NetworkedMultiplayerPeer::get_packet_peer ); - ObjectTypeDB::bind_method(_MD("get_packet_channel"), &NetworkedMultiplayerPeer::get_packet_channel ); ObjectTypeDB::bind_method(_MD("poll"), &NetworkedMultiplayerPeer::poll ); + ObjectTypeDB::bind_method(_MD("get_connection_status"), &NetworkedMultiplayerPeer::get_connection_status ); + ObjectTypeDB::bind_method(_MD("get_unique_id"), &NetworkedMultiplayerPeer::get_unique_id ); - BIND_CONSTANT( TARGET_ALL_PEERS ); + ObjectTypeDB::bind_method(_MD("set_refuse_new_connections","enable"), &NetworkedMultiplayerPeer::set_refuse_new_connections ); + ObjectTypeDB::bind_method(_MD("is_refusing_new_connections"), &NetworkedMultiplayerPeer::is_refusing_new_connections ); BIND_CONSTANT( TRANSFER_MODE_UNRELIABLE ); + BIND_CONSTANT( TRANSFER_MODE_UNRELIABLE_ORDERED ); BIND_CONSTANT( TRANSFER_MODE_RELIABLE ); - BIND_CONSTANT( TRANSFER_MODE_ORDERED ); + + BIND_CONSTANT( CONNECTION_DISCONNECTED ); + BIND_CONSTANT( CONNECTION_CONNECTING ); + BIND_CONSTANT( CONNECTION_CONNECTED ); + + BIND_CONSTANT( TARGET_PEER_BROADCAST ); + BIND_CONSTANT( TARGET_PEER_SERVER ); + ADD_SIGNAL( MethodInfo("peer_connected",PropertyInfo(Variant::INT,"id"))); ADD_SIGNAL( MethodInfo("peer_disconnected",PropertyInfo(Variant::INT,"id"))); + ADD_SIGNAL( MethodInfo("server_disconnected")); + ADD_SIGNAL( MethodInfo("connection_succeeded") ); + ADD_SIGNAL( MethodInfo("connection_failed") ); } NetworkedMultiplayerPeer::NetworkedMultiplayerPeer() { diff --git a/core/io/networked_multiplayer_peer.h b/core/io/networked_multiplayer_peer.h index f140b57b8b..485200a9a9 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/networked_multiplayer_peer.h @@ -12,29 +12,43 @@ protected: public: enum { - TARGET_ALL_PEERS=0xFFFFFF // send to this for all peers + TARGET_PEER_BROADCAST=0, + TARGET_PEER_SERVER=1 }; - enum TransferMode { TRANSFER_MODE_UNRELIABLE, + TRANSFER_MODE_UNRELIABLE_ORDERED, TRANSFER_MODE_RELIABLE, - TRANSFER_MODE_ORDERED }; - virtual void set_transfer_mode(TransferMode p_mode)=0; - virtual void set_target_peer(int p_peer)=0; - virtual void set_channel(int p_channel)=0; + enum ConnectionStatus { + CONNECTION_DISCONNECTED, + CONNECTION_CONNECTING, + CONNECTION_CONNECTED, + }; + virtual void set_transfer_mode(TransferMode p_mode)=0; + virtual void set_target_peer(int p_peer_id)=0; + virtual int get_packet_peer() const=0; - virtual int get_packet_channel() const=0; + virtual bool is_server() const=0; virtual void poll()=0; + virtual int get_unique_id() const=0; + + virtual void set_refuse_new_connections(bool p_enable)=0; + virtual bool is_refusing_new_connections() const=0; + + + virtual ConnectionStatus get_connection_status() const=0; + NetworkedMultiplayerPeer(); }; VARIANT_ENUM_CAST( NetworkedMultiplayerPeer::TransferMode ) +VARIANT_ENUM_CAST( NetworkedMultiplayerPeer::ConnectionStatus ) #endif // NetworkedMultiplayerPeer_H diff --git a/core/script_language.h b/core/script_language.h index 0e3f298790..aac94bb067 100644 --- a/core/script_language.h +++ b/core/script_language.h @@ -113,7 +113,7 @@ public: virtual bool get_property_default_value(const StringName& p_property,Variant& r_value) const=0; virtual void update_exports() {} //editor tool - virtual void get_method_list(List<MethodInfo> *p_list) const=0; + virtual void get_script_method_list(List<MethodInfo> *p_list) const=0; Script() {} @@ -121,6 +121,8 @@ public: class ScriptInstance { public: + + virtual bool set(const StringName& p_name, const Variant& p_value)=0; virtual bool get(const StringName& p_name, Variant &r_ret) const=0; virtual void get_property_list(List<PropertyInfo> *p_properties) const=0; @@ -148,6 +150,17 @@ public: virtual bool is_placeholder() const { return false; } + enum RPCMode { + RPC_MODE_DISABLED, + RPC_MODE_REMOTE, + RPC_MODE_SYNC, + RPC_MODE_MASTER, + RPC_MODE_SLAVE, + }; + + virtual RPCMode get_rpc_mode(const StringName& p_method) const=0; + virtual RPCMode get_rset_mode(const StringName& p_variable) const=0; + virtual ScriptLanguage *get_language()=0; virtual ~ScriptInstance(); }; @@ -279,6 +292,9 @@ public: virtual bool is_placeholder() const { return true; } + virtual RPCMode get_rpc_mode(const StringName& p_method) const { return RPC_MODE_DISABLED; } + virtual RPCMode get_rset_mode(const StringName& p_variable) const { return RPC_MODE_DISABLED; } + PlaceHolderScriptInstance(ScriptLanguage *p_language, Ref<Script> p_script,Object *p_owner); ~PlaceHolderScriptInstance(); diff --git a/doc/base/classes.xml b/doc/base/classes.xml index 56a24d77ee..4b6f5f510d 100644 --- a/doc/base/classes.xml +++ b/doc/base/classes.xml @@ -11505,6 +11505,13 @@ Get the undo/redo object. Most actions in the editor can be undoable, so use this object to make sure this happens when it's worth it. </description> </method> + <method name="get_window_layout" qualifiers="virtual"> + <argument index="0" name="layout" type="ConfigFile"> + </argument> + <description> + Get the GUI layout of the plugin. This is used to save the project's editor layout when the [method EditorPlugin.queue_save_layout] is called or the editor layout was changed(For example changing the position of a dock). + </description> + </method> <method name="handles" qualifiers="virtual"> <return type="bool"> </return> @@ -11529,6 +11536,11 @@ Remember that you have to manage the visibility of all your editor controls manually. </description> </method> + <method name="queue_save_layout" qualifiers="const"> + <description> + Queue save the project's editor layout. + </description> + </method> <method name="remove_control_from_bottom_panel"> <argument index="0" name="control" type="Control"> </argument> @@ -11564,6 +11576,11 @@ Remove the import plugin, don't forget to call this on exit. </description> </method> + <method name="save_external_data" qualifiers="virtual"> + <description> + This method is called after the editor save the project or when the it's closed. It asks the plugin to save edited external scenes/resources. + </description> + </method> <method name="set_state" qualifiers="virtual"> <argument index="0" name="state" type="Dictionary"> </argument> @@ -11571,6 +11588,13 @@ Restore the state saved by [method EditorPlugin.get_state]. </description> </method> + <method name="set_window_layout" qualifiers="virtual"> + <argument index="0" name="layout" type="ConfigFile"> + </argument> + <description> + Restore the plugin GUI layout saved by [method EditorPlugin.get_window_layout]. + </description> + </method> </methods> <constants> <constant name="CONTAINER_TOOLBAR" value="0"> diff --git a/modules/enet/SCsub b/modules/enet/SCsub new file mode 100644 index 0000000000..c676c55c89 --- /dev/null +++ b/modules/enet/SCsub @@ -0,0 +1,6 @@ +Import('env') + +env.add_source_files(env.modules_sources,"*.cpp") +env.add_source_files(env.modules_sources,"*.c") + +Export('env') diff --git a/modules/enet/callbacks.c b/modules/enet/callbacks.c new file mode 100644 index 0000000000..b3990af1fb --- /dev/null +++ b/modules/enet/callbacks.c @@ -0,0 +1,53 @@ +/** + @file callbacks.c + @brief ENet callback functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +static ENetCallbacks callbacks = { malloc, free, abort }; + +int +enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits) +{ + if (version < ENET_VERSION_CREATE (1, 3, 0)) + return -1; + + if (inits -> malloc != NULL || inits -> free != NULL) + { + if (inits -> malloc == NULL || inits -> free == NULL) + return -1; + + callbacks.malloc = inits -> malloc; + callbacks.free = inits -> free; + } + + if (inits -> no_memory != NULL) + callbacks.no_memory = inits -> no_memory; + + return enet_initialize (); +} + +ENetVersion +enet_linked_version (void) +{ + return ENET_VERSION; +} + +void * +enet_malloc (size_t size) +{ + void * memory = callbacks.malloc (size); + + if (memory == NULL) + callbacks.no_memory (); + + return memory; +} + +void +enet_free (void * memory) +{ + callbacks.free (memory); +} + diff --git a/modules/enet/compress.c b/modules/enet/compress.c new file mode 100644 index 0000000000..784489a787 --- /dev/null +++ b/modules/enet/compress.c @@ -0,0 +1,654 @@ +/** + @file compress.c + @brief An adaptive order-2 PPM range coder +*/ +#define ENET_BUILDING_LIB 1 +#include <string.h> +#include "enet/enet.h" + +typedef struct _ENetSymbol +{ + /* binary indexed tree of symbols */ + enet_uint8 value; + enet_uint8 count; + enet_uint16 under; + enet_uint16 left, right; + + /* context defined by this symbol */ + enet_uint16 symbols; + enet_uint16 escapes; + enet_uint16 total; + enet_uint16 parent; +} ENetSymbol; + +/* adaptation constants tuned aggressively for small packet sizes rather than large file compression */ +enum +{ + ENET_RANGE_CODER_TOP = 1<<24, + ENET_RANGE_CODER_BOTTOM = 1<<16, + + ENET_CONTEXT_SYMBOL_DELTA = 3, + ENET_CONTEXT_SYMBOL_MINIMUM = 1, + ENET_CONTEXT_ESCAPE_MINIMUM = 1, + + ENET_SUBCONTEXT_ORDER = 2, + ENET_SUBCONTEXT_SYMBOL_DELTA = 2, + ENET_SUBCONTEXT_ESCAPE_DELTA = 5 +}; + +/* context exclusion roughly halves compression speed, so disable for now */ +#undef ENET_CONTEXT_EXCLUSION + +typedef struct _ENetRangeCoder +{ + /* only allocate enough symbols for reasonable MTUs, would need to be larger for large file compression */ + ENetSymbol symbols[4096]; +} ENetRangeCoder; + +void * +enet_range_coder_create (void) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) enet_malloc (sizeof (ENetRangeCoder)); + if (rangeCoder == NULL) + return NULL; + + return rangeCoder; +} + +void +enet_range_coder_destroy (void * context) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + if (rangeCoder == NULL) + return; + + enet_free (rangeCoder); +} + +#define ENET_SYMBOL_CREATE(symbol, value_, count_) \ +{ \ + symbol = & rangeCoder -> symbols [nextSymbol ++]; \ + symbol -> value = value_; \ + symbol -> count = count_; \ + symbol -> under = count_; \ + symbol -> left = 0; \ + symbol -> right = 0; \ + symbol -> symbols = 0; \ + symbol -> escapes = 0; \ + symbol -> total = 0; \ + symbol -> parent = 0; \ +} + +#define ENET_CONTEXT_CREATE(context, escapes_, minimum) \ +{ \ + ENET_SYMBOL_CREATE (context, 0, 0); \ + (context) -> escapes = escapes_; \ + (context) -> total = escapes_ + 256*minimum; \ + (context) -> symbols = 0; \ +} + +static enet_uint16 +enet_symbol_rescale (ENetSymbol * symbol) +{ + enet_uint16 total = 0; + for (;;) + { + symbol -> count -= symbol->count >> 1; + symbol -> under = symbol -> count; + if (symbol -> left) + symbol -> under += enet_symbol_rescale (symbol + symbol -> left); + total += symbol -> under; + if (! symbol -> right) break; + symbol += symbol -> right; + } + return total; +} + +#define ENET_CONTEXT_RESCALE(context, minimum) \ +{ \ + (context) -> total = (context) -> symbols ? enet_symbol_rescale ((context) + (context) -> symbols) : 0; \ + (context) -> escapes -= (context) -> escapes >> 1; \ + (context) -> total += (context) -> escapes + 256*minimum; \ +} + +#define ENET_RANGE_CODER_OUTPUT(value) \ +{ \ + if (outData >= outEnd) \ + return 0; \ + * outData ++ = value; \ +} + +#define ENET_RANGE_CODER_ENCODE(under, count, total) \ +{ \ + encodeRange /= (total); \ + encodeLow += (under) * encodeRange; \ + encodeRange *= (count); \ + for (;;) \ + { \ + if((encodeLow ^ (encodeLow + encodeRange)) >= ENET_RANGE_CODER_TOP) \ + { \ + if(encodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ + encodeRange = -encodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ + } \ + ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ + encodeRange <<= 8; \ + encodeLow <<= 8; \ + } \ +} + +#define ENET_RANGE_CODER_FLUSH \ +{ \ + while (encodeLow) \ + { \ + ENET_RANGE_CODER_OUTPUT (encodeLow >> 24); \ + encodeLow <<= 8; \ + } \ +} + +#define ENET_RANGE_CODER_FREE_SYMBOLS \ +{ \ + if (nextSymbol >= sizeof (rangeCoder -> symbols) / sizeof (ENetSymbol) - ENET_SUBCONTEXT_ORDER ) \ + { \ + nextSymbol = 0; \ + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); \ + predicted = 0; \ + order = 0; \ + } \ +} + +#define ENET_CONTEXT_ENCODE(context, symbol_, value_, under_, count_, update, minimum) \ +{ \ + under_ = value*minimum; \ + count_ = minimum; \ + if (! (context) -> symbols) \ + { \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + (context) -> symbols = symbol_ - (context); \ + } \ + else \ + { \ + ENetSymbol * node = (context) + (context) -> symbols; \ + for (;;) \ + { \ + if (value_ < node -> value) \ + { \ + node -> under += update; \ + if (node -> left) { node += node -> left; continue; } \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> left = symbol_ - node; \ + } \ + else \ + if (value_ > node -> value) \ + { \ + under_ += node -> under; \ + if (node -> right) { node += node -> right; continue; } \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> right = symbol_ - node; \ + } \ + else \ + { \ + count_ += node -> count; \ + under_ += node -> under - node -> count; \ + node -> under += update; \ + node -> count += update; \ + symbol_ = node; \ + } \ + break; \ + } \ + } \ +} + +#ifdef ENET_CONTEXT_EXCLUSION +static const ENetSymbol emptyContext = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +#define ENET_CONTEXT_WALK(context, body) \ +{ \ + const ENetSymbol * node = (context) + (context) -> symbols; \ + const ENetSymbol * stack [256]; \ + size_t stackSize = 0; \ + while (node -> left) \ + { \ + stack [stackSize ++] = node; \ + node += node -> left; \ + } \ + for (;;) \ + { \ + body; \ + if (node -> right) \ + { \ + node += node -> right; \ + while (node -> left) \ + { \ + stack [stackSize ++] = node; \ + node += node -> left; \ + } \ + } \ + else \ + if (stackSize <= 0) \ + break; \ + else \ + node = stack [-- stackSize]; \ + } \ +} + +#define ENET_CONTEXT_ENCODE_EXCLUDE(context, value_, under, total, minimum) \ +ENET_CONTEXT_WALK(context, { \ + if (node -> value != value_) \ + { \ + enet_uint16 parentCount = rangeCoder -> symbols [node -> parent].count + minimum; \ + if (node -> value < value_) \ + under -= parentCount; \ + total -= parentCount; \ + } \ +}) +#endif + +size_t +enet_range_coder_compress (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; + const enet_uint8 * inData, * inEnd; + enet_uint32 encodeLow = 0, encodeRange = ~0; + ENetSymbol * root; + enet_uint16 predicted = 0; + size_t order = 0, nextSymbol = 0; + + if (rangeCoder == NULL || inBufferCount <= 0 || inLimit <= 0) + return 0; + + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + for (;;) + { + ENetSymbol * subcontext, * symbol; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & emptyContext; +#endif + enet_uint8 value; + enet_uint16 count, under, * parent = & predicted, total; + if (inData >= inEnd) + { + if (inBufferCount <= 0) + break; + inData = (const enet_uint8 *) inBuffers -> data; + inEnd = & inData [inBuffers -> dataLength]; + inBuffers ++; + inBufferCount --; + } + value = * inData ++; + + for (subcontext = & rangeCoder -> symbols [predicted]; + subcontext != root; +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) + { + ENET_CONTEXT_ENCODE (subcontext, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + total = subcontext -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) + ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, 0); +#endif + if (count > 0) + { + ENET_RANGE_CODER_ENCODE (subcontext -> escapes + under, count, total); + } + else + { + if (subcontext -> escapes > 0 && subcontext -> escapes < total) + ENET_RANGE_CODER_ENCODE (0, subcontext -> escapes, total); + subcontext -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; + subcontext -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; + } + subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (subcontext, 0); + if (count > 0) goto nextInput; + } + + ENET_CONTEXT_ENCODE (root, symbol, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + total = root -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > ENET_SUBCONTEXT_SYMBOL_DELTA + ENET_SUBCONTEXT_ESCAPE_DELTA) + ENET_CONTEXT_ENCODE_EXCLUDE (childContext, value, under, total, ENET_CONTEXT_SYMBOL_MINIMUM); +#endif + ENET_RANGE_CODER_ENCODE (root -> escapes + under, count, total); + root -> total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); + + nextInput: + if (order >= ENET_SUBCONTEXT_ORDER) + predicted = rangeCoder -> symbols [predicted].parent; + else + order ++; + ENET_RANGE_CODER_FREE_SYMBOLS; + } + + ENET_RANGE_CODER_FLUSH; + + return (size_t) (outData - outStart); +} + +#define ENET_RANGE_CODER_SEED \ +{ \ + if (inData < inEnd) decodeCode |= * inData ++ << 24; \ + if (inData < inEnd) decodeCode |= * inData ++ << 16; \ + if (inData < inEnd) decodeCode |= * inData ++ << 8; \ + if (inData < inEnd) decodeCode |= * inData ++; \ +} + +#define ENET_RANGE_CODER_READ(total) ((decodeCode - decodeLow) / (decodeRange /= (total))) + +#define ENET_RANGE_CODER_DECODE(under, count, total) \ +{ \ + decodeLow += (under) * decodeRange; \ + decodeRange *= (count); \ + for (;;) \ + { \ + if((decodeLow ^ (decodeLow + decodeRange)) >= ENET_RANGE_CODER_TOP) \ + { \ + if(decodeRange >= ENET_RANGE_CODER_BOTTOM) break; \ + decodeRange = -decodeLow & (ENET_RANGE_CODER_BOTTOM - 1); \ + } \ + decodeCode <<= 8; \ + if (inData < inEnd) \ + decodeCode |= * inData ++; \ + decodeRange <<= 8; \ + decodeLow <<= 8; \ + } \ +} + +#define ENET_CONTEXT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, createRoot, visitNode, createRight, createLeft) \ +{ \ + under_ = 0; \ + count_ = minimum; \ + if (! (context) -> symbols) \ + { \ + createRoot; \ + } \ + else \ + { \ + ENetSymbol * node = (context) + (context) -> symbols; \ + for (;;) \ + { \ + enet_uint16 after = under_ + node -> under + (node -> value + 1)*minimum, before = node -> count + minimum; \ + visitNode; \ + if (code >= after) \ + { \ + under_ += node -> under; \ + if (node -> right) { node += node -> right; continue; } \ + createRight; \ + } \ + else \ + if (code < after - before) \ + { \ + node -> under += update; \ + if (node -> left) { node += node -> left; continue; } \ + createLeft; \ + } \ + else \ + { \ + value_ = node -> value; \ + count_ += node -> count; \ + under_ = after - before; \ + node -> under += update; \ + node -> count += update; \ + symbol_ = node; \ + } \ + break; \ + } \ + } \ +} + +#define ENET_CONTEXT_TRY_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ +ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, return 0, exclude (node -> value, after, before), return 0, return 0) + +#define ENET_CONTEXT_ROOT_DECODE(context, symbol_, code, value_, under_, count_, update, minimum, exclude) \ +ENET_CONTEXT_DECODE (context, symbol_, code, value_, under_, count_, update, minimum, \ + { \ + value_ = code / minimum; \ + under_ = code - code%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + (context) -> symbols = symbol_ - (context); \ + }, \ + exclude (node -> value, after, before), \ + { \ + value_ = node->value + 1 + (code - after)/minimum; \ + under_ = code - (code - after)%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> right = symbol_ - node; \ + }, \ + { \ + value_ = node->value - 1 - (after - before - code - 1)/minimum; \ + under_ = code - (after - before - code - 1)%minimum; \ + ENET_SYMBOL_CREATE (symbol_, value_, update); \ + node -> left = symbol_ - node; \ + }) \ + +#ifdef ENET_CONTEXT_EXCLUSION +typedef struct _ENetExclude +{ + enet_uint8 value; + enet_uint16 under; +} ENetExclude; + +#define ENET_CONTEXT_DECODE_EXCLUDE(context, total, minimum) \ +{ \ + enet_uint16 under = 0; \ + nextExclude = excludes; \ + ENET_CONTEXT_WALK (context, { \ + under += rangeCoder -> symbols [node -> parent].count + minimum; \ + nextExclude -> value = node -> value; \ + nextExclude -> under = under; \ + nextExclude ++; \ + }); \ + total -= under; \ +} + +#define ENET_CONTEXT_EXCLUDED(value_, after, before) \ +{ \ + size_t low = 0, high = nextExclude - excludes; \ + for(;;) \ + { \ + size_t mid = (low + high) >> 1; \ + const ENetExclude * exclude = & excludes [mid]; \ + if (value_ < exclude -> value) \ + { \ + if (low + 1 < high) \ + { \ + high = mid; \ + continue; \ + } \ + if (exclude > excludes) \ + after -= exclude [-1].under; \ + } \ + else \ + { \ + if (value_ > exclude -> value) \ + { \ + if (low + 1 < high) \ + { \ + low = mid; \ + continue; \ + } \ + } \ + else \ + before = 0; \ + after -= exclude -> under; \ + } \ + break; \ + } \ +} +#endif + +#define ENET_CONTEXT_NOT_EXCLUDED(value_, after, before) + +size_t +enet_range_coder_decompress (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit) +{ + ENetRangeCoder * rangeCoder = (ENetRangeCoder *) context; + enet_uint8 * outStart = outData, * outEnd = & outData [outLimit]; + const enet_uint8 * inEnd = & inData [inLimit]; + enet_uint32 decodeLow = 0, decodeCode = 0, decodeRange = ~0; + ENetSymbol * root; + enet_uint16 predicted = 0; + size_t order = 0, nextSymbol = 0; +#ifdef ENET_CONTEXT_EXCLUSION + ENetExclude excludes [256]; + ENetExclude * nextExclude = excludes; +#endif + + if (rangeCoder == NULL || inLimit <= 0) + return 0; + + ENET_CONTEXT_CREATE (root, ENET_CONTEXT_ESCAPE_MINIMUM, ENET_CONTEXT_SYMBOL_MINIMUM); + + ENET_RANGE_CODER_SEED; + + for (;;) + { + ENetSymbol * subcontext, * symbol, * patch; +#ifdef ENET_CONTEXT_EXCLUSION + const ENetSymbol * childContext = & emptyContext; +#endif + enet_uint8 value = 0; + enet_uint16 code, under, count, bottom, * parent = & predicted, total; + + for (subcontext = & rangeCoder -> symbols [predicted]; + subcontext != root; +#ifdef ENET_CONTEXT_EXCLUSION + childContext = subcontext, +#endif + subcontext = & rangeCoder -> symbols [subcontext -> parent]) + { + if (subcontext -> escapes <= 0) + continue; + total = subcontext -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, 0); +#endif + if (subcontext -> escapes >= total) + continue; + code = ENET_RANGE_CODER_READ (total); + if (code < subcontext -> escapes) + { + ENET_RANGE_CODER_DECODE (0, subcontext -> escapes, total); + continue; + } + code -= subcontext -> escapes; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + { + ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_EXCLUDED); + } + else +#endif + { + ENET_CONTEXT_TRY_DECODE (subcontext, symbol, code, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0, ENET_CONTEXT_NOT_EXCLUDED); + } + bottom = symbol - rangeCoder -> symbols; + ENET_RANGE_CODER_DECODE (subcontext -> escapes + under, count, total); + subcontext -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || subcontext -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (subcontext, 0); + goto patchContexts; + } + + total = root -> total; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + ENET_CONTEXT_DECODE_EXCLUDE (childContext, total, ENET_CONTEXT_SYMBOL_MINIMUM); +#endif + code = ENET_RANGE_CODER_READ (total); + if (code < root -> escapes) + { + ENET_RANGE_CODER_DECODE (0, root -> escapes, total); + break; + } + code -= root -> escapes; +#ifdef ENET_CONTEXT_EXCLUSION + if (childContext -> total > 0) + { + ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_EXCLUDED); + } + else +#endif + { + ENET_CONTEXT_ROOT_DECODE (root, symbol, code, value, under, count, ENET_CONTEXT_SYMBOL_DELTA, ENET_CONTEXT_SYMBOL_MINIMUM, ENET_CONTEXT_NOT_EXCLUDED); + } + bottom = symbol - rangeCoder -> symbols; + ENET_RANGE_CODER_DECODE (root -> escapes + under, count, total); + root -> total += ENET_CONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_CONTEXT_SYMBOL_DELTA + ENET_CONTEXT_SYMBOL_MINIMUM || root -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (root, ENET_CONTEXT_SYMBOL_MINIMUM); + + patchContexts: + for (patch = & rangeCoder -> symbols [predicted]; + patch != subcontext; + patch = & rangeCoder -> symbols [patch -> parent]) + { + ENET_CONTEXT_ENCODE (patch, symbol, value, under, count, ENET_SUBCONTEXT_SYMBOL_DELTA, 0); + * parent = symbol - rangeCoder -> symbols; + parent = & symbol -> parent; + if (count <= 0) + { + patch -> escapes += ENET_SUBCONTEXT_ESCAPE_DELTA; + patch -> total += ENET_SUBCONTEXT_ESCAPE_DELTA; + } + patch -> total += ENET_SUBCONTEXT_SYMBOL_DELTA; + if (count > 0xFF - 2*ENET_SUBCONTEXT_SYMBOL_DELTA || patch -> total > ENET_RANGE_CODER_BOTTOM - 0x100) + ENET_CONTEXT_RESCALE (patch, 0); + } + * parent = bottom; + + ENET_RANGE_CODER_OUTPUT (value); + + if (order >= ENET_SUBCONTEXT_ORDER) + predicted = rangeCoder -> symbols [predicted].parent; + else + order ++; + ENET_RANGE_CODER_FREE_SYMBOLS; + } + + return (size_t) (outData - outStart); +} + +/** @defgroup host ENet host functions + @{ +*/ + +/** Sets the packet compressor the host should use to the default range coder. + @param host host to enable the range coder for + @returns 0 on success, < 0 on failure +*/ +int +enet_host_compress_with_range_coder (ENetHost * host) +{ + ENetCompressor compressor; + memset (& compressor, 0, sizeof (compressor)); + compressor.context = enet_range_coder_create(); + if (compressor.context == NULL) + return -1; + compressor.compress = enet_range_coder_compress; + compressor.decompress = enet_range_coder_decompress; + compressor.destroy = enet_range_coder_destroy; + enet_host_compress (host, & compressor); + return 0; +} + +/** @} */ + + diff --git a/modules/enet/config.py b/modules/enet/config.py new file mode 100644 index 0000000000..ea7e83378a --- /dev/null +++ b/modules/enet/config.py @@ -0,0 +1,11 @@ + + +def can_build(platform): + return True + + +def configure(env): + pass + + + diff --git a/modules/enet/enet/callbacks.h b/modules/enet/enet/callbacks.h new file mode 100644 index 0000000000..340a4a9896 --- /dev/null +++ b/modules/enet/enet/callbacks.h @@ -0,0 +1,27 @@ +/** + @file callbacks.h + @brief ENet callbacks +*/ +#ifndef __ENET_CALLBACKS_H__ +#define __ENET_CALLBACKS_H__ + +#include <stdlib.h> + +typedef struct _ENetCallbacks +{ + void * (ENET_CALLBACK * malloc) (size_t size); + void (ENET_CALLBACK * free) (void * memory); + void (ENET_CALLBACK * no_memory) (void); +} ENetCallbacks; + +/** @defgroup callbacks ENet internal callbacks + @{ + @ingroup private +*/ +extern void * enet_malloc (size_t); +extern void enet_free (void *); + +/** @} */ + +#endif /* __ENET_CALLBACKS_H__ */ + diff --git a/modules/enet/enet/enet.h b/modules/enet/enet/enet.h new file mode 100644 index 0000000000..650b199ee5 --- /dev/null +++ b/modules/enet/enet/enet.h @@ -0,0 +1,596 @@ +/** + @file enet.h + @brief ENet public header file +*/ +#ifndef __ENET_ENET_H__ +#define __ENET_ENET_H__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include <stdlib.h> + +#ifdef _WIN32 +#include "enet/win32.h" +#else +#include "enet/unix.h" +#endif + +#include "enet/types.h" +#include "enet/protocol.h" +#include "enet/list.h" +#include "enet/callbacks.h" + +#define ENET_VERSION_MAJOR 1 +#define ENET_VERSION_MINOR 3 +#define ENET_VERSION_PATCH 13 +#define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) +#define ENET_VERSION_GET_MAJOR(version) (((version)>>16)&0xFF) +#define ENET_VERSION_GET_MINOR(version) (((version)>>8)&0xFF) +#define ENET_VERSION_GET_PATCH(version) ((version)&0xFF) +#define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) + +typedef enet_uint32 ENetVersion; + +struct _ENetHost; +struct _ENetEvent; +struct _ENetPacket; + +typedef enum _ENetSocketType +{ + ENET_SOCKET_TYPE_STREAM = 1, + ENET_SOCKET_TYPE_DATAGRAM = 2 +} ENetSocketType; + +typedef enum _ENetSocketWait +{ + ENET_SOCKET_WAIT_NONE = 0, + ENET_SOCKET_WAIT_SEND = (1 << 0), + ENET_SOCKET_WAIT_RECEIVE = (1 << 1), + ENET_SOCKET_WAIT_INTERRUPT = (1 << 2) +} ENetSocketWait; + +typedef enum _ENetSocketOption +{ + ENET_SOCKOPT_NONBLOCK = 1, + ENET_SOCKOPT_BROADCAST = 2, + ENET_SOCKOPT_RCVBUF = 3, + ENET_SOCKOPT_SNDBUF = 4, + ENET_SOCKOPT_REUSEADDR = 5, + ENET_SOCKOPT_RCVTIMEO = 6, + ENET_SOCKOPT_SNDTIMEO = 7, + ENET_SOCKOPT_ERROR = 8, + ENET_SOCKOPT_NODELAY = 9 +} ENetSocketOption; + +typedef enum _ENetSocketShutdown +{ + ENET_SOCKET_SHUTDOWN_READ = 0, + ENET_SOCKET_SHUTDOWN_WRITE = 1, + ENET_SOCKET_SHUTDOWN_READ_WRITE = 2 +} ENetSocketShutdown; + +#define ENET_HOST_ANY 0 +#define ENET_HOST_BROADCAST 0xFFFFFFFFU +#define ENET_PORT_ANY 0 + +/** + * Portable internet address structure. + * + * The host must be specified in network byte-order, and the port must be in host + * byte-order. The constant ENET_HOST_ANY may be used to specify the default + * server host. The constant ENET_HOST_BROADCAST may be used to specify the + * broadcast address (255.255.255.255). This makes sense for enet_host_connect, + * but not for enet_host_create. Once a server responds to a broadcast, the + * address is updated from ENET_HOST_BROADCAST to the server's actual IP address. + */ +typedef struct _ENetAddress +{ + enet_uint32 host; + enet_uint16 port; +} ENetAddress; + +/** + * Packet flag bit constants. + * + * The host must be specified in network byte-order, and the port must be in + * host byte-order. The constant ENET_HOST_ANY may be used to specify the + * default server host. + + @sa ENetPacket +*/ +typedef enum _ENetPacketFlag +{ + /** packet must be received by the target peer and resend attempts should be + * made until the packet is delivered */ + ENET_PACKET_FLAG_RELIABLE = (1 << 0), + /** packet will not be sequenced with other packets + * not supported for reliable packets + */ + ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), + /** packet will not allocate data, and user must supply it instead */ + ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), + /** packet will be fragmented using unreliable (instead of reliable) sends + * if it exceeds the MTU */ + ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3), + + /** whether the packet has been sent from all queues it has been entered into */ + ENET_PACKET_FLAG_SENT = (1<<8) +} ENetPacketFlag; + +typedef void (ENET_CALLBACK * ENetPacketFreeCallback) (struct _ENetPacket *); + +/** + * ENet packet structure. + * + * An ENet data packet that may be sent to or received from a peer. The shown + * fields should only be read and never modified. The data field contains the + * allocated data for the packet. The dataLength fields specifies the length + * of the allocated data. The flags field is either 0 (specifying no flags), + * or a bitwise-or of any combination of the following flags: + * + * ENET_PACKET_FLAG_RELIABLE - packet must be received by the target peer + * and resend attempts should be made until the packet is delivered + * + * ENET_PACKET_FLAG_UNSEQUENCED - packet will not be sequenced with other packets + * (not supported for reliable packets) + * + * ENET_PACKET_FLAG_NO_ALLOCATE - packet will not allocate data, and user must supply it instead + * + * ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT - packet will be fragmented using unreliable + * (instead of reliable) sends if it exceeds the MTU + * + * ENET_PACKET_FLAG_SENT - whether the packet has been sent from all queues it has been entered into + @sa ENetPacketFlag + */ +typedef struct _ENetPacket +{ + size_t referenceCount; /**< internal use only */ + enet_uint32 flags; /**< bitwise-or of ENetPacketFlag constants */ + enet_uint8 * data; /**< allocated data for packet */ + size_t dataLength; /**< length of data */ + ENetPacketFreeCallback freeCallback; /**< function to be called when the packet is no longer in use */ + void * userData; /**< application private data, may be freely modified */ +} ENetPacket; + +typedef struct _ENetAcknowledgement +{ + ENetListNode acknowledgementList; + enet_uint32 sentTime; + ENetProtocol command; +} ENetAcknowledgement; + +typedef struct _ENetOutgoingCommand +{ + ENetListNode outgoingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + enet_uint32 sentTime; + enet_uint32 roundTripTimeout; + enet_uint32 roundTripTimeoutLimit; + enet_uint32 fragmentOffset; + enet_uint16 fragmentLength; + enet_uint16 sendAttempts; + ENetProtocol command; + ENetPacket * packet; +} ENetOutgoingCommand; + +typedef struct _ENetIncomingCommand +{ + ENetListNode incomingCommandList; + enet_uint16 reliableSequenceNumber; + enet_uint16 unreliableSequenceNumber; + ENetProtocol command; + enet_uint32 fragmentCount; + enet_uint32 fragmentsRemaining; + enet_uint32 * fragments; + ENetPacket * packet; +} ENetIncomingCommand; + +typedef enum _ENetPeerState +{ + ENET_PEER_STATE_DISCONNECTED = 0, + ENET_PEER_STATE_CONNECTING = 1, + ENET_PEER_STATE_ACKNOWLEDGING_CONNECT = 2, + ENET_PEER_STATE_CONNECTION_PENDING = 3, + ENET_PEER_STATE_CONNECTION_SUCCEEDED = 4, + ENET_PEER_STATE_CONNECTED = 5, + ENET_PEER_STATE_DISCONNECT_LATER = 6, + ENET_PEER_STATE_DISCONNECTING = 7, + ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT = 8, + ENET_PEER_STATE_ZOMBIE = 9 +} ENetPeerState; + +#ifndef ENET_BUFFER_MAXIMUM +#define ENET_BUFFER_MAXIMUM (1 + 2 * ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS) +#endif + +enum +{ + ENET_HOST_RECEIVE_BUFFER_SIZE = 256 * 1024, + ENET_HOST_SEND_BUFFER_SIZE = 256 * 1024, + ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL = 1000, + ENET_HOST_DEFAULT_MTU = 1400, + ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE = 32 * 1024 * 1024, + ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA = 32 * 1024 * 1024, + + ENET_PEER_DEFAULT_ROUND_TRIP_TIME = 500, + ENET_PEER_DEFAULT_PACKET_THROTTLE = 32, + ENET_PEER_PACKET_THROTTLE_SCALE = 32, + ENET_PEER_PACKET_THROTTLE_COUNTER = 7, + ENET_PEER_PACKET_THROTTLE_ACCELERATION = 2, + ENET_PEER_PACKET_THROTTLE_DECELERATION = 2, + ENET_PEER_PACKET_THROTTLE_INTERVAL = 5000, + ENET_PEER_PACKET_LOSS_SCALE = (1 << 16), + ENET_PEER_PACKET_LOSS_INTERVAL = 10000, + ENET_PEER_WINDOW_SIZE_SCALE = 64 * 1024, + ENET_PEER_TIMEOUT_LIMIT = 32, + ENET_PEER_TIMEOUT_MINIMUM = 5000, + ENET_PEER_TIMEOUT_MAXIMUM = 30000, + ENET_PEER_PING_INTERVAL = 500, + ENET_PEER_UNSEQUENCED_WINDOWS = 64, + ENET_PEER_UNSEQUENCED_WINDOW_SIZE = 1024, + ENET_PEER_FREE_UNSEQUENCED_WINDOWS = 32, + ENET_PEER_RELIABLE_WINDOWS = 16, + ENET_PEER_RELIABLE_WINDOW_SIZE = 0x1000, + ENET_PEER_FREE_RELIABLE_WINDOWS = 8 +}; + +typedef struct _ENetChannel +{ + enet_uint16 outgoingReliableSequenceNumber; + enet_uint16 outgoingUnreliableSequenceNumber; + enet_uint16 usedReliableWindows; + enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS]; + enet_uint16 incomingReliableSequenceNumber; + enet_uint16 incomingUnreliableSequenceNumber; + ENetList incomingReliableCommands; + ENetList incomingUnreliableCommands; +} ENetChannel; + +/** + * An ENet peer which data packets may be sent or received from. + * + * No fields should be modified unless otherwise specified. + */ +typedef struct _ENetPeer +{ + ENetListNode dispatchList; + struct _ENetHost * host; + enet_uint16 outgoingPeerID; + enet_uint16 incomingPeerID; + enet_uint32 connectID; + enet_uint8 outgoingSessionID; + enet_uint8 incomingSessionID; + ENetAddress address; /**< Internet address of the peer */ + void * data; /**< Application private data, may be freely modified */ + ENetPeerState state; + ENetChannel * channels; + size_t channelCount; /**< Number of channels allocated for communication with peer */ + enet_uint32 incomingBandwidth; /**< Downstream bandwidth of the client in bytes/second */ + enet_uint32 outgoingBandwidth; /**< Upstream bandwidth of the client in bytes/second */ + enet_uint32 incomingBandwidthThrottleEpoch; + enet_uint32 outgoingBandwidthThrottleEpoch; + enet_uint32 incomingDataTotal; + enet_uint32 outgoingDataTotal; + enet_uint32 lastSendTime; + enet_uint32 lastReceiveTime; + enet_uint32 nextTimeout; + enet_uint32 earliestTimeout; + enet_uint32 packetLossEpoch; + enet_uint32 packetsSent; + enet_uint32 packetsLost; + enet_uint32 packetLoss; /**< mean packet loss of reliable packets as a ratio with respect to the constant ENET_PEER_PACKET_LOSS_SCALE */ + enet_uint32 packetLossVariance; + enet_uint32 packetThrottle; + enet_uint32 packetThrottleLimit; + enet_uint32 packetThrottleCounter; + enet_uint32 packetThrottleEpoch; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 packetThrottleInterval; + enet_uint32 pingInterval; + enet_uint32 timeoutLimit; + enet_uint32 timeoutMinimum; + enet_uint32 timeoutMaximum; + enet_uint32 lastRoundTripTime; + enet_uint32 lowestRoundTripTime; + enet_uint32 lastRoundTripTimeVariance; + enet_uint32 highestRoundTripTimeVariance; + enet_uint32 roundTripTime; /**< mean round trip time (RTT), in milliseconds, between sending a reliable packet and receiving its acknowledgement */ + enet_uint32 roundTripTimeVariance; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 reliableDataInTransit; + enet_uint16 outgoingReliableSequenceNumber; + ENetList acknowledgements; + ENetList sentReliableCommands; + ENetList sentUnreliableCommands; + ENetList outgoingReliableCommands; + ENetList outgoingUnreliableCommands; + ENetList dispatchedCommands; + int needsDispatch; + enet_uint16 incomingUnsequencedGroup; + enet_uint16 outgoingUnsequencedGroup; + enet_uint32 unsequencedWindow [ENET_PEER_UNSEQUENCED_WINDOW_SIZE / 32]; + enet_uint32 eventData; + size_t totalWaitingData; +} ENetPeer; + +/** An ENet packet compressor for compressing UDP packets before socket sends or receives. + */ +typedef struct _ENetCompressor +{ + /** Context data for the compressor. Must be non-NULL. */ + void * context; + /** Compresses from inBuffers[0:inBufferCount-1], containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * compress) (void * context, const ENetBuffer * inBuffers, size_t inBufferCount, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Decompresses from inData, containing inLimit bytes, to outData, outputting at most outLimit bytes. Should return 0 on failure. */ + size_t (ENET_CALLBACK * decompress) (void * context, const enet_uint8 * inData, size_t inLimit, enet_uint8 * outData, size_t outLimit); + /** Destroys the context when compression is disabled or the host is destroyed. May be NULL. */ + void (ENET_CALLBACK * destroy) (void * context); +} ENetCompressor; + +/** Callback that computes the checksum of the data held in buffers[0:bufferCount-1] */ +typedef enet_uint32 (ENET_CALLBACK * ENetChecksumCallback) (const ENetBuffer * buffers, size_t bufferCount); + +/** Callback for intercepting received raw UDP packets. Should return 1 to intercept, 0 to ignore, or -1 to propagate an error. */ +typedef int (ENET_CALLBACK * ENetInterceptCallback) (struct _ENetHost * host, struct _ENetEvent * event); + +/** An ENet host for communicating with peers. + * + * No fields should be modified unless otherwise stated. + + @sa enet_host_create() + @sa enet_host_destroy() + @sa enet_host_connect() + @sa enet_host_service() + @sa enet_host_flush() + @sa enet_host_broadcast() + @sa enet_host_compress() + @sa enet_host_compress_with_range_coder() + @sa enet_host_channel_limit() + @sa enet_host_bandwidth_limit() + @sa enet_host_bandwidth_throttle() + */ +typedef struct _ENetHost +{ + ENetSocket socket; + ENetAddress address; /**< Internet address of the host */ + enet_uint32 incomingBandwidth; /**< downstream bandwidth of the host */ + enet_uint32 outgoingBandwidth; /**< upstream bandwidth of the host */ + enet_uint32 bandwidthThrottleEpoch; + enet_uint32 mtu; + enet_uint32 randomSeed; + int recalculateBandwidthLimits; + ENetPeer * peers; /**< array of peers allocated for this host */ + size_t peerCount; /**< number of peers allocated for this host */ + size_t channelLimit; /**< maximum number of channels allowed for connected peers */ + enet_uint32 serviceTime; + ENetList dispatchQueue; + int continueSending; + size_t packetSize; + enet_uint16 headerFlags; + ENetProtocol commands [ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS]; + size_t commandCount; + ENetBuffer buffers [ENET_BUFFER_MAXIMUM]; + size_t bufferCount; + ENetChecksumCallback checksum; /**< callback the user can set to enable packet checksums for this host */ + ENetCompressor compressor; + enet_uint8 packetData [2][ENET_PROTOCOL_MAXIMUM_MTU]; + ENetAddress receivedAddress; + enet_uint8 * receivedData; + size_t receivedDataLength; + enet_uint32 totalSentData; /**< total data sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalSentPackets; /**< total UDP packets sent, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedData; /**< total data received, user should reset to 0 as needed to prevent overflow */ + enet_uint32 totalReceivedPackets; /**< total UDP packets received, user should reset to 0 as needed to prevent overflow */ + ENetInterceptCallback intercept; /**< callback the user can set to intercept received raw UDP packets */ + size_t connectedPeers; + size_t bandwidthLimitedPeers; + size_t duplicatePeers; /**< optional number of allowed peers from duplicate IPs, defaults to ENET_PROTOCOL_MAXIMUM_PEER_ID */ + size_t maximumPacketSize; /**< the maximum allowable packet size that may be sent or received on a peer */ + size_t maximumWaitingData; /**< the maximum aggregate amount of buffer space a peer may use waiting for packets to be delivered */ +} ENetHost; + +/** + * An ENet event type, as specified in @ref ENetEvent. + */ +typedef enum _ENetEventType +{ + /** no event occurred within the specified time limit */ + ENET_EVENT_TYPE_NONE = 0, + + /** a connection request initiated by enet_host_connect has completed. + * The peer field contains the peer which successfully connected. + */ + ENET_EVENT_TYPE_CONNECT = 1, + + /** a peer has disconnected. This event is generated on a successful + * completion of a disconnect initiated by enet_peer_disconnect, if + * a peer has timed out, or if a connection request intialized by + * enet_host_connect has timed out. The peer field contains the peer + * which disconnected. The data field contains user supplied data + * describing the disconnection, or 0, if none is available. + */ + ENET_EVENT_TYPE_DISCONNECT = 2, + + /** a packet has been received from a peer. The peer field specifies the + * peer which sent the packet. The channelID field specifies the channel + * number upon which the packet was received. The packet field contains + * the packet that was received; this packet must be destroyed with + * enet_packet_destroy after use. + */ + ENET_EVENT_TYPE_RECEIVE = 3 +} ENetEventType; + +/** + * An ENet event as returned by enet_host_service(). + + @sa enet_host_service + */ +typedef struct _ENetEvent +{ + ENetEventType type; /**< type of the event */ + ENetPeer * peer; /**< peer that generated a connect, disconnect or receive event */ + enet_uint8 channelID; /**< channel on the peer that generated the event, if appropriate */ + enet_uint32 data; /**< data associated with the event, if appropriate */ + ENetPacket * packet; /**< packet associated with the event, if appropriate */ +} ENetEvent; + +/** @defgroup global ENet global functions + @{ +*/ + +/** + Initializes ENet globally. Must be called prior to using any functions in + ENet. + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize (void); + +/** + Initializes ENet globally and supplies user-overridden callbacks. Must be called prior to using any functions in ENet. Do not use enet_initialize() if you use this variant. Make sure the ENetCallbacks structure is zeroed out so that any additional callbacks added in future versions will be properly ignored. + + @param version the constant ENET_VERSION should be supplied so ENet knows which version of ENetCallbacks struct to use + @param inits user-overridden callbacks where any NULL callbacks will use ENet's defaults + @returns 0 on success, < 0 on failure +*/ +ENET_API int enet_initialize_with_callbacks (ENetVersion version, const ENetCallbacks * inits); + +/** + Shuts down ENet globally. Should be called when a program that has + initialized ENet exits. +*/ +ENET_API void enet_deinitialize (void); + +/** + Gives the linked version of the ENet library. + @returns the version number +*/ +ENET_API ENetVersion enet_linked_version (void); + +/** @} */ + +/** @defgroup private ENet private implementation functions */ + +/** + Returns the wall-time in milliseconds. Its initial value is unspecified + unless otherwise set. + */ +ENET_API enet_uint32 enet_time_get (void); +/** + Sets the current wall-time in milliseconds. + */ +ENET_API void enet_time_set (enet_uint32); + +/** @defgroup socket ENet socket functions + @{ +*/ +ENET_API ENetSocket enet_socket_create (ENetSocketType); +ENET_API int enet_socket_bind (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_get_address (ENetSocket, ENetAddress *); +ENET_API int enet_socket_listen (ENetSocket, int); +ENET_API ENetSocket enet_socket_accept (ENetSocket, ENetAddress *); +ENET_API int enet_socket_connect (ENetSocket, const ENetAddress *); +ENET_API int enet_socket_send (ENetSocket, const ENetAddress *, const ENetBuffer *, size_t); +ENET_API int enet_socket_receive (ENetSocket, ENetAddress *, ENetBuffer *, size_t); +ENET_API int enet_socket_wait (ENetSocket, enet_uint32 *, enet_uint32); +ENET_API int enet_socket_set_option (ENetSocket, ENetSocketOption, int); +ENET_API int enet_socket_get_option (ENetSocket, ENetSocketOption, int *); +ENET_API int enet_socket_shutdown (ENetSocket, ENetSocketShutdown); +ENET_API void enet_socket_destroy (ENetSocket); +ENET_API int enet_socketset_select (ENetSocket, ENetSocketSet *, ENetSocketSet *, enet_uint32); + +/** @} */ + +/** @defgroup Address ENet address functions + @{ +*/ +/** Attempts to resolve the host named by the parameter hostName and sets + the host field in the address parameter if successful. + @param address destination to store resolved address + @param hostName host name to lookup + @retval 0 on success + @retval < 0 on failure + @returns the address of the given hostName in address on success +*/ +ENET_API int enet_address_set_host (ENetAddress * address, const char * hostName); + +/** Gives the printable form of the IP address specified in the address parameter. + @param address address printed + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host_ip (const ENetAddress * address, char * hostName, size_t nameLength); + +/** Attempts to do a reverse lookup of the host field in the address parameter. + @param address address used for reverse lookup + @param hostName destination for name, must not be NULL + @param nameLength maximum length of hostName. + @returns the null-terminated name of the host in hostName on success + @retval 0 on success + @retval < 0 on failure +*/ +ENET_API int enet_address_get_host (const ENetAddress * address, char * hostName, size_t nameLength); + +/** @} */ + +ENET_API ENetPacket * enet_packet_create (const void *, size_t, enet_uint32); +ENET_API void enet_packet_destroy (ENetPacket *); +ENET_API int enet_packet_resize (ENetPacket *, size_t); +ENET_API enet_uint32 enet_crc32 (const ENetBuffer *, size_t); + +ENET_API ENetHost * enet_host_create (const ENetAddress *, size_t, size_t, enet_uint32, enet_uint32); +ENET_API void enet_host_destroy (ENetHost *); +ENET_API ENetPeer * enet_host_connect (ENetHost *, const ENetAddress *, size_t, enet_uint32); +ENET_API int enet_host_check_events (ENetHost *, ENetEvent *); +ENET_API int enet_host_service (ENetHost *, ENetEvent *, enet_uint32); +ENET_API void enet_host_flush (ENetHost *); +ENET_API void enet_host_broadcast (ENetHost *, enet_uint8, ENetPacket *); +ENET_API void enet_host_compress (ENetHost *, const ENetCompressor *); +ENET_API int enet_host_compress_with_range_coder (ENetHost * host); +ENET_API void enet_host_channel_limit (ENetHost *, size_t); +ENET_API void enet_host_bandwidth_limit (ENetHost *, enet_uint32, enet_uint32); +extern void enet_host_bandwidth_throttle (ENetHost *); +extern enet_uint32 enet_host_random_seed (void); + +ENET_API int enet_peer_send (ENetPeer *, enet_uint8, ENetPacket *); +ENET_API ENetPacket * enet_peer_receive (ENetPeer *, enet_uint8 * channelID); +ENET_API void enet_peer_ping (ENetPeer *); +ENET_API void enet_peer_ping_interval (ENetPeer *, enet_uint32); +ENET_API void enet_peer_timeout (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +ENET_API void enet_peer_reset (ENetPeer *); +ENET_API void enet_peer_disconnect (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_now (ENetPeer *, enet_uint32); +ENET_API void enet_peer_disconnect_later (ENetPeer *, enet_uint32); +ENET_API void enet_peer_throttle_configure (ENetPeer *, enet_uint32, enet_uint32, enet_uint32); +extern int enet_peer_throttle (ENetPeer *, enet_uint32); +extern void enet_peer_reset_queues (ENetPeer *); +extern void enet_peer_setup_outgoing_command (ENetPeer *, ENetOutgoingCommand *); +extern ENetOutgoingCommand * enet_peer_queue_outgoing_command (ENetPeer *, const ENetProtocol *, ENetPacket *, enet_uint32, enet_uint16); +extern ENetIncomingCommand * enet_peer_queue_incoming_command (ENetPeer *, const ENetProtocol *, const void *, size_t, enet_uint32, enet_uint32); +extern ENetAcknowledgement * enet_peer_queue_acknowledgement (ENetPeer *, const ENetProtocol *, enet_uint16); +extern void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer *, ENetChannel *); +extern void enet_peer_dispatch_incoming_reliable_commands (ENetPeer *, ENetChannel *); +extern void enet_peer_on_connect (ENetPeer *); +extern void enet_peer_on_disconnect (ENetPeer *); + +ENET_API void * enet_range_coder_create (void); +ENET_API void enet_range_coder_destroy (void *); +ENET_API size_t enet_range_coder_compress (void *, const ENetBuffer *, size_t, size_t, enet_uint8 *, size_t); +ENET_API size_t enet_range_coder_decompress (void *, const enet_uint8 *, size_t, enet_uint8 *, size_t); + +extern size_t enet_protocol_command_size (enet_uint8); + +#ifdef __cplusplus +} +#endif + +#endif /* __ENET_ENET_H__ */ + diff --git a/modules/enet/enet/list.h b/modules/enet/enet/list.h new file mode 100644 index 0000000000..d7b2600848 --- /dev/null +++ b/modules/enet/enet/list.h @@ -0,0 +1,43 @@ +/** + @file list.h + @brief ENet list management +*/ +#ifndef __ENET_LIST_H__ +#define __ENET_LIST_H__ + +#include <stdlib.h> + +typedef struct _ENetListNode +{ + struct _ENetListNode * next; + struct _ENetListNode * previous; +} ENetListNode; + +typedef ENetListNode * ENetListIterator; + +typedef struct _ENetList +{ + ENetListNode sentinel; +} ENetList; + +extern void enet_list_clear (ENetList *); + +extern ENetListIterator enet_list_insert (ENetListIterator, void *); +extern void * enet_list_remove (ENetListIterator); +extern ENetListIterator enet_list_move (ENetListIterator, void *, void *); + +extern size_t enet_list_size (ENetList *); + +#define enet_list_begin(list) ((list) -> sentinel.next) +#define enet_list_end(list) (& (list) -> sentinel) + +#define enet_list_empty(list) (enet_list_begin (list) == enet_list_end (list)) + +#define enet_list_next(iterator) ((iterator) -> next) +#define enet_list_previous(iterator) ((iterator) -> previous) + +#define enet_list_front(list) ((void *) (list) -> sentinel.next) +#define enet_list_back(list) ((void *) (list) -> sentinel.previous) + +#endif /* __ENET_LIST_H__ */ + diff --git a/modules/enet/enet/protocol.h b/modules/enet/enet/protocol.h new file mode 100644 index 0000000000..f8c73d8a66 --- /dev/null +++ b/modules/enet/enet/protocol.h @@ -0,0 +1,198 @@ +/** + @file protocol.h + @brief ENet protocol +*/ +#ifndef __ENET_PROTOCOL_H__ +#define __ENET_PROTOCOL_H__ + +#include "enet/types.h" + +enum +{ + ENET_PROTOCOL_MINIMUM_MTU = 576, + ENET_PROTOCOL_MAXIMUM_MTU = 4096, + ENET_PROTOCOL_MAXIMUM_PACKET_COMMANDS = 32, + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE = 4096, + ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE = 65536, + ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT = 1, + ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT = 255, + ENET_PROTOCOL_MAXIMUM_PEER_ID = 0xFFF, + ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT = 1024 * 1024 +}; + +typedef enum _ENetProtocolCommand +{ + ENET_PROTOCOL_COMMAND_NONE = 0, + ENET_PROTOCOL_COMMAND_ACKNOWLEDGE = 1, + ENET_PROTOCOL_COMMAND_CONNECT = 2, + ENET_PROTOCOL_COMMAND_VERIFY_CONNECT = 3, + ENET_PROTOCOL_COMMAND_DISCONNECT = 4, + ENET_PROTOCOL_COMMAND_PING = 5, + ENET_PROTOCOL_COMMAND_SEND_RELIABLE = 6, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE = 7, + ENET_PROTOCOL_COMMAND_SEND_FRAGMENT = 8, + ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, + ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, + ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, + ENET_PROTOCOL_COMMAND_COUNT = 13, + + ENET_PROTOCOL_COMMAND_MASK = 0x0F +} ENetProtocolCommand; + +typedef enum _ENetProtocolFlag +{ + ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE = (1 << 7), + ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED = (1 << 6), + + ENET_PROTOCOL_HEADER_FLAG_COMPRESSED = (1 << 14), + ENET_PROTOCOL_HEADER_FLAG_SENT_TIME = (1 << 15), + ENET_PROTOCOL_HEADER_FLAG_MASK = ENET_PROTOCOL_HEADER_FLAG_COMPRESSED | ENET_PROTOCOL_HEADER_FLAG_SENT_TIME, + + ENET_PROTOCOL_HEADER_SESSION_MASK = (3 << 12), + ENET_PROTOCOL_HEADER_SESSION_SHIFT = 12 +} ENetProtocolFlag; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#define ENET_PACKED +#elif defined(__GNUC__) || defined(__clang__) +#define ENET_PACKED __attribute__ ((packed)) +#else +#define ENET_PACKED +#endif + +typedef struct _ENetProtocolHeader +{ + enet_uint16 peerID; + enet_uint16 sentTime; +} ENET_PACKED ENetProtocolHeader; + +typedef struct _ENetProtocolCommandHeader +{ + enet_uint8 command; + enet_uint8 channelID; + enet_uint16 reliableSequenceNumber; +} ENET_PACKED ENetProtocolCommandHeader; + +typedef struct _ENetProtocolAcknowledge +{ + ENetProtocolCommandHeader header; + enet_uint16 receivedReliableSequenceNumber; + enet_uint16 receivedSentTime; +} ENET_PACKED ENetProtocolAcknowledge; + +typedef struct _ENetProtocolConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; + enet_uint32 data; +} ENET_PACKED ENetProtocolConnect; + +typedef struct _ENetProtocolVerifyConnect +{ + ENetProtocolCommandHeader header; + enet_uint16 outgoingPeerID; + enet_uint8 incomingSessionID; + enet_uint8 outgoingSessionID; + enet_uint32 mtu; + enet_uint32 windowSize; + enet_uint32 channelCount; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; + enet_uint32 connectID; +} ENET_PACKED ENetProtocolVerifyConnect; + +typedef struct _ENetProtocolBandwidthLimit +{ + ENetProtocolCommandHeader header; + enet_uint32 incomingBandwidth; + enet_uint32 outgoingBandwidth; +} ENET_PACKED ENetProtocolBandwidthLimit; + +typedef struct _ENetProtocolThrottleConfigure +{ + ENetProtocolCommandHeader header; + enet_uint32 packetThrottleInterval; + enet_uint32 packetThrottleAcceleration; + enet_uint32 packetThrottleDeceleration; +} ENET_PACKED ENetProtocolThrottleConfigure; + +typedef struct _ENetProtocolDisconnect +{ + ENetProtocolCommandHeader header; + enet_uint32 data; +} ENET_PACKED ENetProtocolDisconnect; + +typedef struct _ENetProtocolPing +{ + ENetProtocolCommandHeader header; +} ENET_PACKED ENetProtocolPing; + +typedef struct _ENetProtocolSendReliable +{ + ENetProtocolCommandHeader header; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendReliable; + +typedef struct _ENetProtocolSendUnreliable +{ + ENetProtocolCommandHeader header; + enet_uint16 unreliableSequenceNumber; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnreliable; + +typedef struct _ENetProtocolSendUnsequenced +{ + ENetProtocolCommandHeader header; + enet_uint16 unsequencedGroup; + enet_uint16 dataLength; +} ENET_PACKED ENetProtocolSendUnsequenced; + +typedef struct _ENetProtocolSendFragment +{ + ENetProtocolCommandHeader header; + enet_uint16 startSequenceNumber; + enet_uint16 dataLength; + enet_uint32 fragmentCount; + enet_uint32 fragmentNumber; + enet_uint32 totalLength; + enet_uint32 fragmentOffset; +} ENET_PACKED ENetProtocolSendFragment; + +typedef union _ENetProtocol +{ + ENetProtocolCommandHeader header; + ENetProtocolAcknowledge acknowledge; + ENetProtocolConnect connect; + ENetProtocolVerifyConnect verifyConnect; + ENetProtocolDisconnect disconnect; + ENetProtocolPing ping; + ENetProtocolSendReliable sendReliable; + ENetProtocolSendUnreliable sendUnreliable; + ENetProtocolSendUnsequenced sendUnsequenced; + ENetProtocolSendFragment sendFragment; + ENetProtocolBandwidthLimit bandwidthLimit; + ENetProtocolThrottleConfigure throttleConfigure; +} ENET_PACKED ENetProtocol; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +#endif /* __ENET_PROTOCOL_H__ */ + diff --git a/modules/enet/enet/time.h b/modules/enet/enet/time.h new file mode 100644 index 0000000000..c82a546035 --- /dev/null +++ b/modules/enet/enet/time.h @@ -0,0 +1,18 @@ +/** + @file time.h + @brief ENet time constants and macros +*/ +#ifndef __ENET_TIME_H__ +#define __ENET_TIME_H__ + +#define ENET_TIME_OVERFLOW 86400000 + +#define ENET_TIME_LESS(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_GREATER(a, b) ((b) - (a) >= ENET_TIME_OVERFLOW) +#define ENET_TIME_LESS_EQUAL(a, b) (! ENET_TIME_GREATER (a, b)) +#define ENET_TIME_GREATER_EQUAL(a, b) (! ENET_TIME_LESS (a, b)) + +#define ENET_TIME_DIFFERENCE(a, b) ((a) - (b) >= ENET_TIME_OVERFLOW ? (b) - (a) : (a) - (b)) + +#endif /* __ENET_TIME_H__ */ + diff --git a/modules/enet/enet/types.h b/modules/enet/enet/types.h new file mode 100644 index 0000000000..ab010a4b13 --- /dev/null +++ b/modules/enet/enet/types.h @@ -0,0 +1,13 @@ +/** + @file types.h + @brief type definitions for ENet +*/ +#ifndef __ENET_TYPES_H__ +#define __ENET_TYPES_H__ + +typedef unsigned char enet_uint8; /**< unsigned 8-bit type */ +typedef unsigned short enet_uint16; /**< unsigned 16-bit type */ +typedef unsigned int enet_uint32; /**< unsigned 32-bit type */ + +#endif /* __ENET_TYPES_H__ */ + diff --git a/modules/enet/enet/unix.h b/modules/enet/enet/unix.h new file mode 100644 index 0000000000..a59e340606 --- /dev/null +++ b/modules/enet/enet/unix.h @@ -0,0 +1,47 @@ +/** + @file unix.h + @brief ENet Unix header +*/ +#ifndef __ENET_UNIX_H__ +#define __ENET_UNIX_H__ + +#include <stdlib.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> + +#ifdef MSG_MAXIOVLEN +#define ENET_BUFFER_MAXIMUM MSG_MAXIOVLEN +#endif + +typedef int ENetSocket; + +#define ENET_SOCKET_NULL -1 + +#define ENET_HOST_TO_NET_16(value) (htons (value)) /**< macro that converts host to net byte-order of a 16-bit value */ +#define ENET_HOST_TO_NET_32(value) (htonl (value)) /**< macro that converts host to net byte-order of a 32-bit value */ + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) /**< macro that converts net to host byte-order of a 16-bit value */ +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) /**< macro that converts net to host byte-order of a 32-bit value */ + +typedef struct +{ + void * data; + size_t dataLength; +} ENetBuffer; + +#define ENET_CALLBACK + +#define ENET_API extern + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_UNIX_H__ */ + diff --git a/modules/enet/enet/utility.h b/modules/enet/enet/utility.h new file mode 100644 index 0000000000..e48a476be3 --- /dev/null +++ b/modules/enet/enet/utility.h @@ -0,0 +1,12 @@ +/** + @file utility.h + @brief ENet utility header +*/ +#ifndef __ENET_UTILITY_H__ +#define __ENET_UTILITY_H__ + +#define ENET_MAX(x, y) ((x) > (y) ? (x) : (y)) +#define ENET_MIN(x, y) ((x) < (y) ? (x) : (y)) + +#endif /* __ENET_UTILITY_H__ */ + diff --git a/modules/enet/enet/win32.h b/modules/enet/enet/win32.h new file mode 100644 index 0000000000..e73ca9d052 --- /dev/null +++ b/modules/enet/enet/win32.h @@ -0,0 +1,57 @@ +/** + @file win32.h + @brief ENet Win32 header +*/ +#ifndef __ENET_WIN32_H__ +#define __ENET_WIN32_H__ + +#ifdef _MSC_VER +#ifdef ENET_BUILDING_LIB +#pragma warning (disable: 4267) // size_t to int conversion +#pragma warning (disable: 4244) // 64bit to 32bit int +#pragma warning (disable: 4018) // signed/unsigned mismatch +#pragma warning (disable: 4146) // unary minus operator applied to unsigned type +#endif +#endif + +#include <stdlib.h> +#include <winsock2.h> + +typedef SOCKET ENetSocket; + +#define ENET_SOCKET_NULL INVALID_SOCKET + +#define ENET_HOST_TO_NET_16(value) (htons (value)) +#define ENET_HOST_TO_NET_32(value) (htonl (value)) + +#define ENET_NET_TO_HOST_16(value) (ntohs (value)) +#define ENET_NET_TO_HOST_32(value) (ntohl (value)) + +typedef struct +{ + size_t dataLength; + void * data; +} ENetBuffer; + +#define ENET_CALLBACK __cdecl + +#ifdef ENET_DLL +#ifdef ENET_BUILDING_LIB +#define ENET_API __declspec( dllexport ) +#else +#define ENET_API __declspec( dllimport ) +#endif /* ENET_BUILDING_LIB */ +#else /* !ENET_DLL */ +#define ENET_API extern +#endif /* ENET_DLL */ + +typedef fd_set ENetSocketSet; + +#define ENET_SOCKETSET_EMPTY(sockset) FD_ZERO (& (sockset)) +#define ENET_SOCKETSET_ADD(sockset, socket) FD_SET (socket, & (sockset)) +#define ENET_SOCKETSET_REMOVE(sockset, socket) FD_CLR (socket, & (sockset)) +#define ENET_SOCKETSET_CHECK(sockset, socket) FD_ISSET (socket, & (sockset)) + +#endif /* __ENET_WIN32_H__ */ + + diff --git a/modules/enet/host.c b/modules/enet/host.c new file mode 100644 index 0000000000..3be6c0922c --- /dev/null +++ b/modules/enet/host.c @@ -0,0 +1,492 @@ +/** + @file host.c + @brief ENet host management functions +*/ +#define ENET_BUILDING_LIB 1 +#include <string.h> +#include "enet/enet.h" + +/** @defgroup host ENet host functions + @{ +*/ + +/** Creates a host for communicating to peers. + + @param address the address at which other peers may connect to this host. If NULL, then no peers may connect to the host. + @param peerCount the maximum number of peers that should be allocated for the host. + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT + @param incomingBandwidth downstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + @param outgoingBandwidth upstream bandwidth of the host in bytes/second; if 0, ENet will assume unlimited bandwidth. + + @returns the host on success and NULL on failure + + @remarks ENet will strategically drop packets on specific sides of a connection between hosts + to ensure the host's bandwidth is not overwhelmed. The bandwidth parameters also determine + the window size of a connection which limits the amount of reliable packets that may be in transit + at any given time. +*/ +ENetHost * +enet_host_create (const ENetAddress * address, size_t peerCount, size_t channelLimit, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + ENetHost * host; + ENetPeer * currentPeer; + + if (peerCount > ENET_PROTOCOL_MAXIMUM_PEER_ID) + return NULL; + + host = (ENetHost *) enet_malloc (sizeof (ENetHost)); + if (host == NULL) + return NULL; + memset (host, 0, sizeof (ENetHost)); + + host -> peers = (ENetPeer *) enet_malloc (peerCount * sizeof (ENetPeer)); + if (host -> peers == NULL) + { + enet_free (host); + + return NULL; + } + memset (host -> peers, 0, peerCount * sizeof (ENetPeer)); + + host -> socket = enet_socket_create (ENET_SOCKET_TYPE_DATAGRAM); + if (host -> socket == ENET_SOCKET_NULL || (address != NULL && enet_socket_bind (host -> socket, address) < 0)) + { + if (host -> socket != ENET_SOCKET_NULL) + enet_socket_destroy (host -> socket); + + enet_free (host -> peers); + enet_free (host); + + return NULL; + } + + enet_socket_set_option (host -> socket, ENET_SOCKOPT_NONBLOCK, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_BROADCAST, 1); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_RCVBUF, ENET_HOST_RECEIVE_BUFFER_SIZE); + enet_socket_set_option (host -> socket, ENET_SOCKOPT_SNDBUF, ENET_HOST_SEND_BUFFER_SIZE); + + if (address != NULL && enet_socket_get_address (host -> socket, & host -> address) < 0) + host -> address = * address; + + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> randomSeed = (enet_uint32) (size_t) host; + host -> randomSeed += enet_host_random_seed (); + host -> randomSeed = (host -> randomSeed << 16) | (host -> randomSeed >> 16); + host -> channelLimit = channelLimit; + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> bandwidthThrottleEpoch = 0; + host -> recalculateBandwidthLimits = 0; + host -> mtu = ENET_HOST_DEFAULT_MTU; + host -> peerCount = peerCount; + host -> commandCount = 0; + host -> bufferCount = 0; + host -> checksum = NULL; + host -> receivedAddress.host = ENET_HOST_ANY; + host -> receivedAddress.port = 0; + host -> receivedData = NULL; + host -> receivedDataLength = 0; + + host -> totalSentData = 0; + host -> totalSentPackets = 0; + host -> totalReceivedData = 0; + host -> totalReceivedPackets = 0; + + host -> connectedPeers = 0; + host -> bandwidthLimitedPeers = 0; + host -> duplicatePeers = ENET_PROTOCOL_MAXIMUM_PEER_ID; + host -> maximumPacketSize = ENET_HOST_DEFAULT_MAXIMUM_PACKET_SIZE; + host -> maximumWaitingData = ENET_HOST_DEFAULT_MAXIMUM_WAITING_DATA; + + host -> compressor.context = NULL; + host -> compressor.compress = NULL; + host -> compressor.decompress = NULL; + host -> compressor.destroy = NULL; + + host -> intercept = NULL; + + enet_list_clear (& host -> dispatchQueue); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + currentPeer -> host = host; + currentPeer -> incomingPeerID = currentPeer - host -> peers; + currentPeer -> outgoingSessionID = currentPeer -> incomingSessionID = 0xFF; + currentPeer -> data = NULL; + + enet_list_clear (& currentPeer -> acknowledgements); + enet_list_clear (& currentPeer -> sentReliableCommands); + enet_list_clear (& currentPeer -> sentUnreliableCommands); + enet_list_clear (& currentPeer -> outgoingReliableCommands); + enet_list_clear (& currentPeer -> outgoingUnreliableCommands); + enet_list_clear (& currentPeer -> dispatchedCommands); + + enet_peer_reset (currentPeer); + } + + return host; +} + +/** Destroys the host and all resources associated with it. + @param host pointer to the host to destroy +*/ +void +enet_host_destroy (ENetHost * host) +{ + ENetPeer * currentPeer; + + if (host == NULL) + return; + + enet_socket_destroy (host -> socket); + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + enet_peer_reset (currentPeer); + } + + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + enet_free (host -> peers); + enet_free (host); +} + +/** Initiates a connection to a foreign host. + @param host host seeking the connection + @param address destination for the connection + @param channelCount number of channels to allocate + @param data user data supplied to the receiving host + @returns a peer representing the foreign host on success, NULL on failure + @remarks The peer returned will have not completed the connection until enet_host_service() + notifies of an ENET_EVENT_TYPE_CONNECT event for the peer. +*/ +ENetPeer * +enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelCount, enet_uint32 data) +{ + ENetPeer * currentPeer; + ENetChannel * channel; + ENetProtocol command; + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + else + if (channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelCount = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + break; + } + + if (currentPeer >= & host -> peers [host -> peerCount]) + return NULL; + + currentPeer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (currentPeer -> channels == NULL) + return NULL; + currentPeer -> channelCount = channelCount; + currentPeer -> state = ENET_PEER_STATE_CONNECTING; + currentPeer -> address = * address; + currentPeer -> connectID = ++ host -> randomSeed; + + if (host -> outgoingBandwidth == 0) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + currentPeer -> windowSize = (host -> outgoingBandwidth / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (currentPeer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (currentPeer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + currentPeer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + for (channel = currentPeer -> channels; + channel < & currentPeer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + command.header.command = ENET_PROTOCOL_COMMAND_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.connect.outgoingPeerID = ENET_HOST_TO_NET_16 (currentPeer -> incomingPeerID); + command.connect.incomingSessionID = currentPeer -> incomingSessionID; + command.connect.outgoingSessionID = currentPeer -> outgoingSessionID; + command.connect.mtu = ENET_HOST_TO_NET_32 (currentPeer -> mtu); + command.connect.windowSize = ENET_HOST_TO_NET_32 (currentPeer -> windowSize); + command.connect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + command.connect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + command.connect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + command.connect.packetThrottleInterval = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleInterval); + command.connect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleAcceleration); + command.connect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (currentPeer -> packetThrottleDeceleration); + command.connect.connectID = currentPeer -> connectID; + command.connect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (currentPeer, & command, NULL, 0, 0); + + return currentPeer; +} + +/** Queues a packet to be sent to all peers associated with the host. + @param host host on which to broadcast the packet + @param channelID channel on which to broadcast + @param packet packet to broadcast +*/ +void +enet_host_broadcast (ENetHost * host, enet_uint8 channelID, ENetPacket * packet) +{ + ENetPeer * currentPeer; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state != ENET_PEER_STATE_CONNECTED) + continue; + + enet_peer_send (currentPeer, channelID, packet); + } + + if (packet -> referenceCount == 0) + enet_packet_destroy (packet); +} + +/** Sets the packet compressor the host should use to compress and decompress packets. + @param host host to enable or disable compression for + @param compressor callbacks for for the packet compressor; if NULL, then compression is disabled +*/ +void +enet_host_compress (ENetHost * host, const ENetCompressor * compressor) +{ + if (host -> compressor.context != NULL && host -> compressor.destroy) + (* host -> compressor.destroy) (host -> compressor.context); + + if (compressor) + host -> compressor = * compressor; + else + host -> compressor.context = NULL; +} + +/** Limits the maximum allowed channels of future incoming connections. + @param host host to limit + @param channelLimit the maximum number of channels allowed; if 0, then this is equivalent to ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT +*/ +void +enet_host_channel_limit (ENetHost * host, size_t channelLimit) +{ + if (! channelLimit || channelLimit > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT; + else + if (channelLimit < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT) + channelLimit = ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT; + + host -> channelLimit = channelLimit; +} + + +/** Adjusts the bandwidth limits of a host. + @param host host to adjust + @param incomingBandwidth new incoming bandwidth + @param outgoingBandwidth new outgoing bandwidth + @remarks the incoming and outgoing bandwidth parameters are identical in function to those + specified in enet_host_create(). +*/ +void +enet_host_bandwidth_limit (ENetHost * host, enet_uint32 incomingBandwidth, enet_uint32 outgoingBandwidth) +{ + host -> incomingBandwidth = incomingBandwidth; + host -> outgoingBandwidth = outgoingBandwidth; + host -> recalculateBandwidthLimits = 1; +} + +void +enet_host_bandwidth_throttle (ENetHost * host) +{ + enet_uint32 timeCurrent = enet_time_get (), + elapsedTime = timeCurrent - host -> bandwidthThrottleEpoch, + peersRemaining = (enet_uint32) host -> connectedPeers, + dataTotal = ~0, + bandwidth = ~0, + throttle = 0, + bandwidthLimit = 0; + int needsAdjustment = host -> bandwidthLimitedPeers > 0 ? 1 : 0; + ENetPeer * peer; + ENetProtocol command; + + if (elapsedTime < ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + return; + + host -> bandwidthThrottleEpoch = timeCurrent; + + if (peersRemaining == 0) + return; + + if (host -> outgoingBandwidth != 0) + { + dataTotal = 0; + bandwidth = (host -> outgoingBandwidth * elapsedTime) / 1000; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + dataTotal += peer -> outgoingDataTotal; + } + } + + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + + if (dataTotal <= bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + enet_uint32 peerBandwidth; + + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidth == 0 || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peerBandwidth = (peer -> incomingBandwidth * elapsedTime) / 1000; + if ((throttle * peer -> outgoingDataTotal) / ENET_PEER_PACKET_THROTTLE_SCALE <= peerBandwidth) + continue; + + peer -> packetThrottleLimit = (peerBandwidth * + ENET_PEER_PACKET_THROTTLE_SCALE) / peer -> outgoingDataTotal; + + if (peer -> packetThrottleLimit == 0) + peer -> packetThrottleLimit = 1; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> outgoingBandwidthThrottleEpoch = timeCurrent; + + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peerBandwidth; + dataTotal -= peerBandwidth; + } + } + + if (peersRemaining > 0) + { + if (dataTotal <= bandwidth) + throttle = ENET_PEER_PACKET_THROTTLE_SCALE; + else + throttle = (bandwidth * ENET_PEER_PACKET_THROTTLE_SCALE) / dataTotal; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> outgoingBandwidthThrottleEpoch == timeCurrent) + continue; + + peer -> packetThrottleLimit = throttle; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + } + } + + if (host -> recalculateBandwidthLimits) + { + host -> recalculateBandwidthLimits = 0; + + peersRemaining = (enet_uint32) host -> connectedPeers; + bandwidth = host -> incomingBandwidth; + needsAdjustment = 1; + + if (bandwidth == 0) + bandwidthLimit = 0; + else + while (peersRemaining > 0 && needsAdjustment != 0) + { + needsAdjustment = 0; + bandwidthLimit = bandwidth / peersRemaining; + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if ((peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) || + peer -> incomingBandwidthThrottleEpoch == timeCurrent) + continue; + + if (peer -> outgoingBandwidth > 0 && + peer -> outgoingBandwidth >= bandwidthLimit) + continue; + + peer -> incomingBandwidthThrottleEpoch = timeCurrent; + + needsAdjustment = 1; + -- peersRemaining; + bandwidth -= peer -> outgoingBandwidth; + } + } + + for (peer = host -> peers; + peer < & host -> peers [host -> peerCount]; + ++ peer) + { + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + continue; + + command.header.command = ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + command.bandwidthLimit.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + + if (peer -> incomingBandwidthThrottleEpoch == timeCurrent) + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (peer -> outgoingBandwidth); + else + command.bandwidthLimit.incomingBandwidth = ENET_HOST_TO_NET_32 (bandwidthLimit); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + } + } +} + +/** @} */ diff --git a/modules/enet/list.c b/modules/enet/list.c new file mode 100644 index 0000000000..1c1a8dfaaf --- /dev/null +++ b/modules/enet/list.c @@ -0,0 +1,75 @@ +/** + @file list.c + @brief ENet linked list functions +*/ +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** + @defgroup list ENet linked list utility functions + @ingroup private + @{ +*/ +void +enet_list_clear (ENetList * list) +{ + list -> sentinel.next = & list -> sentinel; + list -> sentinel.previous = & list -> sentinel; +} + +ENetListIterator +enet_list_insert (ENetListIterator position, void * data) +{ + ENetListIterator result = (ENetListIterator) data; + + result -> previous = position -> previous; + result -> next = position; + + result -> previous -> next = result; + position -> previous = result; + + return result; +} + +void * +enet_list_remove (ENetListIterator position) +{ + position -> previous -> next = position -> next; + position -> next -> previous = position -> previous; + + return position; +} + +ENetListIterator +enet_list_move (ENetListIterator position, void * dataFirst, void * dataLast) +{ + ENetListIterator first = (ENetListIterator) dataFirst, + last = (ENetListIterator) dataLast; + + first -> previous -> next = last -> next; + last -> next -> previous = first -> previous; + + first -> previous = position -> previous; + last -> next = position; + + first -> previous -> next = first; + position -> previous = last; + + return first; +} + +size_t +enet_list_size (ENetList * list) +{ + size_t size = 0; + ENetListIterator position; + + for (position = enet_list_begin (list); + position != enet_list_end (list); + position = enet_list_next (position)) + ++ size; + + return size; +} + +/** @} */ diff --git a/modules/enet/networked_multiplayer_enet.cpp b/modules/enet/networked_multiplayer_enet.cpp new file mode 100644 index 0000000000..dd83a1ac5d --- /dev/null +++ b/modules/enet/networked_multiplayer_enet.cpp @@ -0,0 +1,523 @@ +#include "os/os.h" +#include "io/marshalls.h" +#include "networked_multiplayer_enet.h" + +void NetworkedMultiplayerENet::set_transfer_mode(TransferMode p_mode) { + + transfer_mode=p_mode; +} + +void NetworkedMultiplayerENet::set_target_peer(int p_peer){ + + target_peer=p_peer; +} + +int NetworkedMultiplayerENet::get_packet_peer() const{ + + ERR_FAIL_COND_V(!active,1); + ERR_FAIL_COND_V(incoming_packets.size()==0,1); + + return incoming_packets.front()->get().from; + +} + +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; + + 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 */, + 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 */); + + ERR_FAIL_COND_V(!host,ERR_CANT_CREATE); + + 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_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 */, + 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 */); + + ERR_FAIL_COND_V(!host,ERR_CANT_CREATE); + + + ENetAddress address; + 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, 2, unique_id); + + if (peer == NULL) { + enet_host_destroy(host); + ERR_FAIL_COND_V(!peer,ERR_CANT_CREATE); + } + + //technically safe to ignore the peer or anything else. + + connection_status=CONNECTION_CONNECTING; + active=true; + server=false; + refuse_connections=false; + + return OK; +} + +void NetworkedMultiplayerENet::poll(){ + + ERR_FAIL_COND(!active); + + _pop_current_packet(); + + ENetEvent event; + /* Wait up to 1000 milliseconds for an event. */ + 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; + + 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; + + connection_status=CONNECTION_CONNECTED; //if connecting, this means it connected t something! + + 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. */ + + 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 ); + + } + + + } break; + case ENET_EVENT_TYPE_RECEIVE: { + + + 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(target)); + enet_peer_send(peer_map[target],0,packet.packet); + } + } else { + + incoming_packets.push_back(packet); + } + + + //destroy packet later.. + } else { + ERR_CONTINUE(true); + } + + + }break; + case ENET_EVENT_TYPE_NONE: { + //do nothing + } break; + } + } +} + +bool NetworkedMultiplayerENet::is_server() const { + ERR_FAIL_COND_V(!active,false); + + return server; +} + +void NetworkedMultiplayerENet::close_connection() { + + 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; +} + +int NetworkedMultiplayerENet::get_available_packet_count() const { + + return incoming_packets.size(); +} +Error NetworkedMultiplayerENet::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{ + + ERR_FAIL_COND_V(incoming_packets.size()==0,ERR_UNAVAILABLE); + + _pop_current_packet(); + + current_packet = incoming_packets.front()->get(); + incoming_packets.pop_front(); + + *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(!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; + } break; + case TRANSFER_MODE_UNRELIABLE_ORDERED: { + packet_flags=0; + } break; + case TRANSFER_MODE_RELIABLE: { + packet_flags=ENET_PACKET_FLAG_RELIABLE; + } break; + } + + 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; + + 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 { + + 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); + + return OK; +} + +int NetworkedMultiplayerENet::get_max_packet_size() const { + + return 1<<24; //anything is good +} + +void NetworkedMultiplayerENet::_pop_current_packet() const { + + if (current_packet.packet) { + enet_packet_destroy(current_packet.packet); + current_packet.packet=NULL; + current_packet.from=0; + } + +} + +NetworkedMultiplayerPeer::ConnectionStatus NetworkedMultiplayerENet::get_connection_status() const { + + 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","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); + + +} + + +NetworkedMultiplayerENet::NetworkedMultiplayerENet(){ + + active=false; + server=false; + refuse_connections=false; + unique_id=0; + target_peer=0; + current_packet.packet=NULL; + transfer_mode=TRANSFER_MODE_RELIABLE; + connection_status=CONNECTION_DISCONNECTED; +} + +NetworkedMultiplayerENet::~NetworkedMultiplayerENet(){ + + close_connection(); +} diff --git a/modules/enet/networked_multiplayer_enet.h b/modules/enet/networked_multiplayer_enet.h new file mode 100644 index 0000000000..235686d143 --- /dev/null +++ b/modules/enet/networked_multiplayer_enet.h @@ -0,0 +1,85 @@ +#ifndef NETWORKED_MULTIPLAYER_ENET_H +#define NETWORKED_MULTIPLAYER_ENET_H + +#include "io/networked_multiplayer_peer.h" +#include "enet/enet.h" + +class NetworkedMultiplayerENet : public NetworkedMultiplayerPeer { + + OBJ_TYPE(NetworkedMultiplayerENet,NetworkedMultiplayerPeer) + + enum { + SYSMSG_ADD_PEER, + SYSMSG_REMOVE_PEER + }; + + bool active; + bool server; + + uint32_t unique_id; + + int target_peer; + TransferMode transfer_mode; + + ENetEvent event; + ENetPeer *peer; + ENetHost *host; + + bool refuse_connections; + + ConnectionStatus connection_status; + + Map<int,ENetPeer*> peer_map; + + struct Packet { + + ENetPacket *packet; + int from; + }; + + mutable List<Packet> incoming_packets; + + mutable Packet current_packet; + + uint32_t _gen_unique_id() const; + void _pop_current_packet() const; + +protected: + static void _bind_methods(); +public: + + virtual void set_transfer_mode(TransferMode p_mode); + virtual void set_target_peer(int p_peer); + + + virtual int get_packet_peer() const; + + + 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(); + + virtual void poll(); + + virtual bool is_server() const; + + virtual int get_available_packet_count() const; + virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const; ///< buffer is GONE after next get_packet + virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size); + + virtual int get_max_packet_size() const; + + 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(); +}; + + +#endif // NETWORKED_MULTIPLAYER_ENET_H diff --git a/modules/enet/packet.c b/modules/enet/packet.c new file mode 100644 index 0000000000..5fa78b28ae --- /dev/null +++ b/modules/enet/packet.c @@ -0,0 +1,165 @@ +/** + @file packet.c + @brief ENet packet management functions +*/ +#include <string.h> +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup Packet ENet packet functions + @{ +*/ + +/** Creates a packet that may be sent to a peer. + @param data initial contents of the packet's data; the packet's data will remain uninitialized if data is NULL. + @param dataLength size of the data allocated for this packet + @param flags flags for this packet as described for the ENetPacket structure. + @returns the packet on success, NULL on failure +*/ +ENetPacket * +enet_packet_create (const void * data, size_t dataLength, enet_uint32 flags) +{ + ENetPacket * packet = (ENetPacket *) enet_malloc (sizeof (ENetPacket)); + if (packet == NULL) + return NULL; + + if (flags & ENET_PACKET_FLAG_NO_ALLOCATE) + packet -> data = (enet_uint8 *) data; + else + if (dataLength <= 0) + packet -> data = NULL; + else + { + packet -> data = (enet_uint8 *) enet_malloc (dataLength); + if (packet -> data == NULL) + { + enet_free (packet); + return NULL; + } + + if (data != NULL) + memcpy (packet -> data, data, dataLength); + } + + packet -> referenceCount = 0; + packet -> flags = flags; + packet -> dataLength = dataLength; + packet -> freeCallback = NULL; + packet -> userData = NULL; + + return packet; +} + +/** Destroys the packet and deallocates its data. + @param packet packet to be destroyed +*/ +void +enet_packet_destroy (ENetPacket * packet) +{ + if (packet == NULL) + return; + + if (packet -> freeCallback != NULL) + (* packet -> freeCallback) (packet); + if (! (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE) && + packet -> data != NULL) + enet_free (packet -> data); + enet_free (packet); +} + +/** Attempts to resize the data in the packet to length specified in the + dataLength parameter + @param packet packet to resize + @param dataLength new size for the packet data + @returns 0 on success, < 0 on failure +*/ +int +enet_packet_resize (ENetPacket * packet, size_t dataLength) +{ + enet_uint8 * newData; + + if (dataLength <= packet -> dataLength || (packet -> flags & ENET_PACKET_FLAG_NO_ALLOCATE)) + { + packet -> dataLength = dataLength; + + return 0; + } + + newData = (enet_uint8 *) enet_malloc (dataLength); + if (newData == NULL) + return -1; + + memcpy (newData, packet -> data, packet -> dataLength); + enet_free (packet -> data); + + packet -> data = newData; + packet -> dataLength = dataLength; + + return 0; +} + +static int initializedCRC32 = 0; +static enet_uint32 crcTable [256]; + +static enet_uint32 +reflect_crc (int val, int bits) +{ + int result = 0, bit; + + for (bit = 0; bit < bits; bit ++) + { + if(val & 1) result |= 1 << (bits - 1 - bit); + val >>= 1; + } + + return result; +} + +static void +initialize_crc32 (void) +{ + int byte; + + for (byte = 0; byte < 256; ++ byte) + { + enet_uint32 crc = reflect_crc (byte, 8) << 24; + int offset; + + for(offset = 0; offset < 8; ++ offset) + { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04c11db7; + else + crc <<= 1; + } + + crcTable [byte] = reflect_crc (crc, 32); + } + + initializedCRC32 = 1; +} + +enet_uint32 +enet_crc32 (const ENetBuffer * buffers, size_t bufferCount) +{ + enet_uint32 crc = 0xFFFFFFFF; + + if (! initializedCRC32) initialize_crc32 (); + + while (bufferCount -- > 0) + { + const enet_uint8 * data = (const enet_uint8 *) buffers -> data, + * dataEnd = & data [buffers -> dataLength]; + + while (data < dataEnd) + { + crc = (crc >> 8) ^ crcTable [(crc & 0xFF) ^ *data++]; + } + + ++ buffers; + } + + return ENET_HOST_TO_NET_32 (~ crc); +} + +/** @} */ diff --git a/modules/enet/peer.c b/modules/enet/peer.c new file mode 100644 index 0000000000..e2d0872bd3 --- /dev/null +++ b/modules/enet/peer.c @@ -0,0 +1,1004 @@ +/** + @file peer.c + @brief ENet peer management functions +*/ +#include <string.h> +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +/** @defgroup peer ENet peer functions + @{ +*/ + +/** Configures throttle parameter for a peer. + + Unreliable packets are dropped by ENet in response to the varying conditions + of the Internet connection to the peer. The throttle represents a probability + that an unreliable packet should not be dropped and thus sent by ENet to the peer. + The lowest mean round trip time from the sending of a reliable packet to the + receipt of its acknowledgement is measured over an amount of time specified by + the interval parameter in milliseconds. If a measured round trip time happens to + be significantly less than the mean round trip time measured over the interval, + then the throttle probability is increased to allow more traffic by an amount + specified in the acceleration parameter, which is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE + constant. If a measured round trip time happens to be significantly greater than + the mean round trip time measured over the interval, then the throttle probability + is decreased to limit traffic by an amount specified in the deceleration parameter, which + is a ratio to the ENET_PEER_PACKET_THROTTLE_SCALE constant. When the throttle has + a value of ENET_PEER_PACKET_THROTTLE_SCALE, no unreliable packets are dropped by + ENet, and so 100% of all unreliable packets will be sent. When the throttle has a + value of 0, all unreliable packets are dropped by ENet, and so 0% of all unreliable + packets will be sent. Intermediate values for the throttle represent intermediate + probabilities between 0% and 100% of unreliable packets being sent. The bandwidth + limits of the local and foreign hosts are taken into account to determine a + sensible limit for the throttle probability above which it should not raise even in + the best of conditions. + + @param peer peer to configure + @param interval interval, in milliseconds, over which to measure lowest mean RTT; the default value is ENET_PEER_PACKET_THROTTLE_INTERVAL. + @param acceleration rate at which to increase the throttle probability as mean RTT declines + @param deceleration rate at which to decrease the throttle probability as mean RTT increases +*/ +void +enet_peer_throttle_configure (ENetPeer * peer, enet_uint32 interval, enet_uint32 acceleration, enet_uint32 deceleration) +{ + ENetProtocol command; + + peer -> packetThrottleInterval = interval; + peer -> packetThrottleAcceleration = acceleration; + peer -> packetThrottleDeceleration = deceleration; + + command.header.command = ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + command.throttleConfigure.packetThrottleInterval = ENET_HOST_TO_NET_32 (interval); + command.throttleConfigure.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (acceleration); + command.throttleConfigure.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (deceleration); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +int +enet_peer_throttle (ENetPeer * peer, enet_uint32 rtt) +{ + if (peer -> lastRoundTripTime <= peer -> lastRoundTripTimeVariance) + { + peer -> packetThrottle = peer -> packetThrottleLimit; + } + else + if (rtt < peer -> lastRoundTripTime) + { + peer -> packetThrottle += peer -> packetThrottleAcceleration; + + if (peer -> packetThrottle > peer -> packetThrottleLimit) + peer -> packetThrottle = peer -> packetThrottleLimit; + + return 1; + } + else + if (rtt > peer -> lastRoundTripTime + 2 * peer -> lastRoundTripTimeVariance) + { + if (peer -> packetThrottle > peer -> packetThrottleDeceleration) + peer -> packetThrottle -= peer -> packetThrottleDeceleration; + else + peer -> packetThrottle = 0; + + return -1; + } + + return 0; +} + +/** Queues a packet to be sent. + @param peer destination for the packet + @param channelID channel on which to send + @param packet packet to send + @retval 0 on success + @retval < 0 on failure +*/ +int +enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) +{ + ENetChannel * channel = & peer -> channels [channelID]; + ENetProtocol command; + size_t fragmentLength; + + if (peer -> state != ENET_PEER_STATE_CONNECTED || + channelID >= peer -> channelCount || + packet -> dataLength > peer -> host -> maximumPacketSize) + return -1; + + fragmentLength = peer -> mtu - sizeof (ENetProtocolHeader) - sizeof (ENetProtocolSendFragment); + if (peer -> host -> checksum != NULL) + fragmentLength -= sizeof(enet_uint32); + + if (packet -> dataLength > fragmentLength) + { + enet_uint32 fragmentCount = (packet -> dataLength + fragmentLength - 1) / fragmentLength, + fragmentNumber, + fragmentOffset; + enet_uint8 commandNumber; + enet_uint16 startSequenceNumber; + ENetList fragments; + ENetOutgoingCommand * fragment; + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + return -1; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && + channel -> outgoingUnreliableSequenceNumber < 0xFFFF) + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); + } + else + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); + } + + enet_list_clear (& fragments); + + for (fragmentNumber = 0, + fragmentOffset = 0; + fragmentOffset < packet -> dataLength; + ++ fragmentNumber, + fragmentOffset += fragmentLength) + { + if (packet -> dataLength - fragmentOffset < fragmentLength) + fragmentLength = packet -> dataLength - fragmentOffset; + + fragment = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (fragment == NULL) + { + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_free (fragment); + } + + return -1; + } + + fragment -> fragmentOffset = fragmentOffset; + fragment -> fragmentLength = fragmentLength; + fragment -> packet = packet; + fragment -> command.header.command = commandNumber; + fragment -> command.header.channelID = channelID; + fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; + fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); + fragment -> command.sendFragment.fragmentCount = ENET_HOST_TO_NET_32 (fragmentCount); + fragment -> command.sendFragment.fragmentNumber = ENET_HOST_TO_NET_32 (fragmentNumber); + fragment -> command.sendFragment.totalLength = ENET_HOST_TO_NET_32 (packet -> dataLength); + fragment -> command.sendFragment.fragmentOffset = ENET_NET_TO_HOST_32 (fragmentOffset); + + enet_list_insert (enet_list_end (& fragments), fragment); + } + + packet -> referenceCount += fragmentNumber; + + while (! enet_list_empty (& fragments)) + { + fragment = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (& fragments)); + + enet_peer_setup_outgoing_command (peer, fragment); + } + + return 0; + } + + command.header.channelID = channelID; + + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + else + { + command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; + command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); + } + + if (enet_peer_queue_outgoing_command (peer, & command, packet, 0, packet -> dataLength) == NULL) + return -1; + + return 0; +} + +/** Attempts to dequeue any incoming queued packet. + @param peer peer to dequeue packets from + @param channelID holds the channel ID of the channel the packet was received on success + @returns a pointer to the packet, or NULL if there are no available incoming queued packets +*/ +ENetPacket * +enet_peer_receive (ENetPeer * peer, enet_uint8 * channelID) +{ + ENetIncomingCommand * incomingCommand; + ENetPacket * packet; + + if (enet_list_empty (& peer -> dispatchedCommands)) + return NULL; + + incomingCommand = (ENetIncomingCommand *) enet_list_remove (enet_list_begin (& peer -> dispatchedCommands)); + + if (channelID != NULL) + * channelID = incomingCommand -> command.header.channelID; + + packet = incomingCommand -> packet; + + -- packet -> referenceCount; + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + + peer -> totalWaitingData -= packet -> dataLength; + + return packet; +} + +static void +enet_peer_reset_outgoing_commands (ENetList * queue) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (queue)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_remove (enet_list_begin (queue)); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + } + + enet_free (outgoingCommand); + } +} + +static void +enet_peer_remove_incoming_commands (ENetList * queue, ENetListIterator startCommand, ENetListIterator endCommand) +{ + ENetListIterator currentCommand; + + for (currentCommand = startCommand; currentCommand != endCommand; ) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + enet_list_remove (& incomingCommand -> incomingCommandList); + + if (incomingCommand -> packet != NULL) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + } + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); + } +} + +static void +enet_peer_reset_incoming_commands (ENetList * queue) +{ + enet_peer_remove_incoming_commands(queue, enet_list_begin (queue), enet_list_end (queue)); +} + +void +enet_peer_reset_queues (ENetPeer * peer) +{ + ENetChannel * channel; + + if (peer -> needsDispatch) + { + enet_list_remove (& peer -> dispatchList); + + peer -> needsDispatch = 0; + } + + while (! enet_list_empty (& peer -> acknowledgements)) + enet_free (enet_list_remove (enet_list_begin (& peer -> acknowledgements))); + + enet_peer_reset_outgoing_commands (& peer -> sentReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> sentUnreliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingReliableCommands); + enet_peer_reset_outgoing_commands (& peer -> outgoingUnreliableCommands); + enet_peer_reset_incoming_commands (& peer -> dispatchedCommands); + + if (peer -> channels != NULL && peer -> channelCount > 0) + { + for (channel = peer -> channels; + channel < & peer -> channels [peer -> channelCount]; + ++ channel) + { + enet_peer_reset_incoming_commands (& channel -> incomingReliableCommands); + enet_peer_reset_incoming_commands (& channel -> incomingUnreliableCommands); + } + + enet_free (peer -> channels); + } + + peer -> channels = NULL; + peer -> channelCount = 0; +} + +void +enet_peer_on_connect (ENetPeer * peer) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> incomingBandwidth != 0) + ++ peer -> host -> bandwidthLimitedPeers; + + ++ peer -> host -> connectedPeers; + } +} + +void +enet_peer_on_disconnect (ENetPeer * peer) +{ + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> incomingBandwidth != 0) + -- peer -> host -> bandwidthLimitedPeers; + + -- peer -> host -> connectedPeers; + } +} + +/** Forcefully disconnects a peer. + @param peer peer to forcefully disconnect + @remarks The foreign host represented by the peer is not notified of the disconnection and will timeout + on its connection to the local host. +*/ +void +enet_peer_reset (ENetPeer * peer) +{ + enet_peer_on_disconnect (peer); + + peer -> outgoingPeerID = ENET_PROTOCOL_MAXIMUM_PEER_ID; + peer -> connectID = 0; + + peer -> state = ENET_PEER_STATE_DISCONNECTED; + + peer -> incomingBandwidth = 0; + peer -> outgoingBandwidth = 0; + peer -> incomingBandwidthThrottleEpoch = 0; + peer -> outgoingBandwidthThrottleEpoch = 0; + peer -> incomingDataTotal = 0; + peer -> outgoingDataTotal = 0; + peer -> lastSendTime = 0; + peer -> lastReceiveTime = 0; + peer -> nextTimeout = 0; + peer -> earliestTimeout = 0; + peer -> packetLossEpoch = 0; + peer -> packetsSent = 0; + peer -> packetsLost = 0; + peer -> packetLoss = 0; + peer -> packetLossVariance = 0; + peer -> packetThrottle = ENET_PEER_DEFAULT_PACKET_THROTTLE; + peer -> packetThrottleLimit = ENET_PEER_PACKET_THROTTLE_SCALE; + peer -> packetThrottleCounter = 0; + peer -> packetThrottleEpoch = 0; + peer -> packetThrottleAcceleration = ENET_PEER_PACKET_THROTTLE_ACCELERATION; + peer -> packetThrottleDeceleration = ENET_PEER_PACKET_THROTTLE_DECELERATION; + peer -> packetThrottleInterval = ENET_PEER_PACKET_THROTTLE_INTERVAL; + peer -> pingInterval = ENET_PEER_PING_INTERVAL; + peer -> timeoutLimit = ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = ENET_PEER_TIMEOUT_MAXIMUM; + peer -> lastRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lowestRoundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> lastRoundTripTimeVariance = 0; + peer -> highestRoundTripTimeVariance = 0; + peer -> roundTripTime = ENET_PEER_DEFAULT_ROUND_TRIP_TIME; + peer -> roundTripTimeVariance = 0; + peer -> mtu = peer -> host -> mtu; + peer -> reliableDataInTransit = 0; + peer -> outgoingReliableSequenceNumber = 0; + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + peer -> incomingUnsequencedGroup = 0; + peer -> outgoingUnsequencedGroup = 0; + peer -> eventData = 0; + peer -> totalWaitingData = 0; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + + enet_peer_reset_queues (peer); +} + +/** Sends a ping request to a peer. + @param peer destination for the ping request + @remarks ping requests factor into the mean round trip time as designated by the + roundTripTime field in the ENetPeer structure. ENet automatically pings all connected + peers at regular intervals, however, this function may be called to ensure more + frequent ping requests. +*/ +void +enet_peer_ping (ENetPeer * peer) +{ + ENetProtocol command; + + if (peer -> state != ENET_PEER_STATE_CONNECTED) + return; + + command.header.command = ENET_PROTOCOL_COMMAND_PING | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + command.header.channelID = 0xFF; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); +} + +/** Sets the interval at which pings will be sent to a peer. + + Pings are used both to monitor the liveness of the connection and also to dynamically + adjust the throttle during periods of low traffic so that the throttle has reasonable + responsiveness during traffic spikes. + + @param peer the peer to adjust + @param pingInterval the interval at which to send pings; defaults to ENET_PEER_PING_INTERVAL if 0 +*/ +void +enet_peer_ping_interval (ENetPeer * peer, enet_uint32 pingInterval) +{ + peer -> pingInterval = pingInterval ? pingInterval : ENET_PEER_PING_INTERVAL; +} + +/** Sets the timeout parameters for a peer. + + The timeout parameter control how and when a peer will timeout from a failure to acknowledge + reliable traffic. Timeout values use an exponential backoff mechanism, where if a reliable + packet is not acknowledge within some multiple of the average RTT plus a variance tolerance, + the timeout will be doubled until it reaches a set limit. If the timeout is thus at this + limit and reliable packets have been sent but not acknowledged within a certain minimum time + period, the peer will be disconnected. Alternatively, if reliable packets have been sent + but not acknowledged for a certain maximum time period, the peer will be disconnected regardless + of the current timeout limit value. + + @param peer the peer to adjust + @param timeoutLimit the timeout limit; defaults to ENET_PEER_TIMEOUT_LIMIT if 0 + @param timeoutMinimum the timeout minimum; defaults to ENET_PEER_TIMEOUT_MINIMUM if 0 + @param timeoutMaximum the timeout maximum; defaults to ENET_PEER_TIMEOUT_MAXIMUM if 0 +*/ + +void +enet_peer_timeout (ENetPeer * peer, enet_uint32 timeoutLimit, enet_uint32 timeoutMinimum, enet_uint32 timeoutMaximum) +{ + peer -> timeoutLimit = timeoutLimit ? timeoutLimit : ENET_PEER_TIMEOUT_LIMIT; + peer -> timeoutMinimum = timeoutMinimum ? timeoutMinimum : ENET_PEER_TIMEOUT_MINIMUM; + peer -> timeoutMaximum = timeoutMaximum ? timeoutMaximum : ENET_PEER_TIMEOUT_MAXIMUM; +} + +/** Force an immediate disconnection from a peer. + @param peer peer to disconnect + @param data data describing the disconnection + @remarks No ENET_EVENT_DISCONNECT event will be generated. The foreign peer is not + guaranteed to receive the disconnect notification, and is reset immediately upon + return from this function. +*/ +void +enet_peer_disconnect_now (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED) + return; + + if (peer -> state != ENET_PEER_STATE_ZOMBIE && + peer -> state != ENET_PEER_STATE_DISCONNECTING) + { + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + enet_host_flush (peer -> host); + } + + enet_peer_reset (peer); +} + +/** Request a disconnection from a peer. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect (ENetPeer * peer, enet_uint32 data) +{ + ENetProtocol command; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTING || + peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT || + peer -> state == ENET_PEER_STATE_ZOMBIE) + return; + + enet_peer_reset_queues (peer); + + command.header.command = ENET_PROTOCOL_COMMAND_DISCONNECT; + command.header.channelID = 0xFF; + command.disconnect.data = ENET_HOST_TO_NET_32 (data); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + else + command.header.command |= ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; + + enet_peer_queue_outgoing_command (peer, & command, NULL, 0, 0); + + if (peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + { + enet_peer_on_disconnect (peer); + + peer -> state = ENET_PEER_STATE_DISCONNECTING; + } + else + { + enet_host_flush (peer -> host); + enet_peer_reset (peer); + } +} + +/** Request a disconnection from a peer, but only after all queued outgoing packets are sent. + @param peer peer to request a disconnection + @param data data describing the disconnection + @remarks An ENET_EVENT_DISCONNECT event will be generated by enet_host_service() + once the disconnection is complete. +*/ +void +enet_peer_disconnect_later (ENetPeer * peer, enet_uint32 data) +{ + if ((peer -> state == ENET_PEER_STATE_CONNECTED || peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) && + ! (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands))) + { + peer -> state = ENET_PEER_STATE_DISCONNECT_LATER; + peer -> eventData = data; + } + else + enet_peer_disconnect (peer, data); +} + +ENetAcknowledgement * +enet_peer_queue_acknowledgement (ENetPeer * peer, const ENetProtocol * command, enet_uint16 sentTime) +{ + ENetAcknowledgement * acknowledgement; + + if (command -> header.channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint16 reliableWindow = command -> header.reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (command -> header.reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1 && reliableWindow <= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS) + return NULL; + } + + acknowledgement = (ENetAcknowledgement *) enet_malloc (sizeof (ENetAcknowledgement)); + if (acknowledgement == NULL) + return NULL; + + peer -> outgoingDataTotal += sizeof (ENetProtocolAcknowledge); + + acknowledgement -> sentTime = sentTime; + acknowledgement -> command = * command; + + enet_list_insert (enet_list_end (& peer -> acknowledgements), acknowledgement); + + return acknowledgement; +} + +void +enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoingCommand) +{ + ENetChannel * channel = & peer -> channels [outgoingCommand -> command.header.channelID]; + + peer -> outgoingDataTotal += enet_protocol_command_size (outgoingCommand -> command.header.command) + outgoingCommand -> fragmentLength; + + if (outgoingCommand -> command.header.channelID == 0xFF) + { + ++ peer -> outgoingReliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = peer -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + { + ++ channel -> outgoingReliableSequenceNumber; + channel -> outgoingUnreliableSequenceNumber = 0; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED) + { + ++ peer -> outgoingUnsequencedGroup; + + outgoingCommand -> reliableSequenceNumber = 0; + outgoingCommand -> unreliableSequenceNumber = 0; + } + else + { + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; + + outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; + outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; + } + + outgoingCommand -> sendAttempts = 0; + outgoingCommand -> sentTime = 0; + outgoingCommand -> roundTripTimeout = 0; + outgoingCommand -> roundTripTimeoutLimit = 0; + outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + + switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber); + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); + break; + + default: + break; + } + + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand); + else + enet_list_insert (enet_list_end (& peer -> outgoingUnreliableCommands), outgoingCommand); +} + +ENetOutgoingCommand * +enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, ENetPacket * packet, enet_uint32 offset, enet_uint16 length) +{ + ENetOutgoingCommand * outgoingCommand = (ENetOutgoingCommand *) enet_malloc (sizeof (ENetOutgoingCommand)); + if (outgoingCommand == NULL) + return NULL; + + outgoingCommand -> command = * command; + outgoingCommand -> fragmentOffset = offset; + outgoingCommand -> fragmentLength = length; + outgoingCommand -> packet = packet; + if (packet != NULL) + ++ packet -> referenceCount; + + enet_peer_setup_outgoing_command (peer, outgoingCommand); + + return outgoingCommand; +} + +void +enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator droppedCommand, startCommand, currentCommand; + + for (droppedCommand = startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (incomingCommand -> reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> fragmentsRemaining <= 0) + { + channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; + continue; + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + else + if (droppedCommand != currentCommand) + droppedCommand = enet_list_previous (currentCommand); + } + else + { + enet_uint16 reliableWindow = incomingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE, + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + if (reliableWindow >= currentWindow && reliableWindow < currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + break; + + droppedCommand = enet_list_next (currentCommand); + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + } + } + + startCommand = enet_list_next (currentCommand); + } + + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + droppedCommand = currentCommand; + } + + enet_peer_remove_incoming_commands (& channel -> incomingUnreliableCommands, enet_list_begin (& channel -> incomingUnreliableCommands), droppedCommand); +} + +void +enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * channel) +{ + ENetListIterator currentCommand; + + for (currentCommand = enet_list_begin (& channel -> incomingReliableCommands); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (incomingCommand -> fragmentsRemaining > 0 || + incomingCommand -> reliableSequenceNumber != (enet_uint16) (channel -> incomingReliableSequenceNumber + 1)) + break; + + channel -> incomingReliableSequenceNumber = incomingCommand -> reliableSequenceNumber; + + if (incomingCommand -> fragmentCount > 0) + channel -> incomingReliableSequenceNumber += incomingCommand -> fragmentCount - 1; + } + + if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands)) + return; + + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + if (! enet_list_empty (& channel -> incomingUnreliableCommands)) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); +} + +ENetIncomingCommand * +enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, const void * data, size_t dataLength, enet_uint32 flags, enet_uint32 fragmentCount) +{ + static ENetIncomingCommand dummyCommand; + + ENetChannel * channel = & peer -> channels [command -> header.channelID]; + enet_uint32 unreliableSequenceNumber = 0, reliableSequenceNumber = 0; + enet_uint16 reliableWindow, currentWindow; + ENetIncomingCommand * incomingCommand; + ENetListIterator currentCommand; + ENetPacket * packet = NULL; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER) + goto discardCommand; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + { + reliableSequenceNumber = command -> header.reliableSequenceNumber; + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + goto discardCommand; + } + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber) + goto discardCommand; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= reliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + goto discardCommand; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber); + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + goto discardCommand; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + incomingCommand = (ENetIncomingCommand *) currentCommand; + + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= unreliableSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < unreliableSequenceNumber) + break; + + goto discardCommand; + } + } + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + currentCommand = enet_list_end (& channel -> incomingUnreliableCommands); + break; + + default: + goto discardCommand; + } + + if (peer -> totalWaitingData >= peer -> host -> maximumWaitingData) + goto notifyError; + + packet = enet_packet_create (data, dataLength, flags); + if (packet == NULL) + goto notifyError; + + incomingCommand = (ENetIncomingCommand *) enet_malloc (sizeof (ENetIncomingCommand)); + if (incomingCommand == NULL) + goto notifyError; + + incomingCommand -> reliableSequenceNumber = command -> header.reliableSequenceNumber; + incomingCommand -> unreliableSequenceNumber = unreliableSequenceNumber & 0xFFFF; + incomingCommand -> command = * command; + incomingCommand -> fragmentCount = fragmentCount; + incomingCommand -> fragmentsRemaining = fragmentCount; + incomingCommand -> packet = packet; + incomingCommand -> fragments = NULL; + + if (fragmentCount > 0) + { + if (fragmentCount <= ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT) + incomingCommand -> fragments = (enet_uint32 *) enet_malloc ((fragmentCount + 31) / 32 * sizeof (enet_uint32)); + if (incomingCommand -> fragments == NULL) + { + enet_free (incomingCommand); + + goto notifyError; + } + memset (incomingCommand -> fragments, 0, (fragmentCount + 31) / 32 * sizeof (enet_uint32)); + } + + if (packet != NULL) + { + ++ packet -> referenceCount; + + peer -> totalWaitingData += packet -> dataLength; + } + + enet_list_insert (enet_list_next (currentCommand), incomingCommand); + + switch (command -> header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + break; + + default: + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + break; + } + + return incomingCommand; + +discardCommand: + if (fragmentCount > 0) + goto notifyError; + + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return & dummyCommand; + +notifyError: + if (packet != NULL && packet -> referenceCount == 0) + enet_packet_destroy (packet); + + return NULL; +} + +/** @} */ diff --git a/modules/enet/protocol.c b/modules/enet/protocol.c new file mode 100644 index 0000000000..5225147942 --- /dev/null +++ b/modules/enet/protocol.c @@ -0,0 +1,1914 @@ +/** + @file protocol.c + @brief ENet protocol functions +*/ +#include <stdio.h> +#include <string.h> +#define ENET_BUILDING_LIB 1 +#include "enet/utility.h" +#include "enet/time.h" +#include "enet/enet.h" + + +static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = +{ + 0, + sizeof (ENetProtocolAcknowledge), + sizeof (ENetProtocolConnect), + sizeof (ENetProtocolVerifyConnect), + sizeof (ENetProtocolDisconnect), + sizeof (ENetProtocolPing), + sizeof (ENetProtocolSendReliable), + sizeof (ENetProtocolSendUnreliable), + sizeof (ENetProtocolSendFragment), + sizeof (ENetProtocolSendUnsequenced), + sizeof (ENetProtocolBandwidthLimit), + sizeof (ENetProtocolThrottleConfigure), + sizeof (ENetProtocolSendFragment) +}; + +size_t +enet_protocol_command_size (enet_uint8 commandNumber) +{ + return commandSizes [commandNumber & ENET_PROTOCOL_COMMAND_MASK]; +} + +static void +enet_protocol_change_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) +{ + if (state == ENET_PEER_STATE_CONNECTED || state == ENET_PEER_STATE_DISCONNECT_LATER) + enet_peer_on_connect (peer); + else + enet_peer_on_disconnect (peer); + + peer -> state = state; +} + +static void +enet_protocol_dispatch_state (ENetHost * host, ENetPeer * peer, ENetPeerState state) +{ + enet_protocol_change_state (host, peer, state); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } +} + +static int +enet_protocol_dispatch_incoming_commands (ENetHost * host, ENetEvent * event) +{ + while (! enet_list_empty (& host -> dispatchQueue)) + { + ENetPeer * peer = (ENetPeer *) enet_list_remove (enet_list_begin (& host -> dispatchQueue)); + + peer -> needsDispatch = 0; + + switch (peer -> state) + { + case ENET_PEER_STATE_CONNECTION_PENDING: + case ENET_PEER_STATE_CONNECTION_SUCCEEDED: + enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + return 1; + + case ENET_PEER_STATE_ZOMBIE: + host -> recalculateBandwidthLimits = 1; + + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + + enet_peer_reset (peer); + + return 1; + + case ENET_PEER_STATE_CONNECTED: + if (enet_list_empty (& peer -> dispatchedCommands)) + continue; + + event -> packet = enet_peer_receive (peer, & event -> channelID); + if (event -> packet == NULL) + continue; + + event -> type = ENET_EVENT_TYPE_RECEIVE; + event -> peer = peer; + + if (! enet_list_empty (& peer -> dispatchedCommands)) + { + peer -> needsDispatch = 1; + + enet_list_insert (enet_list_end (& host -> dispatchQueue), & peer -> dispatchList); + } + + return 1; + + default: + break; + } + } + + return 0; +} + +static void +enet_protocol_notify_connect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + host -> recalculateBandwidthLimits = 1; + + if (event != NULL) + { + enet_protocol_change_state (host, peer, ENET_PEER_STATE_CONNECTED); + + event -> type = ENET_EVENT_TYPE_CONNECT; + event -> peer = peer; + event -> data = peer -> eventData; + } + else + enet_protocol_dispatch_state (host, peer, peer -> state == ENET_PEER_STATE_CONNECTING ? ENET_PEER_STATE_CONNECTION_SUCCEEDED : ENET_PEER_STATE_CONNECTION_PENDING); +} + +static void +enet_protocol_notify_disconnect (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + if (peer -> state >= ENET_PEER_STATE_CONNECTION_PENDING) + host -> recalculateBandwidthLimits = 1; + + if (peer -> state != ENET_PEER_STATE_CONNECTING && peer -> state < ENET_PEER_STATE_CONNECTION_SUCCEEDED) + enet_peer_reset (peer); + else + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_DISCONNECT; + event -> peer = peer; + event -> data = 0; + + enet_peer_reset (peer); + } + else + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + } +} + +static void +enet_protocol_remove_sent_unreliable_commands (ENetPeer * peer) +{ + ENetOutgoingCommand * outgoingCommand; + + while (! enet_list_empty (& peer -> sentUnreliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentUnreliableCommands); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + } +} + +static ENetProtocolCommand +enet_protocol_remove_sent_reliable_command (ENetPeer * peer, enet_uint16 reliableSequenceNumber, enet_uint8 channelID) +{ + ENetOutgoingCommand * outgoingCommand = NULL; + ENetListIterator currentCommand; + ENetProtocolCommand commandNumber; + int wasSent = 1; + + for (currentCommand = enet_list_begin (& peer -> sentReliableCommands); + currentCommand != enet_list_end (& peer -> sentReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> sentReliableCommands)) + { + for (currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + currentCommand != enet_list_end (& peer -> outgoingReliableCommands); + currentCommand = enet_list_next (currentCommand)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + if (outgoingCommand -> sendAttempts < 1) return ENET_PROTOCOL_COMMAND_NONE; + + if (outgoingCommand -> reliableSequenceNumber == reliableSequenceNumber && + outgoingCommand -> command.header.channelID == channelID) + break; + } + + if (currentCommand == enet_list_end (& peer -> outgoingReliableCommands)) + return ENET_PROTOCOL_COMMAND_NONE; + + wasSent = 0; + } + + if (outgoingCommand == NULL) + return ENET_PROTOCOL_COMMAND_NONE; + + if (channelID < peer -> channelCount) + { + ENetChannel * channel = & peer -> channels [channelID]; + enet_uint16 reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel -> reliableWindows [reliableWindow] > 0) + { + -- channel -> reliableWindows [reliableWindow]; + if (! channel -> reliableWindows [reliableWindow]) + channel -> usedReliableWindows &= ~ (1 << reliableWindow); + } + } + + commandNumber = (ENetProtocolCommand) (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + if (wasSent) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + { + outgoingCommand -> packet -> flags |= ENET_PACKET_FLAG_SENT; + + enet_packet_destroy (outgoingCommand -> packet); + } + } + + enet_free (outgoingCommand); + + if (enet_list_empty (& peer -> sentReliableCommands)) + return commandNumber; + + outgoingCommand = (ENetOutgoingCommand *) enet_list_front (& peer -> sentReliableCommands); + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + + return commandNumber; +} + +static ENetPeer * +enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENetProtocol * command) +{ + enet_uint8 incomingSessionID, outgoingSessionID; + enet_uint32 mtu, windowSize; + ENetChannel * channel; + size_t channelCount, duplicatePeers = 0; + ENetPeer * currentPeer, * peer = NULL; + ENetProtocol verifyCommand; + + channelCount = ENET_NET_TO_HOST_32 (command -> connect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || + channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT) + return NULL; + + for (currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED) + { + if (peer == NULL) + peer = currentPeer; + } + else + if (currentPeer -> state != ENET_PEER_STATE_CONNECTING && + currentPeer -> address.host == host -> receivedAddress.host) + { + if (currentPeer -> address.port == host -> receivedAddress.port && + currentPeer -> connectID == command -> connect.connectID) + return NULL; + + ++ duplicatePeers; + } + } + + if (peer == NULL || duplicatePeers >= host -> duplicatePeers) + return NULL; + + if (channelCount > host -> channelLimit) + channelCount = host -> channelLimit; + peer -> channels = (ENetChannel *) enet_malloc (channelCount * sizeof (ENetChannel)); + if (peer -> channels == NULL) + return NULL; + peer -> channelCount = channelCount; + peer -> state = ENET_PEER_STATE_ACKNOWLEDGING_CONNECT; + peer -> connectID = command -> connect.connectID; + peer -> address = host -> receivedAddress; + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> connect.outgoingPeerID); + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> connect.outgoingBandwidth); + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> connect.packetThrottleDeceleration); + peer -> eventData = ENET_NET_TO_HOST_32 (command -> connect.data); + + incomingSessionID = command -> connect.incomingSessionID == 0xFF ? peer -> outgoingSessionID : command -> connect.incomingSessionID; + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (incomingSessionID == peer -> outgoingSessionID) + incomingSessionID = (incomingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + peer -> outgoingSessionID = incomingSessionID; + + outgoingSessionID = command -> connect.outgoingSessionID == 0xFF ? peer -> incomingSessionID : command -> connect.outgoingSessionID; + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + if (outgoingSessionID == peer -> incomingSessionID) + outgoingSessionID = (outgoingSessionID + 1) & (ENET_PROTOCOL_HEADER_SESSION_MASK >> ENET_PROTOCOL_HEADER_SESSION_SHIFT); + peer -> incomingSessionID = outgoingSessionID; + + for (channel = peer -> channels; + channel < & peer -> channels [channelCount]; + ++ channel) + { + channel -> outgoingReliableSequenceNumber = 0; + channel -> outgoingUnreliableSequenceNumber = 0; + channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; + + enet_list_clear (& channel -> incomingReliableCommands); + enet_list_clear (& channel -> incomingUnreliableCommands); + + channel -> usedReliableWindows = 0; + memset (channel -> reliableWindows, 0, sizeof (channel -> reliableWindows)); + } + + mtu = ENET_NET_TO_HOST_32 (command -> connect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + peer -> mtu = mtu; + + if (host -> outgoingBandwidth == 0 && + peer -> incomingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + if (host -> outgoingBandwidth == 0 || + peer -> incomingBandwidth == 0) + peer -> windowSize = (ENET_MAX (host -> outgoingBandwidth, peer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (host -> outgoingBandwidth, peer -> incomingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (host -> incomingBandwidth == 0) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + windowSize = (host -> incomingBandwidth / ENET_PEER_WINDOW_SIZE_SCALE) * + ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_NET_TO_HOST_32 (command -> connect.windowSize)) + windowSize = ENET_NET_TO_HOST_32 (command -> connect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + verifyCommand.header.command = ENET_PROTOCOL_COMMAND_VERIFY_CONNECT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + verifyCommand.header.channelID = 0xFF; + verifyCommand.verifyConnect.outgoingPeerID = ENET_HOST_TO_NET_16 (peer -> incomingPeerID); + verifyCommand.verifyConnect.incomingSessionID = incomingSessionID; + verifyCommand.verifyConnect.outgoingSessionID = outgoingSessionID; + verifyCommand.verifyConnect.mtu = ENET_HOST_TO_NET_32 (peer -> mtu); + verifyCommand.verifyConnect.windowSize = ENET_HOST_TO_NET_32 (windowSize); + verifyCommand.verifyConnect.channelCount = ENET_HOST_TO_NET_32 (channelCount); + verifyCommand.verifyConnect.incomingBandwidth = ENET_HOST_TO_NET_32 (host -> incomingBandwidth); + verifyCommand.verifyConnect.outgoingBandwidth = ENET_HOST_TO_NET_32 (host -> outgoingBandwidth); + verifyCommand.verifyConnect.packetThrottleInterval = ENET_HOST_TO_NET_32 (peer -> packetThrottleInterval); + verifyCommand.verifyConnect.packetThrottleAcceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleAcceleration); + verifyCommand.verifyConnect.packetThrottleDeceleration = ENET_HOST_TO_NET_32 (peer -> packetThrottleDeceleration); + verifyCommand.verifyConnect.connectID = peer -> connectID; + + enet_peer_queue_outgoing_command (peer, & verifyCommand, NULL, 0, 0); + + return peer; +} + +static int +enet_protocol_handle_send_reliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendReliable.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendReliable), dataLength, ENET_PACKET_FLAG_RELIABLE, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_unsequenced (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 unsequencedGroup, index; + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + unsequencedGroup = ENET_NET_TO_HOST_16 (command -> sendUnsequenced.unsequencedGroup); + index = unsequencedGroup % ENET_PEER_UNSEQUENCED_WINDOW_SIZE; + + if (unsequencedGroup < peer -> incomingUnsequencedGroup) + unsequencedGroup += 0x10000; + + if (unsequencedGroup >= (enet_uint32) peer -> incomingUnsequencedGroup + ENET_PEER_FREE_UNSEQUENCED_WINDOWS * ENET_PEER_UNSEQUENCED_WINDOW_SIZE) + return 0; + + unsequencedGroup &= 0xFFFF; + + if (unsequencedGroup - index != peer -> incomingUnsequencedGroup) + { + peer -> incomingUnsequencedGroup = unsequencedGroup - index; + + memset (peer -> unsequencedWindow, 0, sizeof (peer -> unsequencedWindow)); + } + else + if (peer -> unsequencedWindow [index / 32] & (1 << (index % 32))) + return 0; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnsequenced), dataLength, ENET_PACKET_FLAG_UNSEQUENCED, 0) == NULL) + return -1; + + peer -> unsequencedWindow [index / 32] |= 1 << (index % 32); + + return 0; +} + +static int +enet_protocol_handle_send_unreliable (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + size_t dataLength; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + dataLength = ENET_NET_TO_HOST_16 (command -> sendUnreliable.dataLength); + * currentData += dataLength; + if (dataLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + if (enet_peer_queue_incoming_command (peer, command, (const enet_uint8 *) command + sizeof (ENetProtocolSendUnreliable), dataLength, 0, 0) == NULL) + return -1; + + return 0; +} + +static int +enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + startSequenceNumber, + totalLength; + ENetChannel * channel; + enet_uint16 startWindow, currentWindow; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + startWindow = startSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (startSequenceNumber < channel -> incomingReliableSequenceNumber) + startWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (startWindow < currentWindow || startWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > host -> maximumPacketSize || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); + currentCommand != enet_list_end (& channel -> incomingReliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (startSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + ENetProtocol hostCommand = * command; + + hostCommand.header.reliableSequenceNumber = startSequenceNumber; + + startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, NULL, totalLength, ENET_PACKET_FLAG_RELIABLE, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_reliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + reliableSequenceNumber, + startSequenceNumber, + totalLength; + enet_uint16 reliableWindow, currentWindow; + ENetChannel * channel; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (fragmentLength > host -> maximumPacketSize || + * currentData < host -> receivedData || + * currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + reliableSequenceNumber = command -> header.reliableSequenceNumber; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + startSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentCount > ENET_PROTOCOL_MAXIMUM_FRAGMENT_COUNT || + fragmentNumber >= fragmentCount || + totalLength > host -> maximumPacketSize || + fragmentOffset >= totalLength || + fragmentLength > totalLength - fragmentOffset) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + startCommand = enet_peer_queue_incoming_command (peer, command, NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + } + + return 0; +} + +static int +enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + return 0; +} + +static int +enet_protocol_handle_bandwidth_limit (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + if (peer -> incomingBandwidth != 0) + -- host -> bandwidthLimitedPeers; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> bandwidthLimit.outgoingBandwidth); + + if (peer -> incomingBandwidth != 0) + ++ host -> bandwidthLimitedPeers; + + if (peer -> incomingBandwidth == 0 && host -> outgoingBandwidth == 0) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + else + if (peer -> incomingBandwidth == 0 || host -> outgoingBandwidth == 0) + peer -> windowSize = (ENET_MAX (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + peer -> windowSize = (ENET_MIN (peer -> incomingBandwidth, host -> outgoingBandwidth) / + ENET_PEER_WINDOW_SIZE_SCALE) * ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (peer -> windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + else + if (peer -> windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + peer -> windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + return 0; +} + +static int +enet_protocol_handle_throttle_configure (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + return -1; + + peer -> packetThrottleInterval = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleInterval); + peer -> packetThrottleAcceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleAcceleration); + peer -> packetThrottleDeceleration = ENET_NET_TO_HOST_32 (command -> throttleConfigure.packetThrottleDeceleration); + + return 0; +} + +static int +enet_protocol_handle_disconnect (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) +{ + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE || peer -> state == ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT) + return 0; + + enet_peer_reset_queues (peer); + + if (peer -> state == ENET_PEER_STATE_CONNECTION_SUCCEEDED || peer -> state == ENET_PEER_STATE_DISCONNECTING || peer -> state == ENET_PEER_STATE_CONNECTING) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + else + if (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER) + { + if (peer -> state == ENET_PEER_STATE_CONNECTION_PENDING) host -> recalculateBandwidthLimits = 1; + + enet_peer_reset (peer); + } + else + if (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) + enet_protocol_change_state (host, peer, ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT); + else + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + if (peer -> state != ENET_PEER_STATE_DISCONNECTED) + peer -> eventData = ENET_NET_TO_HOST_32 (command -> disconnect.data); + + return 0; +} + +static int +enet_protocol_handle_acknowledge (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 roundTripTime, + receivedSentTime, + receivedReliableSequenceNumber; + ENetProtocolCommand commandNumber; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || peer -> state == ENET_PEER_STATE_ZOMBIE) + return 0; + + receivedSentTime = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedSentTime); + receivedSentTime |= host -> serviceTime & 0xFFFF0000; + if ((receivedSentTime & 0x8000) > (host -> serviceTime & 0x8000)) + receivedSentTime -= 0x10000; + + if (ENET_TIME_LESS (host -> serviceTime, receivedSentTime)) + return 0; + + peer -> lastReceiveTime = host -> serviceTime; + peer -> earliestTimeout = 0; + + roundTripTime = ENET_TIME_DIFFERENCE (host -> serviceTime, receivedSentTime); + + enet_peer_throttle (peer, roundTripTime); + + peer -> roundTripTimeVariance -= peer -> roundTripTimeVariance / 4; + + if (roundTripTime >= peer -> roundTripTime) + { + peer -> roundTripTime += (roundTripTime - peer -> roundTripTime) / 8; + peer -> roundTripTimeVariance += (roundTripTime - peer -> roundTripTime) / 4; + } + else + { + peer -> roundTripTime -= (peer -> roundTripTime - roundTripTime) / 8; + peer -> roundTripTimeVariance += (peer -> roundTripTime - roundTripTime) / 4; + } + + if (peer -> roundTripTime < peer -> lowestRoundTripTime) + peer -> lowestRoundTripTime = peer -> roundTripTime; + + if (peer -> roundTripTimeVariance > peer -> highestRoundTripTimeVariance) + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + + if (peer -> packetThrottleEpoch == 0 || + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> packetThrottleEpoch) >= peer -> packetThrottleInterval) + { + peer -> lastRoundTripTime = peer -> lowestRoundTripTime; + peer -> lastRoundTripTimeVariance = peer -> highestRoundTripTimeVariance; + peer -> lowestRoundTripTime = peer -> roundTripTime; + peer -> highestRoundTripTimeVariance = peer -> roundTripTimeVariance; + peer -> packetThrottleEpoch = host -> serviceTime; + } + + receivedReliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> acknowledge.receivedReliableSequenceNumber); + + commandNumber = enet_protocol_remove_sent_reliable_command (peer, receivedReliableSequenceNumber, command -> header.channelID); + + switch (peer -> state) + { + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + if (commandNumber != ENET_PROTOCOL_COMMAND_VERIFY_CONNECT) + return -1; + + enet_protocol_notify_connect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECTING: + if (commandNumber != ENET_PROTOCOL_COMMAND_DISCONNECT) + return -1; + + enet_protocol_notify_disconnect (host, peer, event); + break; + + case ENET_PEER_STATE_DISCONNECT_LATER: + if (enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); + break; + + default: + break; + } + + return 0; +} + +static int +enet_protocol_handle_verify_connect (ENetHost * host, ENetEvent * event, ENetPeer * peer, const ENetProtocol * command) +{ + enet_uint32 mtu, windowSize; + size_t channelCount; + + if (peer -> state != ENET_PEER_STATE_CONNECTING) + return 0; + + channelCount = ENET_NET_TO_HOST_32 (command -> verifyConnect.channelCount); + + if (channelCount < ENET_PROTOCOL_MINIMUM_CHANNEL_COUNT || channelCount > ENET_PROTOCOL_MAXIMUM_CHANNEL_COUNT || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleInterval) != peer -> packetThrottleInterval || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleAcceleration) != peer -> packetThrottleAcceleration || + ENET_NET_TO_HOST_32 (command -> verifyConnect.packetThrottleDeceleration) != peer -> packetThrottleDeceleration || + command -> verifyConnect.connectID != peer -> connectID) + { + peer -> eventData = 0; + + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + return -1; + } + + enet_protocol_remove_sent_reliable_command (peer, 1, 0xFF); + + if (channelCount < peer -> channelCount) + peer -> channelCount = channelCount; + + peer -> outgoingPeerID = ENET_NET_TO_HOST_16 (command -> verifyConnect.outgoingPeerID); + peer -> incomingSessionID = command -> verifyConnect.incomingSessionID; + peer -> outgoingSessionID = command -> verifyConnect.outgoingSessionID; + + mtu = ENET_NET_TO_HOST_32 (command -> verifyConnect.mtu); + + if (mtu < ENET_PROTOCOL_MINIMUM_MTU) + mtu = ENET_PROTOCOL_MINIMUM_MTU; + else + if (mtu > ENET_PROTOCOL_MAXIMUM_MTU) + mtu = ENET_PROTOCOL_MAXIMUM_MTU; + + if (mtu < peer -> mtu) + peer -> mtu = mtu; + + windowSize = ENET_NET_TO_HOST_32 (command -> verifyConnect.windowSize); + + if (windowSize < ENET_PROTOCOL_MINIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MINIMUM_WINDOW_SIZE; + + if (windowSize > ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE) + windowSize = ENET_PROTOCOL_MAXIMUM_WINDOW_SIZE; + + if (windowSize < peer -> windowSize) + peer -> windowSize = windowSize; + + peer -> incomingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.incomingBandwidth); + peer -> outgoingBandwidth = ENET_NET_TO_HOST_32 (command -> verifyConnect.outgoingBandwidth); + + enet_protocol_notify_connect (host, peer, event); + return 0; +} + +static int +enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) +{ + ENetProtocolHeader * header; + ENetProtocol * command; + ENetPeer * peer; + enet_uint8 * currentData; + size_t headerSize; + enet_uint16 peerID, flags; + enet_uint8 sessionID; + + if (host -> receivedDataLength < (size_t) & ((ENetProtocolHeader *) 0) -> sentTime) + return 0; + + header = (ENetProtocolHeader *) host -> receivedData; + + peerID = ENET_NET_TO_HOST_16 (header -> peerID); + sessionID = (peerID & ENET_PROTOCOL_HEADER_SESSION_MASK) >> ENET_PROTOCOL_HEADER_SESSION_SHIFT; + flags = peerID & ENET_PROTOCOL_HEADER_FLAG_MASK; + peerID &= ~ (ENET_PROTOCOL_HEADER_FLAG_MASK | ENET_PROTOCOL_HEADER_SESSION_MASK); + + headerSize = (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME ? sizeof (ENetProtocolHeader) : (size_t) & ((ENetProtocolHeader *) 0) -> sentTime); + if (host -> checksum != NULL) + headerSize += sizeof (enet_uint32); + + if (peerID == ENET_PROTOCOL_MAXIMUM_PEER_ID) + peer = NULL; + else + if (peerID >= host -> peerCount) + return 0; + else + { + peer = & host -> peers [peerID]; + + if (peer -> state == ENET_PEER_STATE_DISCONNECTED || + peer -> state == ENET_PEER_STATE_ZOMBIE || + ((host -> receivedAddress.host != peer -> address.host || + host -> receivedAddress.port != peer -> address.port) && + peer -> address.host != ENET_HOST_BROADCAST) || + (peer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID && + sessionID != peer -> incomingSessionID)) + return 0; + } + + if (flags & ENET_PROTOCOL_HEADER_FLAG_COMPRESSED) + { + size_t originalSize; + if (host -> compressor.context == NULL || host -> compressor.decompress == NULL) + return 0; + + originalSize = host -> compressor.decompress (host -> compressor.context, + host -> receivedData + headerSize, + host -> receivedDataLength - headerSize, + host -> packetData [1] + headerSize, + sizeof (host -> packetData [1]) - headerSize); + if (originalSize <= 0 || originalSize > sizeof (host -> packetData [1]) - headerSize) + return 0; + + memcpy (host -> packetData [1], header, headerSize); + host -> receivedData = host -> packetData [1]; + host -> receivedDataLength = headerSize + originalSize; + } + + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & host -> receivedData [headerSize - sizeof (enet_uint32)], + desiredChecksum = * checksum; + ENetBuffer buffer; + + * checksum = peer != NULL ? peer -> connectID : 0; + + buffer.data = host -> receivedData; + buffer.dataLength = host -> receivedDataLength; + + if (host -> checksum (& buffer, 1) != desiredChecksum) + return 0; + } + + if (peer != NULL) + { + peer -> address.host = host -> receivedAddress.host; + peer -> address.port = host -> receivedAddress.port; + peer -> incomingDataTotal += host -> receivedDataLength; + } + + currentData = host -> receivedData + headerSize; + + while (currentData < & host -> receivedData [host -> receivedDataLength]) + { + enet_uint8 commandNumber; + size_t commandSize; + + command = (ENetProtocol *) currentData; + + if (currentData + sizeof (ENetProtocolCommandHeader) > & host -> receivedData [host -> receivedDataLength]) + break; + + commandNumber = command -> header.command & ENET_PROTOCOL_COMMAND_MASK; + if (commandNumber >= ENET_PROTOCOL_COMMAND_COUNT) + break; + + commandSize = commandSizes [commandNumber]; + if (commandSize == 0 || currentData + commandSize > & host -> receivedData [host -> receivedDataLength]) + break; + + currentData += commandSize; + + if (peer == NULL && commandNumber != ENET_PROTOCOL_COMMAND_CONNECT) + break; + + command -> header.reliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> header.reliableSequenceNumber); + + switch (commandNumber) + { + case ENET_PROTOCOL_COMMAND_ACKNOWLEDGE: + if (enet_protocol_handle_acknowledge (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_CONNECT: + if (peer != NULL) + goto commandError; + peer = enet_protocol_handle_connect (host, header, command); + if (peer == NULL) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_VERIFY_CONNECT: + if (enet_protocol_handle_verify_connect (host, event, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_DISCONNECT: + if (enet_protocol_handle_disconnect (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_PING: + if (enet_protocol_handle_ping (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: + if (enet_protocol_handle_send_reliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + if (enet_protocol_handle_send_unreliable (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + if (enet_protocol_handle_send_unsequenced (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: + if (enet_protocol_handle_send_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT: + if (enet_protocol_handle_bandwidth_limit (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE: + if (enet_protocol_handle_throttle_configure (host, peer, command)) + goto commandError; + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData)) + goto commandError; + break; + + default: + goto commandError; + } + + if (peer != NULL && + (command -> header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) != 0) + { + enet_uint16 sentTime; + + if (! (flags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME)) + break; + + sentTime = ENET_NET_TO_HOST_16 (header -> sentTime); + + switch (peer -> state) + { + case ENET_PEER_STATE_DISCONNECTING: + case ENET_PEER_STATE_ACKNOWLEDGING_CONNECT: + case ENET_PEER_STATE_DISCONNECTED: + case ENET_PEER_STATE_ZOMBIE: + break; + + case ENET_PEER_STATE_ACKNOWLEDGING_DISCONNECT: + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + + default: + enet_peer_queue_acknowledgement (peer, command, sentTime); + break; + } + } + } + +commandError: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + return 0; +} + +static int +enet_protocol_receive_incoming_commands (ENetHost * host, ENetEvent * event) +{ + int packets; + + for (packets = 0; packets < 256; ++ packets) + { + int receivedLength; + ENetBuffer buffer; + + buffer.data = host -> packetData [0]; + buffer.dataLength = sizeof (host -> packetData [0]); + + receivedLength = enet_socket_receive (host -> socket, + & host -> receivedAddress, + & buffer, + 1); + + if (receivedLength < 0) + return -1; + + if (receivedLength == 0) + return 0; + + host -> receivedData = host -> packetData [0]; + host -> receivedDataLength = receivedLength; + + host -> totalReceivedData += receivedLength; + host -> totalReceivedPackets ++; + + if (host -> intercept != NULL) + { + switch (host -> intercept (host, event)) + { + case 1: + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + + continue; + + case -1: + return -1; + + default: + break; + } + } + + switch (enet_protocol_handle_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: + return -1; + + default: + break; + } + } + + return -1; +} + +static void +enet_protocol_send_acknowledgements (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetAcknowledgement * acknowledgement; + ENetListIterator currentAcknowledgement; + enet_uint16 reliableSequenceNumber; + + currentAcknowledgement = enet_list_begin (& peer -> acknowledgements); + + while (currentAcknowledgement != enet_list_end (& peer -> acknowledgements)) + { + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < sizeof (ENetProtocolAcknowledge)) + { + host -> continueSending = 1; + + break; + } + + acknowledgement = (ENetAcknowledgement *) currentAcknowledgement; + + currentAcknowledgement = enet_list_next (currentAcknowledgement); + + buffer -> data = command; + buffer -> dataLength = sizeof (ENetProtocolAcknowledge); + + host -> packetSize += buffer -> dataLength; + + reliableSequenceNumber = ENET_HOST_TO_NET_16 (acknowledgement -> command.header.reliableSequenceNumber); + + command -> header.command = ENET_PROTOCOL_COMMAND_ACKNOWLEDGE; + command -> header.channelID = acknowledgement -> command.header.channelID; + command -> header.reliableSequenceNumber = reliableSequenceNumber; + command -> acknowledge.receivedReliableSequenceNumber = reliableSequenceNumber; + command -> acknowledge.receivedSentTime = ENET_HOST_TO_NET_16 (acknowledgement -> sentTime); + + if ((acknowledgement -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_DISCONNECT) + enet_protocol_dispatch_state (host, peer, ENET_PEER_STATE_ZOMBIE); + + enet_list_remove (& acknowledgement -> acknowledgementList); + enet_free (acknowledgement); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; +} + +static void +enet_protocol_send_unreliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + + currentCommand = enet_list_begin (& peer -> outgoingUnreliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingUnreliableCommands)) + { + size_t commandSize; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + peer -> mtu - host -> packetSize < commandSize + outgoingCommand -> fragmentLength)) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (outgoingCommand -> packet != NULL && outgoingCommand -> fragmentOffset == 0) + { + peer -> packetThrottleCounter += ENET_PEER_PACKET_THROTTLE_COUNTER; + peer -> packetThrottleCounter %= ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> packetThrottleCounter > peer -> packetThrottle) + { + enet_uint16 reliableSequenceNumber = outgoingCommand -> reliableSequenceNumber, + unreliableSequenceNumber = outgoingCommand -> unreliableSequenceNumber; + for (;;) + { + -- outgoingCommand -> packet -> referenceCount; + + if (outgoingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (outgoingCommand -> packet); + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + enet_free (outgoingCommand); + + if (currentCommand == enet_list_end (& peer -> outgoingUnreliableCommands)) + break; + + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + if (outgoingCommand -> reliableSequenceNumber != reliableSequenceNumber || + outgoingCommand -> unreliableSequenceNumber != unreliableSequenceNumber) + break; + + currentCommand = enet_list_next (currentCommand); + } + + continue; + } + } + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + + * command = outgoingCommand -> command; + + enet_list_remove (& outgoingCommand -> outgoingCommandList); + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += buffer -> dataLength; + + enet_list_insert (enet_list_end (& peer -> sentUnreliableCommands), outgoingCommand); + } + else + enet_free (outgoingCommand); + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + if (peer -> state == ENET_PEER_STATE_DISCONNECT_LATER && + enet_list_empty (& peer -> outgoingReliableCommands) && + enet_list_empty (& peer -> outgoingUnreliableCommands) && + enet_list_empty (& peer -> sentReliableCommands)) + enet_peer_disconnect (peer, peer -> eventData); +} + +static int +enet_protocol_check_timeouts (ENetHost * host, ENetPeer * peer, ENetEvent * event) +{ + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand, insertPosition; + + currentCommand = enet_list_begin (& peer -> sentReliableCommands); + insertPosition = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + currentCommand = enet_list_next (currentCommand); + + if (ENET_TIME_DIFFERENCE (host -> serviceTime, outgoingCommand -> sentTime) < outgoingCommand -> roundTripTimeout) + continue; + + if (peer -> earliestTimeout == 0 || + ENET_TIME_LESS (outgoingCommand -> sentTime, peer -> earliestTimeout)) + peer -> earliestTimeout = outgoingCommand -> sentTime; + + if (peer -> earliestTimeout != 0 && + (ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMaximum || + (outgoingCommand -> roundTripTimeout >= outgoingCommand -> roundTripTimeoutLimit && + ENET_TIME_DIFFERENCE (host -> serviceTime, peer -> earliestTimeout) >= peer -> timeoutMinimum))) + { + enet_protocol_notify_disconnect (host, peer, event); + + return 1; + } + + if (outgoingCommand -> packet != NULL) + peer -> reliableDataInTransit -= outgoingCommand -> fragmentLength; + + ++ peer -> packetsLost; + + outgoingCommand -> roundTripTimeout *= 2; + + enet_list_insert (insertPosition, enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + if (currentCommand == enet_list_begin (& peer -> sentReliableCommands) && + ! enet_list_empty (& peer -> sentReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + peer -> nextTimeout = outgoingCommand -> sentTime + outgoingCommand -> roundTripTimeout; + } + } + + return 0; +} + +static int +enet_protocol_send_reliable_outgoing_commands (ENetHost * host, ENetPeer * peer) +{ + ENetProtocol * command = & host -> commands [host -> commandCount]; + ENetBuffer * buffer = & host -> buffers [host -> bufferCount]; + ENetOutgoingCommand * outgoingCommand; + ENetListIterator currentCommand; + ENetChannel *channel; + enet_uint16 reliableWindow; + size_t commandSize; + int windowExceeded = 0, windowWrap = 0, canPing = 1; + + currentCommand = enet_list_begin (& peer -> outgoingReliableCommands); + + while (currentCommand != enet_list_end (& peer -> outgoingReliableCommands)) + { + outgoingCommand = (ENetOutgoingCommand *) currentCommand; + + channel = outgoingCommand -> command.header.channelID < peer -> channelCount ? & peer -> channels [outgoingCommand -> command.header.channelID] : NULL; + reliableWindow = outgoingCommand -> reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + if (channel != NULL) + { + if (! windowWrap && + outgoingCommand -> sendAttempts < 1 && + ! (outgoingCommand -> reliableSequenceNumber % ENET_PEER_RELIABLE_WINDOW_SIZE) && + (channel -> reliableWindows [(reliableWindow + ENET_PEER_RELIABLE_WINDOWS - 1) % ENET_PEER_RELIABLE_WINDOWS] >= ENET_PEER_RELIABLE_WINDOW_SIZE || + channel -> usedReliableWindows & ((((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) << reliableWindow) | + (((1 << ENET_PEER_FREE_RELIABLE_WINDOWS) - 1) >> (ENET_PEER_RELIABLE_WINDOWS - reliableWindow))))) + windowWrap = 1; + if (windowWrap) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + if (outgoingCommand -> packet != NULL) + { + if (! windowExceeded) + { + enet_uint32 windowSize = (peer -> packetThrottle * peer -> windowSize) / ENET_PEER_PACKET_THROTTLE_SCALE; + + if (peer -> reliableDataInTransit + outgoingCommand -> fragmentLength > ENET_MAX (windowSize, peer -> mtu)) + windowExceeded = 1; + } + if (windowExceeded) + { + currentCommand = enet_list_next (currentCommand); + + continue; + } + } + + canPing = 0; + + commandSize = commandSizes [outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK]; + if (command >= & host -> commands [sizeof (host -> commands) / sizeof (ENetProtocol)] || + buffer + 1 >= & host -> buffers [sizeof (host -> buffers) / sizeof (ENetBuffer)] || + peer -> mtu - host -> packetSize < commandSize || + (outgoingCommand -> packet != NULL && + (enet_uint16) (peer -> mtu - host -> packetSize) < (enet_uint16) (commandSize + outgoingCommand -> fragmentLength))) + { + host -> continueSending = 1; + + break; + } + + currentCommand = enet_list_next (currentCommand); + + if (channel != NULL && outgoingCommand -> sendAttempts < 1) + { + channel -> usedReliableWindows |= 1 << reliableWindow; + ++ channel -> reliableWindows [reliableWindow]; + } + + ++ outgoingCommand -> sendAttempts; + + if (outgoingCommand -> roundTripTimeout == 0) + { + outgoingCommand -> roundTripTimeout = peer -> roundTripTime + 4 * peer -> roundTripTimeVariance; + outgoingCommand -> roundTripTimeoutLimit = peer -> timeoutLimit * outgoingCommand -> roundTripTimeout; + } + + if (enet_list_empty (& peer -> sentReliableCommands)) + peer -> nextTimeout = host -> serviceTime + outgoingCommand -> roundTripTimeout; + + enet_list_insert (enet_list_end (& peer -> sentReliableCommands), + enet_list_remove (& outgoingCommand -> outgoingCommandList)); + + outgoingCommand -> sentTime = host -> serviceTime; + + buffer -> data = command; + buffer -> dataLength = commandSize; + + host -> packetSize += buffer -> dataLength; + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_SENT_TIME; + + * command = outgoingCommand -> command; + + if (outgoingCommand -> packet != NULL) + { + ++ buffer; + + buffer -> data = outgoingCommand -> packet -> data + outgoingCommand -> fragmentOffset; + buffer -> dataLength = outgoingCommand -> fragmentLength; + + host -> packetSize += outgoingCommand -> fragmentLength; + + peer -> reliableDataInTransit += outgoingCommand -> fragmentLength; + } + + ++ peer -> packetsSent; + + ++ command; + ++ buffer; + } + + host -> commandCount = command - host -> commands; + host -> bufferCount = buffer - host -> buffers; + + return canPing; +} + +static int +enet_protocol_send_outgoing_commands (ENetHost * host, ENetEvent * event, int checkForTimeouts) +{ + enet_uint8 headerData [sizeof (ENetProtocolHeader) + sizeof (enet_uint32)]; + ENetProtocolHeader * header = (ENetProtocolHeader *) headerData; + ENetPeer * currentPeer; + int sentLength; + size_t shouldCompress = 0; + + host -> continueSending = 1; + + while (host -> continueSending) + for (host -> continueSending = 0, + currentPeer = host -> peers; + currentPeer < & host -> peers [host -> peerCount]; + ++ currentPeer) + { + if (currentPeer -> state == ENET_PEER_STATE_DISCONNECTED || + currentPeer -> state == ENET_PEER_STATE_ZOMBIE) + continue; + + host -> headerFlags = 0; + host -> commandCount = 0; + host -> bufferCount = 1; + host -> packetSize = sizeof (ENetProtocolHeader); + + if (! enet_list_empty (& currentPeer -> acknowledgements)) + enet_protocol_send_acknowledgements (host, currentPeer); + + if (checkForTimeouts != 0 && + ! enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_GREATER_EQUAL (host -> serviceTime, currentPeer -> nextTimeout) && + enet_protocol_check_timeouts (host, currentPeer, event) == 1) + { + if (event != NULL && event -> type != ENET_EVENT_TYPE_NONE) + return 1; + else + continue; + } + + if ((enet_list_empty (& currentPeer -> outgoingReliableCommands) || + enet_protocol_send_reliable_outgoing_commands (host, currentPeer)) && + enet_list_empty (& currentPeer -> sentReliableCommands) && + ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> lastReceiveTime) >= currentPeer -> pingInterval && + currentPeer -> mtu - host -> packetSize >= sizeof (ENetProtocolPing)) + { + enet_peer_ping (currentPeer); + enet_protocol_send_reliable_outgoing_commands (host, currentPeer); + } + + if (! enet_list_empty (& currentPeer -> outgoingUnreliableCommands)) + enet_protocol_send_unreliable_outgoing_commands (host, currentPeer); + + if (host -> commandCount == 0) + continue; + + if (currentPeer -> packetLossEpoch == 0) + currentPeer -> packetLossEpoch = host -> serviceTime; + else + if (ENET_TIME_DIFFERENCE (host -> serviceTime, currentPeer -> packetLossEpoch) >= ENET_PEER_PACKET_LOSS_INTERVAL && + currentPeer -> packetsSent > 0) + { + enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; + +#ifdef ENET_DEBUG + printf ("peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands) : 0); +#endif + + currentPeer -> packetLossVariance -= currentPeer -> packetLossVariance / 4; + + if (packetLoss >= currentPeer -> packetLoss) + { + currentPeer -> packetLoss += (packetLoss - currentPeer -> packetLoss) / 8; + currentPeer -> packetLossVariance += (packetLoss - currentPeer -> packetLoss) / 4; + } + else + { + currentPeer -> packetLoss -= (currentPeer -> packetLoss - packetLoss) / 8; + currentPeer -> packetLossVariance += (currentPeer -> packetLoss - packetLoss) / 4; + } + + currentPeer -> packetLossEpoch = host -> serviceTime; + currentPeer -> packetsSent = 0; + currentPeer -> packetsLost = 0; + } + + host -> buffers -> data = headerData; + if (host -> headerFlags & ENET_PROTOCOL_HEADER_FLAG_SENT_TIME) + { + header -> sentTime = ENET_HOST_TO_NET_16 (host -> serviceTime & 0xFFFF); + + host -> buffers -> dataLength = sizeof (ENetProtocolHeader); + } + else + host -> buffers -> dataLength = (size_t) & ((ENetProtocolHeader *) 0) -> sentTime; + + shouldCompress = 0; + if (host -> compressor.context != NULL && host -> compressor.compress != NULL) + { + size_t originalSize = host -> packetSize - sizeof(ENetProtocolHeader), + compressedSize = host -> compressor.compress (host -> compressor.context, + & host -> buffers [1], host -> bufferCount - 1, + originalSize, + host -> packetData [1], + originalSize); + if (compressedSize > 0 && compressedSize < originalSize) + { + host -> headerFlags |= ENET_PROTOCOL_HEADER_FLAG_COMPRESSED; + shouldCompress = compressedSize; +#ifdef ENET_DEBUG_COMPRESS + printf ("peer %u: compressed %u -> %u (%u%%)\n", currentPeer -> incomingPeerID, originalSize, compressedSize, (compressedSize * 100) / originalSize); +#endif + } + } + + if (currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID) + host -> headerFlags |= currentPeer -> outgoingSessionID << ENET_PROTOCOL_HEADER_SESSION_SHIFT; + header -> peerID = ENET_HOST_TO_NET_16 (currentPeer -> outgoingPeerID | host -> headerFlags); + if (host -> checksum != NULL) + { + enet_uint32 * checksum = (enet_uint32 *) & headerData [host -> buffers -> dataLength]; + * checksum = currentPeer -> outgoingPeerID < ENET_PROTOCOL_MAXIMUM_PEER_ID ? currentPeer -> connectID : 0; + host -> buffers -> dataLength += sizeof (enet_uint32); + * checksum = host -> checksum (host -> buffers, host -> bufferCount); + } + + if (shouldCompress > 0) + { + host -> buffers [1].data = host -> packetData [1]; + host -> buffers [1].dataLength = shouldCompress; + host -> bufferCount = 2; + } + + currentPeer -> lastSendTime = host -> serviceTime; + + sentLength = enet_socket_send (host -> socket, & currentPeer -> address, host -> buffers, host -> bufferCount); + + enet_protocol_remove_sent_unreliable_commands (currentPeer); + + if (sentLength < 0) + return -1; + + host -> totalSentData += sentLength; + host -> totalSentPackets ++; + } + + return 0; +} + +/** Sends any queued packets on the host specified to its designated peers. + + @param host host to flush + @remarks this function need only be used in circumstances where one wishes to send queued packets earlier than in a call to enet_host_service(). + @ingroup host +*/ +void +enet_host_flush (ENetHost * host) +{ + host -> serviceTime = enet_time_get (); + + enet_protocol_send_outgoing_commands (host, NULL, 0); +} + +/** Checks for any queued events on the host and dispatches one if available. + + @param host host to check for events + @param event an event structure where event details will be placed if available + @retval > 0 if an event was dispatched + @retval 0 if no events are available + @retval < 0 on failure + @ingroup host +*/ +int +enet_host_check_events (ENetHost * host, ENetEvent * event) +{ + if (event == NULL) return -1; + + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + return enet_protocol_dispatch_incoming_commands (host, event); +} + +/** Waits for events on the host specified and shuttles packets between + the host and its peers. + + @param host host to service + @param event an event structure where event details will be placed if one occurs + if event == NULL then no events will be delivered + @param timeout number of milliseconds that ENet should wait for events + @retval > 0 if an event occurred within the specified time limit + @retval 0 if no event occurred + @retval < 0 on failure + @remarks enet_host_service should be called fairly regularly for adequate performance + @ingroup host +*/ +int +enet_host_service (ENetHost * host, ENetEvent * event, enet_uint32 timeout) +{ + enet_uint32 waitCondition; + + if (event != NULL) + { + event -> type = ENET_EVENT_TYPE_NONE; + event -> peer = NULL; + event -> packet = NULL; + + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error dispatching incoming packets"); +#endif + + return -1; + + default: + break; + } + } + + host -> serviceTime = enet_time_get (); + + timeout += host -> serviceTime; + + do + { + if (ENET_TIME_DIFFERENCE (host -> serviceTime, host -> bandwidthThrottleEpoch) >= ENET_HOST_BANDWIDTH_THROTTLE_INTERVAL) + enet_host_bandwidth_throttle (host); + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error sending outgoing packets"); +#endif + + return -1; + + default: + break; + } + + switch (enet_protocol_receive_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error receiving incoming packets"); +#endif + + return -1; + + default: + break; + } + + switch (enet_protocol_send_outgoing_commands (host, event, 1)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error sending outgoing packets"); +#endif + + return -1; + + default: + break; + } + + if (event != NULL) + { + switch (enet_protocol_dispatch_incoming_commands (host, event)) + { + case 1: + return 1; + + case -1: +#ifdef ENET_DEBUG + perror ("Error dispatching incoming packets"); +#endif + + return -1; + + default: + break; + } + } + + if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) + return 0; + + do + { + host -> serviceTime = enet_time_get (); + + if (ENET_TIME_GREATER_EQUAL (host -> serviceTime, timeout)) + return 0; + + waitCondition = ENET_SOCKET_WAIT_RECEIVE | ENET_SOCKET_WAIT_INTERRUPT; + + if (enet_socket_wait (host -> socket, & waitCondition, ENET_TIME_DIFFERENCE (timeout, host -> serviceTime)) != 0) + return -1; + } + while (waitCondition & ENET_SOCKET_WAIT_INTERRUPT); + + host -> serviceTime = enet_time_get (); + } while (waitCondition & ENET_SOCKET_WAIT_RECEIVE); + + return 0; +} + diff --git a/modules/enet/register_types.cpp b/modules/enet/register_types.cpp new file mode 100644 index 0000000000..630b76ced8 --- /dev/null +++ b/modules/enet/register_types.cpp @@ -0,0 +1,51 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +#include "register_types.h" +#include "error_macros.h" +#include "networked_multiplayer_enet.h" + +static bool enet_ok=false; + +void register_enet_types() { + + if (enet_initialize() !=0 ) { + ERR_PRINT("ENet initialization failure"); + } else { + enet_ok=true; + } + + ObjectTypeDB::register_type<NetworkedMultiplayerENet>(); +} + +void unregister_enet_types() { + + if (enet_ok) + enet_deinitialize(); + +} diff --git a/modules/enet/register_types.h b/modules/enet/register_types.h new file mode 100644 index 0000000000..50f34dc67f --- /dev/null +++ b/modules/enet/register_types.h @@ -0,0 +1,30 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* http://www.godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ +void register_enet_types(); +void unregister_enet_types(); diff --git a/modules/enet/unix.c b/modules/enet/unix.c new file mode 100644 index 0000000000..3138cc04b6 --- /dev/null +++ b/modules/enet/unix.c @@ -0,0 +1,616 @@ +/** + @file unix.c + @brief ENet Unix system specific functions +*/ +#ifndef _WIN32 + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <arpa/inet.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <time.h> + +#define ENET_BUILDING_LIB 1 +#include "enet/enet.h" + +//@godot: added this since enet takes them fromt he build system +#define HAS_POLL +#define HAS_FCNTL +#define HAS_SOCKLEN_T + + +#ifdef __APPLE__ +#ifdef HAS_POLL +#undef HAS_POLL +#endif +#ifndef HAS_FCNTL +#define HAS_FCNTL 1 +#endif +#ifndef HAS_INET_PTON +#define HAS_INET_PTON 1 +#endif +#ifndef HAS_INET_NTOP +#define HAS_INET_NTOP 1 +#endif +#ifndef HAS_MSGHDR_FLAGS +#define HAS_MSGHDR_FLAGS 1 +#endif +#ifndef HAS_SOCKLEN_T +#define HAS_SOCKLEN_T 1 +#endif +#ifndef HAS_GETADDRINFO +#define HAS_GETADDRINFO 1 +#endif +#ifndef HAS_GETNAMEINFO +#define HAS_GETNAMEINFO 1 +#endif +#endif + +#ifdef HAS_FCNTL +#include <fcntl.h> +#endif + +#ifdef HAS_POLL +#include <sys/poll.h> +#endif + +#ifndef HAS_SOCKLEN_T +typedef int socklen_t; +#endif + +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + return 0; +} + +void +enet_deinitialize (void) +{ +} + +enet_uint32 +enet_host_random_seed (void) +{ + return (enet_uint32) time (NULL); +} + +enet_uint32 +enet_time_get (void) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + return timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + struct timeval timeVal; + + gettimeofday (& timeVal, NULL); + + timeBase = timeVal.tv_sec * 1000 + timeVal.tv_usec / 1000 - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ +#ifdef HAS_GETADDRINFO + struct addrinfo hints, * resultList = NULL, * result = NULL; + + memset (& hints, 0, sizeof (hints)); + hints.ai_family = AF_INET; + + if (getaddrinfo (name, NULL, NULL, & resultList) != 0) + return -1; + + for (result = resultList; result != NULL; result = result -> ai_next) + { + if (result -> ai_family == AF_INET && result -> ai_addr != NULL && result -> ai_addrlen >= sizeof (struct sockaddr_in)) + { + struct sockaddr_in * sin = (struct sockaddr_in *) result -> ai_addr; + + address -> host = sin -> sin_addr.s_addr; + + freeaddrinfo (resultList); + + return 0; + } + } + + if (resultList != NULL) + freeaddrinfo (resultList); +#else + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYNAME_R + struct hostent hostData; + char buffer [2048]; + int errnum; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyname_r (name, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + hostEntry = gethostbyname (name); +#endif + + if (hostEntry != NULL && hostEntry -> h_addrtype == AF_INET) + { + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; + } +#endif + +#ifdef HAS_INET_PTON + if (! inet_pton (AF_INET, name, & address -> host)) +#else + if (! inet_aton (name, (struct in_addr *) & address -> host)) +#endif + return -1; + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ +#ifdef HAS_INET_NTOP + if (inet_ntop (AF_INET, & address -> host, name, nameLength) == NULL) +#else + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr != NULL) + { + size_t addrLen = strlen(addr); + if (addrLen >= nameLength) + return -1; + memcpy (name, addr, addrLen + 1); + } + else +#endif + return -1; + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ +#ifdef HAS_GETNAMEINFO + struct sockaddr_in sin; + int err; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + err = getnameinfo ((struct sockaddr *) & sin, sizeof (sin), name, nameLength, NULL, 0, NI_NAMEREQD); + if (! err) + { + if (name != NULL && nameLength > 0 && ! memchr (name, '\0', nameLength)) + return -1; + return 0; + } + if (err != EAI_NONAME) + return -1; +#else + struct in_addr in; + struct hostent * hostEntry = NULL; +#ifdef HAS_GETHOSTBYADDR_R + struct hostent hostData; + char buffer [2048]; + int errnum; + + in.s_addr = address -> host; + +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) + gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & hostEntry, & errnum); +#else + hostEntry = gethostbyaddr_r ((char *) & in, sizeof (struct in_addr), AF_INET, & hostData, buffer, sizeof (buffer), & errnum); +#endif +#else + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); +#endif + + if (hostEntry != NULL) + { + size_t hostLen = strlen (hostEntry -> h_name); + if (hostLen >= nameLength) + return -1; + memcpy (name, hostEntry -> h_name, hostLen + 1); + return 0; + } +#endif + + return enet_address_get_host_ip (address, name, nameLength); +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)); +} + +int +enet_socket_get_address (ENetSocket socket, ENetAddress * address) +{ + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) + return -1; + + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + + return 0; +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog); +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = -1; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: +#ifdef HAS_FCNTL + result = fcntl (socket, F_SETFL, (value ? O_NONBLOCK : 0) | (fcntl (socket, F_GETFL) & ~O_NONBLOCK)); +#else + result = ioctl (socket, FIONBIO, & value); +#endif + break; + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + { + struct timeval timeVal; + timeVal.tv_sec = value / 1000; + timeVal.tv_usec = (value % 1000) * 1000; + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & timeVal, sizeof (struct timeval)); + break; + } + + case ENET_SOCKOPT_SNDTIMEO: + { + struct timeval timeVal; + timeVal.tv_sec = value / 1000; + timeVal.tv_usec = (value % 1000) * 1000; + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & timeVal, sizeof (struct timeval)); + break; + } + + case ENET_SOCKOPT_NODELAY: + result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == -1 ? -1 : 0; +} + +int +enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) +{ + int result = -1; + socklen_t len; + switch (option) + { + case ENET_SOCKOPT_ERROR: + len = sizeof (int); + result = getsockopt (socket, SOL_SOCKET, SO_ERROR, value, & len); + break; + + default: + break; + } + return result == -1 ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == -1 && errno == EINPROGRESS) + return 0; + + return result; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + int result; + struct sockaddr_in sin; + socklen_t sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == -1) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how); +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != -1) + close (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int sentLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + sentLength = sendmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (sentLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + + return sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + struct msghdr msgHdr; + struct sockaddr_in sin; + int recvLength; + + memset (& msgHdr, 0, sizeof (struct msghdr)); + + if (address != NULL) + { + msgHdr.msg_name = & sin; + msgHdr.msg_namelen = sizeof (struct sockaddr_in); + } + + msgHdr.msg_iov = (struct iovec *) buffers; + msgHdr.msg_iovlen = bufferCount; + + recvLength = recvmsg (socket, & msgHdr, MSG_NOSIGNAL); + + if (recvLength == -1) + { + if (errno == EWOULDBLOCK) + return 0; + + return -1; + } + +#ifdef HAS_MSGHDR_FLAGS + if (msgHdr.msg_flags & MSG_TRUNC) + return -1; +#endif + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ +#ifdef HAS_POLL + struct pollfd pollSocket; + int pollCount; + + pollSocket.fd = socket; + pollSocket.events = 0; + + if (* condition & ENET_SOCKET_WAIT_SEND) + pollSocket.events |= POLLOUT; + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + pollSocket.events |= POLLIN; + + pollCount = poll (& pollSocket, 1, timeout); + + if (pollCount < 0) + { + if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) + { + * condition = ENET_SOCKET_WAIT_INTERRUPT; + + return 0; + } + + return -1; + } + + * condition = ENET_SOCKET_WAIT_NONE; + + if (pollCount == 0) + return 0; + + if (pollSocket.revents & POLLOUT) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (pollSocket.revents & POLLIN) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#else + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + { + if (errno == EINTR && * condition & ENET_SOCKET_WAIT_INTERRUPT) + { + * condition = ENET_SOCKET_WAIT_INTERRUPT; + + return 0; + } + + return -1; + } + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +#endif +} + +#endif + diff --git a/modules/enet/win32.c b/modules/enet/win32.c new file mode 100644 index 0000000000..d77fa9a49a --- /dev/null +++ b/modules/enet/win32.c @@ -0,0 +1,422 @@ +/** + @file win32.c + @brief ENet Win32 system specific functions +*/ +#ifdef _WIN32 + +#define ENET_BUILDING_LIB 0 +#include "enet/enet.h" +#include <windows.h> +#include <mmsystem.h> + +static enet_uint32 timeBase = 0; + +int +enet_initialize (void) +{ + WORD versionRequested = MAKEWORD (1, 1); + WSADATA wsaData; + + if (WSAStartup (versionRequested, & wsaData)) + return -1; + + if (LOBYTE (wsaData.wVersion) != 1|| + HIBYTE (wsaData.wVersion) != 1) + { + WSACleanup (); + + return -1; + } + + timeBeginPeriod (1); + + return 0; +} + +void +enet_deinitialize (void) +{ + timeEndPeriod (1); + + WSACleanup (); +} + +enet_uint32 +enet_host_random_seed (void) +{ + return (enet_uint32) timeGetTime (); +} + +enet_uint32 +enet_time_get (void) +{ + return (enet_uint32) timeGetTime () - timeBase; +} + +void +enet_time_set (enet_uint32 newTimeBase) +{ + timeBase = (enet_uint32) timeGetTime () - newTimeBase; +} + +int +enet_address_set_host (ENetAddress * address, const char * name) +{ + struct hostent * hostEntry; + + hostEntry = gethostbyname (name); + if (hostEntry == NULL || + hostEntry -> h_addrtype != AF_INET) + { + unsigned long host = inet_addr (name); + if (host == INADDR_NONE) + return -1; + address -> host = host; + return 0; + } + + address -> host = * (enet_uint32 *) hostEntry -> h_addr_list [0]; + + return 0; +} + +int +enet_address_get_host_ip (const ENetAddress * address, char * name, size_t nameLength) +{ + char * addr = inet_ntoa (* (struct in_addr *) & address -> host); + if (addr == NULL) + return -1; + else + { + size_t addrLen = strlen(addr); + if (addrLen >= nameLength) + return -1; + memcpy (name, addr, addrLen + 1); + } + return 0; +} + +int +enet_address_get_host (const ENetAddress * address, char * name, size_t nameLength) +{ + struct in_addr in; + struct hostent * hostEntry; + + in.s_addr = address -> host; + + hostEntry = gethostbyaddr ((char *) & in, sizeof (struct in_addr), AF_INET); + if (hostEntry == NULL) + return enet_address_get_host_ip (address, name, nameLength); + else + { + size_t hostLen = strlen (hostEntry -> h_name); + if (hostLen >= nameLength) + return -1; + memcpy (name, hostEntry -> h_name, hostLen + 1); + } + + return 0; +} + +int +enet_socket_bind (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + + if (address != NULL) + { + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + else + { + sin.sin_port = 0; + sin.sin_addr.s_addr = INADDR_ANY; + } + + return bind (socket, + (struct sockaddr *) & sin, + sizeof (struct sockaddr_in)) == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_get_address (ENetSocket socket, ENetAddress * address) +{ + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + if (getsockname (socket, (struct sockaddr *) & sin, & sinLength) == -1) + return -1; + + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + + return 0; +} + +int +enet_socket_listen (ENetSocket socket, int backlog) +{ + return listen (socket, backlog < 0 ? SOMAXCONN : backlog) == SOCKET_ERROR ? -1 : 0; +} + +ENetSocket +enet_socket_create (ENetSocketType type) +{ + return socket (PF_INET, type == ENET_SOCKET_TYPE_DATAGRAM ? SOCK_DGRAM : SOCK_STREAM, 0); +} + +int +enet_socket_set_option (ENetSocket socket, ENetSocketOption option, int value) +{ + int result = SOCKET_ERROR; + switch (option) + { + case ENET_SOCKOPT_NONBLOCK: + { + u_long nonBlocking = (u_long) value; + result = ioctlsocket (socket, FIONBIO, & nonBlocking); + break; + } + + case ENET_SOCKOPT_BROADCAST: + result = setsockopt (socket, SOL_SOCKET, SO_BROADCAST, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_REUSEADDR: + result = setsockopt (socket, SOL_SOCKET, SO_REUSEADDR, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVBUF: + result = setsockopt (socket, SOL_SOCKET, SO_RCVBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDBUF: + result = setsockopt (socket, SOL_SOCKET, SO_SNDBUF, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_RCVTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_RCVTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_SNDTIMEO: + result = setsockopt (socket, SOL_SOCKET, SO_SNDTIMEO, (char *) & value, sizeof (int)); + break; + + case ENET_SOCKOPT_NODELAY: + result = setsockopt (socket, IPPROTO_TCP, TCP_NODELAY, (char *) & value, sizeof (int)); + break; + + default: + break; + } + return result == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_get_option (ENetSocket socket, ENetSocketOption option, int * value) +{ + int result = SOCKET_ERROR, len; + switch (option) + { + case ENET_SOCKOPT_ERROR: + len = sizeof(int); + result = getsockopt (socket, SOL_SOCKET, SO_ERROR, (char *) value, & len); + break; + + default: + break; + } + return result == SOCKET_ERROR ? -1 : 0; +} + +int +enet_socket_connect (ENetSocket socket, const ENetAddress * address) +{ + struct sockaddr_in sin; + int result; + + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + + result = connect (socket, (struct sockaddr *) & sin, sizeof (struct sockaddr_in)); + if (result == SOCKET_ERROR && WSAGetLastError () != WSAEWOULDBLOCK) + return -1; + + return 0; +} + +ENetSocket +enet_socket_accept (ENetSocket socket, ENetAddress * address) +{ + SOCKET result; + struct sockaddr_in sin; + int sinLength = sizeof (struct sockaddr_in); + + result = accept (socket, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL); + + if (result == INVALID_SOCKET) + return ENET_SOCKET_NULL; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return result; +} + +int +enet_socket_shutdown (ENetSocket socket, ENetSocketShutdown how) +{ + return shutdown (socket, (int) how) == SOCKET_ERROR ? -1 : 0; +} + +void +enet_socket_destroy (ENetSocket socket) +{ + if (socket != INVALID_SOCKET) + closesocket (socket); +} + +int +enet_socket_send (ENetSocket socket, + const ENetAddress * address, + const ENetBuffer * buffers, + size_t bufferCount) +{ + struct sockaddr_in sin; + DWORD sentLength; + + if (address != NULL) + { + memset (& sin, 0, sizeof (struct sockaddr_in)); + + sin.sin_family = AF_INET; + sin.sin_port = ENET_HOST_TO_NET_16 (address -> port); + sin.sin_addr.s_addr = address -> host; + } + + if (WSASendTo (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & sentLength, + 0, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? sizeof (struct sockaddr_in) : 0, + NULL, + NULL) == SOCKET_ERROR) + { + if (WSAGetLastError () == WSAEWOULDBLOCK) + return 0; + + return -1; + } + + return (int) sentLength; +} + +int +enet_socket_receive (ENetSocket socket, + ENetAddress * address, + ENetBuffer * buffers, + size_t bufferCount) +{ + INT sinLength = sizeof (struct sockaddr_in); + DWORD flags = 0, + recvLength; + struct sockaddr_in sin; + + if (WSARecvFrom (socket, + (LPWSABUF) buffers, + (DWORD) bufferCount, + & recvLength, + & flags, + address != NULL ? (struct sockaddr *) & sin : NULL, + address != NULL ? & sinLength : NULL, + NULL, + NULL) == SOCKET_ERROR) + { + switch (WSAGetLastError ()) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + return 0; + } + + return -1; + } + + if (flags & MSG_PARTIAL) + return -1; + + if (address != NULL) + { + address -> host = (enet_uint32) sin.sin_addr.s_addr; + address -> port = ENET_NET_TO_HOST_16 (sin.sin_port); + } + + return (int) recvLength; +} + +int +enet_socketset_select (ENetSocket maxSocket, ENetSocketSet * readSet, ENetSocketSet * writeSet, enet_uint32 timeout) +{ + struct timeval timeVal; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + return select (maxSocket + 1, readSet, writeSet, NULL, & timeVal); +} + +int +enet_socket_wait (ENetSocket socket, enet_uint32 * condition, enet_uint32 timeout) +{ + fd_set readSet, writeSet; + struct timeval timeVal; + int selectCount; + + timeVal.tv_sec = timeout / 1000; + timeVal.tv_usec = (timeout % 1000) * 1000; + + FD_ZERO (& readSet); + FD_ZERO (& writeSet); + + if (* condition & ENET_SOCKET_WAIT_SEND) + FD_SET (socket, & writeSet); + + if (* condition & ENET_SOCKET_WAIT_RECEIVE) + FD_SET (socket, & readSet); + + selectCount = select (socket + 1, & readSet, & writeSet, NULL, & timeVal); + + if (selectCount < 0) + return -1; + + * condition = ENET_SOCKET_WAIT_NONE; + + if (selectCount == 0) + return 0; + + if (FD_ISSET (socket, & writeSet)) + * condition |= ENET_SOCKET_WAIT_SEND; + + if (FD_ISSET (socket, & readSet)) + * condition |= ENET_SOCKET_WAIT_RECEIVE; + + return 0; +} + +#endif + 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 425436d907..469ceaac20 100644 --- a/modules/visual_script/visual_script.cpp +++ b/modules/visual_script/visual_script.cpp @@ -281,9 +281,10 @@ void VisualScript::_node_ports_changed(int p_id) { } } +#ifdef TOOLS_ENABLED set_edited(true); //something changed, let's set as edited emit_signal("node_ports_changed",function,p_id); - +#endif } void VisualScript::add_node(const StringName& p_func,int p_id, const Ref<VisualScriptNode>& p_node, const Point2 &p_pos) { @@ -991,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()) { @@ -1766,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 @@ -1870,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_editor.cpp b/modules/visual_script/visual_script_editor.cpp index 0d97126e0a..412865fbfe 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -6,6 +6,7 @@ #include "os/input.h" #include "os/keyboard.h" +#ifdef TOOLS_ENABLED class VisualScriptEditorSignalEdit : public Object { OBJ_TYPE(VisualScriptEditorSignalEdit,Object) @@ -2620,3 +2621,4 @@ void VisualScriptEditor::register_editor() { } +#endif diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index bd33f35739..252519913d 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -11,6 +11,7 @@ class VisualScriptEditorSignalEdit; class VisualScriptEditorVariableEdit; +#ifdef TOOLS_ENABLED class VisualScriptEditor : public ScriptEditorBase { OBJ_TYPE(VisualScriptEditor,ScriptEditorBase) @@ -180,5 +181,6 @@ public: VisualScriptEditor(); ~VisualScriptEditor(); }; +#endif #endif // VisualSCRIPT_EDITOR_H 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); } diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 320fb9d269..2414cee57e 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -237,8 +237,7 @@ def configure(env): env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) env.Append(CCFLAGS=['/DGLES2_ENABLED']) - - LIBS=['winmm','opengl32','dsound','kernel32','ole32','oleaut32','user32','gdi32', 'IPHLPAPI','Shlwapi', 'wsock32', 'shell32','advapi32','dinput8','dxguid'] + LIBS=['winmm','opengl32','dsound','kernel32','ole32','oleaut32','user32','gdi32', 'IPHLPAPI','Shlwapi', 'wsock32','Ws2_32', 'shell32','advapi32','dinput8','dxguid'] env.Append(LINKFLAGS=[p+env["LIBSUFFIX"] for p in LIBS]) env.Append(LIBPATH=[os.getenv("WindowsSdkDir")+"/Lib"]) @@ -361,7 +360,7 @@ def configure(env): env.Append(CCFLAGS=['-DWINDOWS_ENABLED','-mwindows']) env.Append(CPPFLAGS=['-DRTAUDIO_ENABLED']) env.Append(CCFLAGS=['-DGLES2_ENABLED']) - env.Append(LIBS=['mingw32','opengl32', 'dsound', 'ole32', 'd3d9','winmm','gdi32','iphlpapi','shlwapi','wsock32','kernel32', 'oleaut32', 'dinput8', 'dxguid']) + env.Append(LIBS=['mingw32','opengl32', 'dsound', 'ole32', 'd3d9','winmm','gdi32','iphlpapi','shlwapi','wsock32','ws2_32','kernel32', 'oleaut32', 'dinput8', 'dxguid']) # if (env["bits"]=="32"): # env.Append(LIBS=['gcc_s']) diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index b265ef840a..4756fdee26 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1768,7 +1768,7 @@ void TextEdit::_input_event(const InputEvent& p_input_event) { return; } - if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { + if (k.scancode==KEY_ENTER || k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { _confirm_completion(); accept_event(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index a53c19d2e7..086b69c1c5 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -36,6 +36,8 @@ #include "instance_placeholder.h" VARIANT_ENUM_CAST(Node::PauseMode); +VARIANT_ENUM_CAST(Node::NetworkMode); +VARIANT_ENUM_CAST(Node::RPCMode); @@ -77,6 +79,16 @@ void Node::_notification(int p_notification) { data.pause_owner=this; } + if (data.network_mode==NETWORK_MODE_INHERIT) { + + if (data.parent) + data.network_owner=data.parent->data.network_owner; + else + data.network_owner=NULL; + } else { + data.network_owner=this; + } + if (data.input) add_to_group("_vp_input"+itos(get_viewport()->get_instance_ID())); if (data.unhandled_input) @@ -97,6 +109,20 @@ void Node::_notification(int p_notification) { if (data.unhandled_key_input) remove_from_group("_vp_unhandled_key_input"+itos(get_viewport()->get_instance_ID())); + + data.pause_owner=NULL; + data.network_owner=NULL; + if (data.path_cache) { + memdelete(data.path_cache); + data.path_cache=NULL; + } + } break; + case NOTIFICATION_PATH_CHANGED: { + + if (data.path_cache) { + memdelete(data.path_cache); + data.path_cache=NULL; + } } break; case NOTIFICATION_READY: { @@ -412,6 +438,640 @@ void Node::_propagate_pause_owner(Node*p_owner) { } } +void Node::set_network_mode(NetworkMode p_mode) { + + if (data.network_mode==p_mode) + return; + + bool prev_inherits=data.network_mode==NETWORK_MODE_INHERIT; + data.network_mode=p_mode; + if (!is_inside_tree()) + return; //pointless + if ((data.network_mode==NETWORK_MODE_INHERIT) == prev_inherits) + return; ///nothing changed + + Node *owner=NULL; + + if (data.network_mode==NETWORK_MODE_INHERIT) { + + if (data.parent) + owner=data.parent->data.network_owner; + } else { + owner=this; + } + + _propagate_network_owner(owner); + + + +} + +Node::NetworkMode Node::get_network_mode() const { + + return data.network_mode; +} + +bool Node::is_network_master() const { + + ERR_FAIL_COND_V(!is_inside_tree(),false); + + switch(data.network_mode) { + case NETWORK_MODE_INHERIT: { + + if (data.network_owner) + return data.network_owner->is_network_master(); + else + return get_tree()->is_network_server(); + } break; + case NETWORK_MODE_MASTER: { + + return true; + } break; + case NETWORK_MODE_SLAVE: { + return false; + } break; + } + + return false; +} + + + +void Node::_propagate_network_owner(Node*p_owner) { + + if (data.network_mode!=NETWORK_MODE_INHERIT) + return; + data.network_owner=p_owner; + for(int i=0;i<data.children.size();i++) { + + data.children[i]->_propagate_network_owner(p_owner); + } +} + +/***** RPC CONFIG ********/ + +void Node::rpc_config(const StringName& p_method,RPCMode p_mode) { + + if (p_mode==RPC_MODE_DISABLED) { + data.rpc_methods.erase(p_method); + } else { + data.rpc_methods[p_method]=p_mode; + }; +} + +void Node::rset_config(const StringName& p_property,RPCMode p_mode) { + + if (p_mode==RPC_MODE_DISABLED) { + data.rpc_properties.erase(p_property); + } else { + data.rpc_properties[p_property]=p_mode; + }; +} + +/***** RPC FUNCTIONS ********/ + +void Node::rpc(const StringName& p_method,VARIANT_ARG_DECLARE) { + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;i<VARIANT_ARG_MAX;i++) { + if (argptr[i]->get_type()==Variant::NIL) + break; + argc++; + } + + rpcp(0,false,p_method,argptr,argc); +} + + +void Node::rpc_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_DECLARE) { + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;i<VARIANT_ARG_MAX;i++) { + if (argptr[i]->get_type()==Variant::NIL) + break; + argc++; + } + + rpcp(p_peer_id,false,p_method,argptr,argc); +} + + +void Node::rpc_unreliable(const StringName& p_method,VARIANT_ARG_DECLARE) { + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;i<VARIANT_ARG_MAX;i++) { + if (argptr[i]->get_type()==Variant::NIL) + break; + argc++; + } + + rpcp(0,true,p_method,argptr,argc); +} + + +void Node::rpc_unreliable_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_DECLARE) { + + VARIANT_ARGPTRS; + + int argc=0; + for(int i=0;i<VARIANT_ARG_MAX;i++) { + if (argptr[i]->get_type()==Variant::NIL) + break; + argc++; + } + + rpcp(p_peer_id,true,p_method,argptr,argc); +} + + +Variant Node::_rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<1) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + return Variant(); + } + + StringName method = *p_args[0]; + + rpcp(0,false,method,&p_args[1],p_argcount-1); + + r_error.error=Variant::CallError::CALL_OK; + return Variant(); +} + + +Variant Node::_rpc_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<2) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=2; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::INT) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::INT; + return Variant(); + } + + if (p_args[1]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=1; + r_error.expected=Variant::STRING; + return Variant(); + } + + int peer_id = *p_args[0]; + StringName method = *p_args[1]; + + rpcp(peer_id,false,method,&p_args[2],p_argcount-2); + + r_error.error=Variant::CallError::CALL_OK; + return Variant(); +} + + +Variant Node::_rpc_unreliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<1) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + return Variant(); + } + + StringName method = *p_args[0]; + + rpcp(0,true,method,&p_args[1],p_argcount-1); + + r_error.error=Variant::CallError::CALL_OK; + return Variant(); +} + + +Variant Node::_rpc_unreliable_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<2) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=2; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::INT) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::INT; + return Variant(); + } + + if (p_args[1]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=1; + r_error.expected=Variant::STRING; + return Variant(); + } + + int peer_id = *p_args[0]; + StringName method = *p_args[1]; + + rpcp(peer_id,true,method,&p_args[2],p_argcount-2); + + r_error.error=Variant::CallError::CALL_OK; + return Variant(); +} + +#if 0 +Variant Node::_rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + if (p_argcount<1) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + return Variant(); + } + + if (p_args[0]->get_type()!=Variant::STRING) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=0; + r_error.expected=Variant::STRING; + return Variant(); + } + + StringName method = *p_args[0]; + + rpcp(method,&p_args[1],p_argcount-1); + + r_error.error=Variant::CallError::CALL_OK; + return Variant(); +} + +#endif +void Node::rpcp(int p_peer_id,bool p_unreliable,const StringName& p_method,const Variant** p_arg,int p_argcount) { + + ERR_FAIL_COND(!is_inside_tree()); + + bool skip_rpc=false; + + if (p_peer_id==0 || p_peer_id==get_tree()->get_network_unique_id() || (p_peer_id<0 && p_peer_id!=-get_tree()->get_network_unique_id())) { + //check that send mode can use local call + + + bool call_local=false; + + + + Map<StringName,RPCMode>::Element *E = data.rpc_methods.find(p_method); + if (E) { + + switch(E->get()) { + + case RPC_MODE_DISABLED: { + //do nothing + } break; + case RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case RPC_MODE_SYNC: { + //call it, sync always results in call + call_local=true; + } break; + case RPC_MODE_MASTER: { + call_local=is_network_master(); + if (call_local) { + skip_rpc=true; //no other master so.. + } + } break; + case RPC_MODE_SLAVE: { + call_local=!is_network_master(); + } break; + + } + } + + + if (call_local) { + Variant::CallError ce; + call(p_method,p_arg,p_argcount,ce); + if (ce.error!=Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(this,p_method,p_arg,p_argcount,ce); + error="rpc() aborted in local call: - "+error; + ERR_PRINTS(error); + return; + } + } else if (get_script_instance()){ + //attempt with script + ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method); + + switch(rpc_mode) { + + case ScriptInstance::RPC_MODE_DISABLED: { + //do nothing + } break; + case ScriptInstance::RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case ScriptInstance::RPC_MODE_SYNC: { + //call it, sync always results in call + call_local=true; + } break; + case ScriptInstance::RPC_MODE_MASTER: { + call_local=is_network_master(); + if (call_local) { + skip_rpc=true; //no other master so.. + } + } break; + case ScriptInstance::RPC_MODE_SLAVE: { + call_local=!is_network_master(); + } break; + } + + if (call_local) { + Variant::CallError ce; + ce.error=Variant::CallError::CALL_OK; + get_script_instance()->call(p_method,p_arg,p_argcount,ce); + if (ce.error!=Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(this,p_method,p_arg,p_argcount,ce); + error="rpc() aborted in script local call: - "+error; + ERR_PRINTS(error); + return; + } + } + } + } + + if (skip_rpc) + return; + + + get_tree()->_rpc(this,p_peer_id,p_unreliable,false,p_method,p_arg,p_argcount); + +} + + +/******** RSET *********/ + + +void Node::rsetp(int p_peer_id,bool p_unreliable,const StringName& p_property,const Variant& p_value) { + + ERR_FAIL_COND(!is_inside_tree()); + + bool skip_rset=false; + + if (p_peer_id==0 || p_peer_id==get_tree()->get_network_unique_id() || (p_peer_id<0 && p_peer_id!=-get_tree()->get_network_unique_id())) { + //check that send mode can use local call + + + bool set_local=false; + + Map<StringName,RPCMode>::Element *E = data.rpc_properties.find(p_property); + if (E) { + + switch(E->get()) { + + case RPC_MODE_DISABLED: { + //do nothing + } break; + case RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case RPC_MODE_SYNC: { + //call it, sync always results in call + set_local=true; + } break; + case RPC_MODE_MASTER: { + set_local=is_network_master(); + if (set_local) { + skip_rset=true; + } + + } break; + case RPC_MODE_SLAVE: { + set_local=!is_network_master(); + } break; + + } + } + + + if (set_local) { + bool valid; + set(p_property,p_value,&valid); + + if (!valid) { + String error="rset() aborted in local set, property not found: - "+String(p_property); + ERR_PRINTS(error); + return; + } + } else if (get_script_instance()){ + //attempt with script + ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property); + + switch(rpc_mode) { + + case ScriptInstance::RPC_MODE_DISABLED: { + //do nothing + } break; + case ScriptInstance::RPC_MODE_REMOTE: { + //do nothing also, no need to call local + } break; + case ScriptInstance::RPC_MODE_SYNC: { + //call it, sync always results in call + set_local=true; + } break; + case ScriptInstance::RPC_MODE_MASTER: { + set_local=is_network_master(); + if (set_local) { + skip_rset=true; + } + } break; + case ScriptInstance::RPC_MODE_SLAVE: { + set_local=!is_network_master(); + } break; + } + + if (set_local) { + + bool valid = get_script_instance()->set(p_property,p_value); + + if (!valid) { + String error="rset() aborted in local script set, property not found: - "+String(p_property); + ERR_PRINTS(error); + return; + } + } + + } + } + + if (skip_rset) + return; + + const Variant*vptr = &p_value; + + get_tree()->_rpc(this,p_peer_id,p_unreliable,true,p_property,&vptr,1); + +} + + + +void Node::rset(const StringName& p_property,const Variant& p_value) { + + rsetp(0,false,p_property,p_value); + +} + +void Node::rset_id(int p_peer_id,const StringName& p_property,const Variant& p_value) { + + rsetp(p_peer_id,false,p_property,p_value); + +} + +void Node::rset_unreliable(const StringName& p_property,const Variant& p_value) { + + rsetp(0,true,p_property,p_value); + +} + +void Node::rset_unreliable_id(int p_peer_id,const StringName& p_property,const Variant& p_value) { + + rsetp(p_peer_id,true,p_property,p_value); + +} + +//////////// end of rpc + +bool Node::can_call_rpc(const StringName& p_method) const { + + const Map<StringName,RPCMode>::Element *E = data.rpc_methods.find(p_method); + if (E) { + + switch(E->get()) { + + case RPC_MODE_DISABLED: { + return false; + } break; + case RPC_MODE_REMOTE: { + return true; + } break; + case RPC_MODE_SYNC: { + return true; + } break; + case RPC_MODE_MASTER: { + return is_network_master(); + } break; + case RPC_MODE_SLAVE: { + return !is_network_master(); + } break; + } + } + + + if (get_script_instance()){ + //attempt with script + ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rpc_mode(p_method); + + switch(rpc_mode) { + + case ScriptInstance::RPC_MODE_DISABLED: { + return false; + } break; + case ScriptInstance::RPC_MODE_REMOTE: { + return true; + } break; + case ScriptInstance::RPC_MODE_SYNC: { + return true; + } break; + case ScriptInstance::RPC_MODE_MASTER: { + return is_network_master(); + } break; + case ScriptInstance::RPC_MODE_SLAVE: { + return !is_network_master(); + } break; + } + + } + + ERR_PRINTS("RPC on unauthorized method attempted: "+String(p_method)+" on base: "+String(Variant(this))); + return false; +} + +bool Node::can_call_rset(const StringName& p_property) const { + + const Map<StringName,RPCMode>::Element *E = data.rpc_properties.find(p_property); + if (E) { + + switch(E->get()) { + + case RPC_MODE_DISABLED: { + return false; + } break; + case RPC_MODE_REMOTE: { + return true; + } break; + case RPC_MODE_SYNC: { + return true; + } break; + case RPC_MODE_MASTER: { + return is_network_master(); + } break; + case RPC_MODE_SLAVE: { + return !is_network_master(); + } break; + } + } + + + if (get_script_instance()){ + //attempt with script + ScriptInstance::RPCMode rpc_mode = get_script_instance()->get_rset_mode(p_property); + + switch(rpc_mode) { + + case ScriptInstance::RPC_MODE_DISABLED: { + return false; + } break; + case ScriptInstance::RPC_MODE_REMOTE: { + return true; + } break; + case ScriptInstance::RPC_MODE_SYNC: { + return true; + } break; + case ScriptInstance::RPC_MODE_MASTER: { + return is_network_master(); + } break; + case ScriptInstance::RPC_MODE_SLAVE: { + return !is_network_master(); + } break; + } + + } + + ERR_PRINTS("RSET on unauthorized property attempted: "+String(p_property)+" on base: "+String(Variant(this))); + + return false; +} + + bool Node::can_process() const { ERR_FAIL_COND_V( !is_inside_tree(), false ); @@ -567,6 +1227,8 @@ void Node::set_name(const String& p_name) { data.parent->_validate_child_name(this); } + propagate_notification(NOTIFICATION_PATH_CHANGED); + if (is_inside_tree()) { emit_signal("renamed"); @@ -1210,6 +1872,10 @@ NodePath Node::get_path_to(const Node *p_node) const { NodePath Node::get_path() const { ERR_FAIL_COND_V(!is_inside_tree(),NodePath()); + + if (data.path_cache) + return *data.path_cache; + const Node *n = this; Vector<StringName> path; @@ -1221,7 +1887,9 @@ NodePath Node::get_path() const { path.invert(); - return NodePath( path, true ); + data.path_cache = memnew( NodePath( path, true ) ); + + return *data.path_cache; } bool Node::is_in_group(const StringName& p_identifier) const { @@ -2212,7 +2880,13 @@ void Node::_bind_methods() { ObjectTypeDB::bind_method(_MD("queue_free"),&Node::queue_delete); + ObjectTypeDB::bind_method(_MD("set_network_mode","mode"),&Node::set_network_mode); + ObjectTypeDB::bind_method(_MD("get_network_mode"),&Node::get_network_mode); + + ObjectTypeDB::bind_method(_MD("is_network_master"),&Node::is_network_master); + ObjectTypeDB::bind_method(_MD("rpc_config","method","mode"),&Node::rpc_config); + ObjectTypeDB::bind_method(_MD("rset_config","property","mode"),&Node::rset_config); #ifdef TOOLS_ENABLED @@ -2222,6 +2896,33 @@ void Node::_bind_methods() { #endif + { + MethodInfo mi; + + mi.arguments.push_back( PropertyInfo( Variant::STRING, "method")); + + + mi.name="rpc"; + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"rpc",&Node::_rpc_bind,mi); + mi.name="rpc_unreliable"; + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"rpc_unreliable",&Node::_rpc_unreliable_bind,mi); + + mi.arguments.push_front( PropertyInfo( Variant::INT, "peer_") ); + + mi.name="rpc_id"; + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"rpc_id",&Node::_rpc_id_bind,mi); + mi.name="rpc_unreliable_id"; + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"rpc_unreliable_id",&Node::_rpc_unreliable_id_bind,mi); + + + } + + ObjectTypeDB::bind_method(_MD("rset","property","value:Variant"),&Node::rset); + ObjectTypeDB::bind_method(_MD("rset_id","peer_id","property","value:Variant"),&Node::rset_id); + ObjectTypeDB::bind_method(_MD("rset_unreliable","property","value:Variant"),&Node::rset_unreliable); + ObjectTypeDB::bind_method(_MD("rset_unreliable_id","peer_id","property","value:Variant"),&Node::rset_unreliable_id); + + BIND_CONSTANT( NOTIFICATION_ENTER_TREE ); BIND_CONSTANT( NOTIFICATION_EXIT_TREE ); BIND_CONSTANT( NOTIFICATION_MOVED_IN_PARENT ); @@ -2236,8 +2937,18 @@ void Node::_bind_methods() { BIND_CONSTANT( NOTIFICATION_INSTANCED ); BIND_CONSTANT( NOTIFICATION_DRAG_BEGIN ); BIND_CONSTANT( NOTIFICATION_DRAG_END ); + BIND_CONSTANT( NOTIFICATION_PATH_CHANGED); + BIND_CONSTANT( NETWORK_MODE_INHERIT ); + BIND_CONSTANT( NETWORK_MODE_MASTER ); + BIND_CONSTANT( NETWORK_MODE_SLAVE ); + + BIND_CONSTANT( RPC_MODE_DISABLED ); + BIND_CONSTANT( RPC_MODE_REMOTE ); + BIND_CONSTANT( RPC_MODE_SYNC ); + BIND_CONSTANT( RPC_MODE_MASTER ); + BIND_CONSTANT( RPC_MODE_SLAVE ); BIND_CONSTANT( PAUSE_MODE_INHERIT ); BIND_CONSTANT( PAUSE_MODE_STOP ); @@ -2286,11 +2997,15 @@ Node::Node() { data.unhandled_key_input=false; data.pause_mode=PAUSE_MODE_INHERIT; data.pause_owner=NULL; + data.network_mode=NETWORK_MODE_INHERIT; + data.network_owner=NULL; + data.path_cache=NULL; data.parent_owned=false; data.in_constructor=true; data.viewport=NULL; data.use_placeholder=false; data.display_folded=false; + } Node::~Node() { diff --git a/scene/main/node.h b/scene/main/node.h index 18e403cd61..3568da2ab0 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -53,6 +53,21 @@ public: PAUSE_MODE_PROCESS }; + enum NetworkMode { + + NETWORK_MODE_INHERIT, + NETWORK_MODE_MASTER, + NETWORK_MODE_SLAVE + }; + + enum RPCMode { + + RPC_MODE_DISABLED, //no rpc for this method, calls to this will be blocked (default) + RPC_MODE_REMOTE, // using rpc() on it will call method / set property in all other peers + RPC_MODE_SYNC, // using rpc() on it will call method / set property in all other peers and locally + RPC_MODE_MASTER, // usinc rpc() on it will call method on wherever the master is, be it local or remote + RPC_MODE_SLAVE, // usinc rpc() on it will call method for all slaves, be it local or remote + }; struct Comparator { @@ -68,6 +83,8 @@ private: GroupData() { persistent=false; } }; + + struct Data { String filename; @@ -98,6 +115,13 @@ private: PauseMode pause_mode; Node *pause_owner; + + NetworkMode network_mode; + Node *network_owner; + Map<StringName,RPCMode> rpc_methods; + Map<StringName,RPCMode> rpc_properties; + + // variables used to properly sort the node when processing, ignored otherwise //should move all the stuff below to bits bool fixed_process; @@ -113,6 +137,8 @@ private: bool display_folded; + mutable NodePath *path_cache; + } data; @@ -134,6 +160,7 @@ private: void _propagate_validate_owner(); void _print_stray_nodes(); void _propagate_pause_owner(Node*p_owner); + void _propagate_network_owner(Node*p_owner); Array _get_node_and_resource(const NodePath& p_path); void _duplicate_signals(const Node* p_original,Node* p_copy) const; @@ -143,6 +170,11 @@ private: Array _get_children() const; Array _get_groups() const; + Variant _rpc_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + Variant _rpc_unreliable_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + Variant _rpc_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + Variant _rpc_unreliable_id_bind(const Variant** p_args, int p_argcount, Variant::CallError& r_error); + friend class SceneTree; void _set_tree(SceneTree *p_tree); @@ -186,6 +218,7 @@ public: NOTIFICATION_INSTANCED=20, NOTIFICATION_DRAG_BEGIN=21, NOTIFICATION_DRAG_END=22, + NOTIFICATION_PATH_CHANGED=23, }; /* NODE/TREE */ @@ -331,7 +364,32 @@ public: void set_display_folded(bool p_folded); bool is_displayed_folded() const; - /* CANVAS */ + /* NETWORK */ + + void set_network_mode(NetworkMode p_mode); + NetworkMode get_network_mode() const; + bool is_network_master() const; + + void rpc_config(const StringName& p_method,RPCMode p_mode); // config a local method for RPC + void rset_config(const StringName& p_property,RPCMode p_mode); // config a local property for RPC + + void rpc(const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode + void rpc_unreliable(const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode + void rpc_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode + void rpc_unreliable_id(int p_peer_id,const StringName& p_method,VARIANT_ARG_LIST); //rpc call, honors RPCMode + + void rpcp(int p_peer_id,bool p_unreliable,const StringName& p_method,const Variant** p_arg,int p_argcount); + + void rset(const StringName& p_property, const Variant& p_value); //remote set call, honors RPCMode + void rset_unreliable(const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode + void rset_id(int p_peer_id,const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode + void rset_unreliable_id(int p_peer_id,const StringName& p_property,const Variant& p_value); //remote set call, honors RPCMode + + void rsetp(int p_peer_id,bool p_unreliable,const StringName& p_property,const Variant& p_value); + + bool can_call_rpc(const StringName& p_method) const; + bool can_call_rset(const StringName& p_property) const; + Node(); ~Node(); diff --git a/scene/main/scene_main_loop.cpp b/scene/main/scene_main_loop.cpp index 77156203b8..e161bd7334 100644 --- a/scene/main/scene_main_loop.cpp +++ b/scene/main/scene_main_loop.cpp @@ -545,6 +545,8 @@ bool SceneTree::idle(float p_time){ idle_process_time=p_time; + _network_poll(); + emit_signal("idle_frame"); _flush_transform_notifications(); @@ -1655,6 +1657,401 @@ Ref<SceneTreeTimer> SceneTree::create_timer(float p_delay_sec) { return stt; } +void SceneTree::_network_peer_connected(int p_id) { + + + connected_peers.insert(p_id); + path_get_cache.insert(p_id,PathGetCache()); + + + emit_signal("network_peer_connected",p_id); +} + +void SceneTree::_network_peer_disconnected(int p_id) { + + connected_peers.erase(p_id); + path_get_cache.erase(p_id); //I no longer need your cache, sorry + emit_signal("network_peer_disconnected",p_id); +} + +void SceneTree::_connected_to_server() { + + emit_signal("connected_to_server"); +} + +void SceneTree::_connection_failed() { + + emit_signal("connection_failed"); +} + +void SceneTree::_server_disconnected() { + + emit_signal("server_disconnected"); +} + +void SceneTree::set_network_peer(const Ref<NetworkedMultiplayerPeer>& p_network_peer) { + if (network_peer.is_valid()) { + network_peer->disconnect("peer_connected",this,"_network_peer_connected"); + network_peer->disconnect("peer_disconnected",this,"_network_peer_disconnected"); + network_peer->disconnect("connection_succeeded",this,"_connected_to_server"); + network_peer->disconnect("connection_failed",this,"_connection_failed"); + network_peer->disconnect("server_disconnected",this,"_server_disconnected"); + connected_peers.clear(); + path_get_cache.clear(); + path_send_cache.clear(); + last_send_cache_id=1; + } + + ERR_EXPLAIN("Supplied NetworkedNetworkPeer must be connecting or connected."); + ERR_FAIL_COND(p_network_peer.is_valid() && p_network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED); + + network_peer=p_network_peer; + + if (network_peer.is_valid()) { + network_peer->connect("peer_connected",this,"_network_peer_connected"); + network_peer->connect("peer_disconnected",this,"_network_peer_disconnected"); + network_peer->connect("connection_succeeded",this,"_connected_to_server"); + network_peer->connect("connection_failed",this,"_connection_failed"); + network_peer->connect("server_disconnected",this,"_server_disconnected"); + } +} + +bool SceneTree::is_network_server() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(),false); + return network_peer->is_server(); + +} + +int SceneTree::get_network_unique_id() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(),0); + return network_peer->get_unique_id(); +} + +void SceneTree::set_refuse_new_network_connections(bool p_refuse) { + ERR_FAIL_COND(!network_peer.is_valid()); + network_peer->set_refuse_new_connections(p_refuse); +} + +bool SceneTree::is_refusing_new_network_connections() const { + + ERR_FAIL_COND_V(!network_peer.is_valid(),false); + + return network_peer->is_refusing_new_connections(); + +} + + +void SceneTree::_rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const StringName& p_name,const Variant** p_arg,int p_argcount) { + + if (network_peer.is_null()) { + ERR_EXPLAIN("Attempt to remote call/set when networking is not active in SceneTree."); + ERR_FAIL(); + } + + if (network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_CONNECTING) { + ERR_EXPLAIN("Attempt to remote call/set when networking is not connected yet in SceneTree."); + ERR_FAIL(); + } + + if (network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { + ERR_EXPLAIN("Attempt to remote call/set when networking is disconnected."); + ERR_FAIL(); + } + + if (p_to!=0 && !connected_peers.has(ABS(p_to))) { + if (p_to==get_network_unique_id()) { + ERR_EXPLAIN("Attempt to remote call/set yourself! unique ID: "+itos(get_network_unique_id())); + } else { + ERR_EXPLAIN("Attempt to remote call unexisting ID: "+itos(p_to)); + + } + + ERR_FAIL(); + } + + NodePath from_path = p_from->get_path(); + ERR_FAIL_COND(from_path.is_empty()); + + //create base packet + Array message; + + message.resize(3+p_argcount); //alloc size for args + + //set message type + if (p_set) { + message[0]=NETWORK_COMMAND_REMOTE_SET; + } else { + message[0]=NETWORK_COMMAND_REMOTE_CALL; + } + + //set message name + message[2]=p_name; + + //set message args + for(int i=0;i<p_argcount;i++) { + message[3+i]=*p_arg[i]; + } + + //see if the path is cached + PathSentCache *psc = path_send_cache.getptr(from_path); + if (!psc) { + //path is not cached, create + path_send_cache[from_path]=PathSentCache(); + psc = path_send_cache.getptr(from_path); + psc->id=last_send_cache_id++; + + } + + //see if all peers have cached path (is so, call can be fast) + bool has_all_peers=true; + + List<int> peers_to_add; //if one is missing, take note to add it + + for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) { + + if (p_to<0 && E->get()==-p_to) + continue; //continue, excluded + + if (p_to>0 && E->get()!=p_to) + continue; //continue, not for this peer + + Map<int,bool>::Element *F = psc->confirmed_peers.find(E->get()); + + if (!F || F->get()==false) { + //path was not cached, or was cached but is unconfirmed + if (!F) { + //not cached at all, take note + peers_to_add.push_back(E->get()); + } + + has_all_peers=false; + } + } + + //those that need to be added, send a message for this + + 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; + + network_peer->set_target_peer(E->get()); //to all of you + network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->put_var(add_path_message); //a message with love + + psc->confirmed_peers.insert(E->get(),false); //insert into confirmed, but as false since it was not confirmed + } + + //take chance and set transfer mode, since all send methods will use it + network_peer->set_transfer_mode(p_unreliable ? NetworkedMultiplayerPeer::TRANSFER_MODE_UNRELIABLE : NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + + if (has_all_peers) { + + //they all have verified paths, so send fast + message[1]=psc->id; + + network_peer->set_target_peer(p_to); //to all of you + network_peer->put_var(message); //a message with love + } else { + //not all verified path, so send one by one + for (Set<int>::Element *E=connected_peers.front();E;E=E->next()) { + + if (p_to<0 && E->get()==-p_to) + continue; //continue, excluded + + if (p_to>0 && E->get()!=p_to) + continue; //continue, not for this peer + + Map<int,bool>::Element *F = psc->confirmed_peers.find(E->get()); + ERR_CONTINUE(!F);//should never happen + + network_peer->set_target_peer(E->get()); //to this one specifically + + if (F->get()==true) { + //this one confirmed path, so use id + message[1]=psc->id; + } else { + //this one did not confirm path yet, so use entire path (sorry!) + message[1]=from_path; + } + + network_peer->put_var(message); + } + } +} + + +void SceneTree::_network_process_packet(int p_from, const Array& p_packet) { + + ERR_FAIL_COND(p_packet.empty()); + + int packet_type = p_packet[0]; + + switch(packet_type) { + + case NETWORK_COMMAND_REMOTE_CALL: + case NETWORK_COMMAND_REMOTE_SET: { + + ERR_FAIL_COND(p_packet.size()<3); + Variant target = p_packet[1]; + Node* node=NULL; + + if (target.get_type()==Variant::NODE_PATH) { + NodePath np = target; + node = get_root()->get_node(np); + if (node==NULL) { + ERR_EXPLAIN("Failed to get path from RPC: "+String(np)); + ERR_FAIL_COND(node==NULL); + } + } else if (target.get_type()==Variant::INT) { + + int id = target; + + Map<int,PathGetCache>::Element *E=path_get_cache.find(p_from); + ERR_FAIL_COND(!E); + + Map<int,PathGetCache::NodeInfo>::Element *F=E->get().nodes.find(id); + ERR_FAIL_COND(!F); + + PathGetCache::NodeInfo *ni = &F->get(); + //do proper caching later + + node = get_root()->get_node(ni->path); + if (node==NULL) { + ERR_EXPLAIN("Failed to get cached path from RPC: "+String(ni->path)); + ERR_FAIL_COND(node==NULL); + } + + + } else { + ERR_FAIL(); + } + + StringName name = p_packet[2]; + + if (packet_type==NETWORK_COMMAND_REMOTE_CALL) { + + if (!node->can_call_rpc(name)) + return; + + int argc = p_packet.size()-3; + Vector<Variant> args; + Vector<const Variant*> argp; + args.resize(argc); + argp.resize(argc); + + for(int i=0;i<argc;i++) { + args[i]=p_packet[3+i]; + argp[i]=&args[i]; + } + + Variant::CallError ce; + + node->call(name,argp.ptr(),argc,ce); + if (ce.error!=Variant::CallError::CALL_OK) { + String error = Variant::get_call_error_text(node,name,argp.ptr(),argc,ce); + error="RPC - "+error; + ERR_PRINTS(error); + } + + } else { + + if (!node->can_call_rset(name)) + return; + + + ERR_FAIL_COND(p_packet.size()!=4); + Variant value = p_packet[3]; + bool valid; + + node->set(name,value,&valid); + if (!valid) { + String error = "Error setting remote property '"+String(name)+"', not found in object of type "+node->get_type(); + ERR_PRINTS(error); + } + } + + } break; + case NETWORK_COMMAND_SIMPLIFY_PATH: { + + ERR_FAIL_COND(p_packet.size()!=3); + NodePath path = p_packet[1]; + int id = p_packet[2]; + + if (!path_get_cache.has(p_from)) { + path_get_cache[p_from]=PathGetCache(); + } + + PathGetCache::NodeInfo ni; + ni.path=path; + ni.instance=0; + + 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); + } break; + case NETWORK_COMMAND_CONFIRM_PATH: { + + ERR_FAIL_COND(p_packet.size()!=2); + NodePath path = p_packet[1]; + + PathSentCache *psc = path_send_cache.getptr(path); + ERR_FAIL_COND(!psc); + + Map<int,bool>::Element *E=psc->confirmed_peers.find(p_from); + ERR_FAIL_COND(!E); + E->get()=true; + } break; + } + +} + +void SceneTree::_network_poll() { + + if (!network_peer.is_valid() || network_peer->get_connection_status()==NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) + return; + + network_peer->poll(); + + if (!network_peer.is_valid()) //it's possible that polling might have resulted in a disconnection, so check here + return; + + while(network_peer->get_available_packet_count()) { + + int sender = network_peer->get_packet_peer(); + Variant packet; + Error err = network_peer->get_var(packet); + if (err!=OK) { + ERR_PRINT("Error getting packet!"); + } + if (packet.get_type()!=Variant::ARRAY) { + + ERR_PRINT("Error getting packet! (not an array)"); + } + + _network_process_packet(sender,packet); + + if (!network_peer.is_valid()) { + break; //it's also possible that a packet or RPC caused a disconnection, so also check here + } + } + + +} + + void SceneTree::_bind_methods() { @@ -1722,6 +2119,18 @@ void SceneTree::_bind_methods() { ObjectTypeDB::bind_method(_MD("_change_scene"),&SceneTree::_change_scene); + + ObjectTypeDB::bind_method(_MD("set_network_peer","peer:NetworkedMultiplayerPeer"),&SceneTree::set_network_peer); + ObjectTypeDB::bind_method(_MD("is_network_server"),&SceneTree::is_network_server); + ObjectTypeDB::bind_method(_MD("get_network_unique_id"),&SceneTree::get_network_unique_id); + ObjectTypeDB::bind_method(_MD("set_refuse_new_network_connections","refuse"),&SceneTree::set_refuse_new_network_connections); + ObjectTypeDB::bind_method(_MD("is_refusing_new_network_connections"),&SceneTree::is_refusing_new_network_connections); + ObjectTypeDB::bind_method(_MD("_network_peer_connected"),&SceneTree::_network_peer_connected); + ObjectTypeDB::bind_method(_MD("_network_peer_disconnected"),&SceneTree::_network_peer_disconnected); + ObjectTypeDB::bind_method(_MD("_connected_to_server"),&SceneTree::_connected_to_server); + ObjectTypeDB::bind_method(_MD("_connection_failed"),&SceneTree::_connection_failed); + ObjectTypeDB::bind_method(_MD("_server_disconnected"),&SceneTree::_server_disconnected); + ADD_SIGNAL( MethodInfo("tree_changed") ); ADD_SIGNAL( MethodInfo("node_removed",PropertyInfo( Variant::OBJECT, "node") ) ); ADD_SIGNAL( MethodInfo("screen_resized") ); @@ -1731,6 +2140,11 @@ void SceneTree::_bind_methods() { ADD_SIGNAL( MethodInfo("fixed_frame")); ADD_SIGNAL( MethodInfo("files_dropped",PropertyInfo(Variant::STRING_ARRAY,"files"),PropertyInfo(Variant::INT,"screen")) ); + ADD_SIGNAL( MethodInfo("network_peer_connected",PropertyInfo(Variant::INT,"id"))); + ADD_SIGNAL( MethodInfo("network_peer_disconnected",PropertyInfo(Variant::INT,"id"))); + ADD_SIGNAL( MethodInfo("connected_to_server")); + ADD_SIGNAL( MethodInfo("connection_failed")); + ADD_SIGNAL( MethodInfo("server_disconnected")); BIND_CONSTANT( GROUP_CALL_DEFAULT ); BIND_CONSTANT( GROUP_CALL_REVERSE ); @@ -1831,6 +2245,8 @@ SceneTree::SceneTree() { live_edit_root=NodePath("/root"); + last_send_cache_id=1; + #endif diff --git a/scene/main/scene_main_loop.h b/scene/main/scene_main_loop.h index 6129a12446..652a835fd5 100644 --- a/scene/main/scene_main_loop.h +++ b/scene/main/scene_main_loop.h @@ -35,6 +35,9 @@ #include "scene/resources/world_2d.h" #include "os/thread_safe.h" #include "self_list.h" +#include "io/networked_multiplayer_peer.h" + + /** @author Juan Linietsky <reduzio@gmail.com> */ @@ -173,9 +176,58 @@ private: List<Ref<SceneTreeTimer> > timers; + + ///network/// + + enum NetworkCommands { + NETWORK_COMMAND_REMOTE_CALL, + NETWORK_COMMAND_REMOTE_SET, + NETWORK_COMMAND_SIMPLIFY_PATH, + NETWORK_COMMAND_CONFIRM_PATH, + }; + + Ref<NetworkedMultiplayerPeer> network_peer; + + Set<int> connected_peers; + void _network_peer_connected(int p_id); + void _network_peer_disconnected(int p_id); + + void _connected_to_server(); + void _connection_failed(); + void _server_disconnected(); + + //path sent caches + struct PathSentCache { + Map<int,bool> confirmed_peers; + int id; + }; + + HashMap<NodePath,PathSentCache> path_send_cache; + int last_send_cache_id; + + //path get caches + struct PathGetCache { + struct NodeInfo { + NodePath path; + ObjectID instance; + }; + + Map<int,NodeInfo> nodes; + }; + + Map<int,PathGetCache> path_get_cache; + + void _network_process_packet(int p_from, const Array& p_packet); + void _network_poll(); + static SceneTree *singleton; friend class Node; + + + + void _rpc(Node* p_from,int p_to,bool p_unreliable,bool p_set,const StringName& p_name,const Variant** p_arg,int p_argcount); + void tree_changed(); void node_removed(Node *p_node); @@ -251,6 +303,7 @@ friend class Viewport; #endif protected: + void _notification(int p_notification); static void _bind_methods(); @@ -366,6 +419,15 @@ public: void drop_files(const Vector<String>& p_files,int p_from_screen=0); + //network API + + void set_network_peer(const Ref<NetworkedMultiplayerPeer>& p_network_peer); + bool is_network_server() const; + int get_network_unique_id() const; + + void set_refuse_new_network_connections(bool p_refuse); + bool is_refusing_new_network_connections() const; + SceneTree(); ~SceneTree(); diff --git a/tools/editor/editor_plugin.cpp b/tools/editor/editor_plugin.cpp index 0d162cbe56..5e671549ef 100644 --- a/tools/editor/editor_plugin.cpp +++ b/tools/editor/editor_plugin.cpp @@ -211,15 +211,22 @@ void EditorPlugin::clear() { } -void EditorPlugin::save_external_data() {} // if editor references external resources/scenes, save them +// if editor references external resources/scenes, save them +void EditorPlugin::save_external_data() { + + if (get_script_instance() && get_script_instance()->has_method("save_external_data")) { + get_script_instance()->call("save_external_data"); + } +} + +// if changes are pending in editor, apply them void EditorPlugin::apply_changes() { if (get_script_instance() && get_script_instance()->has_method("apply_changes")) { get_script_instance()->call("apply_changes"); } +} - -} // if changes are pending in editor, apply them void EditorPlugin::get_breakpoints(List<String> *p_breakpoints) { if (get_script_instance() && get_script_instance()->has_method("get_breakpoints")) { @@ -239,10 +246,21 @@ void EditorPlugin::save_global_state() {} void EditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) { + if (get_script_instance() && get_script_instance()->has_method("set_window_layout")) { + get_script_instance()->call("set_window_layout", p_layout); + } } void EditorPlugin::get_window_layout(Ref<ConfigFile> p_layout){ + if (get_script_instance() && get_script_instance()->has_method("get_window_layout")) { + get_script_instance()->call("get_window_layout", p_layout); + } +} + +void EditorPlugin::queue_save_layout() const { + + EditorNode::get_singleton()->save_layout(); } EditorSelection* EditorPlugin::get_selection() { @@ -302,6 +320,7 @@ void EditorPlugin::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_undo_redo:UndoRedo"),&EditorPlugin::_get_undo_redo); ObjectTypeDB::bind_method(_MD("get_selection:EditorSelection"),&EditorPlugin::get_selection); ObjectTypeDB::bind_method(_MD("get_editor_settings:EditorSettings"),&EditorPlugin::get_editor_settings); + ObjectTypeDB::bind_method(_MD("queue_save_layout"),&EditorPlugin::queue_save_layout); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo(Variant::BOOL,"forward_input_event",PropertyInfo(Variant::INPUT_EVENT,"event"))); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo(Variant::BOOL,"forward_spatial_input_event",PropertyInfo(Variant::OBJECT,"camera",PROPERTY_HINT_RESOURCE_TYPE,"Camera"),PropertyInfo(Variant::INPUT_EVENT,"event"))); @@ -317,8 +336,11 @@ void EditorPlugin::_bind_methods() { ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo(Variant::DICTIONARY,"get_state")); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("set_state",PropertyInfo(Variant::DICTIONARY,"state"))); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("clear")); + ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("save_external_data")); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("apply_changes")); ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo(Variant::STRING_ARRAY,"get_breakpoints")); + ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("set_window_layout",PropertyInfo(Variant::OBJECT,"layout",PROPERTY_HINT_RESOURCE_TYPE,"ConfigFile"))); + ObjectTypeDB::add_virtual_method(get_type_static(),MethodInfo("get_window_layout",PropertyInfo(Variant::OBJECT,"layout",PROPERTY_HINT_RESOURCE_TYPE,"ConfigFile"))); BIND_CONSTANT( CONTAINER_TOOLBAR ); BIND_CONSTANT( CONTAINER_SPATIAL_EDITOR_MENU ); diff --git a/tools/editor/editor_plugin.h b/tools/editor/editor_plugin.h index b960a7d5af..9a9c32357d 100644 --- a/tools/editor/editor_plugin.h +++ b/tools/editor/editor_plugin.h @@ -118,6 +118,7 @@ public: virtual void get_window_layout(Ref<ConfigFile> p_layout); virtual void edited_scene_changed(){}; // if changes are pending in editor, apply them + void queue_save_layout() const; Control *get_base_control(); diff --git a/tools/editor/icons/2x/icon_capsule_shape_2d.png b/tools/editor/icons/2x/icon_capsule_shape_2d.png Binary files differindex 7f4734618b..e449ce4985 100644 --- a/tools/editor/icons/2x/icon_capsule_shape_2d.png +++ b/tools/editor/icons/2x/icon_capsule_shape_2d.png diff --git a/tools/editor/icons/2x/icon_circle_shape_2d.png b/tools/editor/icons/2x/icon_circle_shape_2d.png Binary files differindex 5975c90f91..feb84d2f1c 100644 --- a/tools/editor/icons/2x/icon_circle_shape_2d.png +++ b/tools/editor/icons/2x/icon_circle_shape_2d.png diff --git a/tools/editor/icons/2x/icon_concave_polygon_shape_2d.png b/tools/editor/icons/2x/icon_concave_polygon_shape_2d.png Binary files differindex 39e4d888af..1ad3f30950 100644 --- a/tools/editor/icons/2x/icon_concave_polygon_shape_2d.png +++ b/tools/editor/icons/2x/icon_concave_polygon_shape_2d.png diff --git a/tools/editor/icons/2x/icon_convex_polygon_shape_2d.png b/tools/editor/icons/2x/icon_convex_polygon_shape_2d.png Binary files differindex 38ed87089d..6d46a96f9d 100644 --- a/tools/editor/icons/2x/icon_convex_polygon_shape_2d.png +++ b/tools/editor/icons/2x/icon_convex_polygon_shape_2d.png diff --git a/tools/editor/icons/2x/icon_line_shape_2d.png b/tools/editor/icons/2x/icon_line_shape_2d.png Binary files differindex 5ec406ea1f..490db5ca5b 100644 --- a/tools/editor/icons/2x/icon_line_shape_2d.png +++ b/tools/editor/icons/2x/icon_line_shape_2d.png diff --git a/tools/editor/icons/2x/icon_ray_shape_2d.png b/tools/editor/icons/2x/icon_ray_shape_2d.png Binary files differindex 37eedfb7de..2dc7041a93 100644 --- a/tools/editor/icons/2x/icon_ray_shape_2d.png +++ b/tools/editor/icons/2x/icon_ray_shape_2d.png diff --git a/tools/editor/icons/2x/icon_rectangle_shape_2d.png b/tools/editor/icons/2x/icon_rectangle_shape_2d.png Binary files differindex a2754e6880..51a93cdb1d 100644 --- a/tools/editor/icons/2x/icon_rectangle_shape_2d.png +++ b/tools/editor/icons/2x/icon_rectangle_shape_2d.png diff --git a/tools/editor/icons/2x/icon_segment_shape_2d.png b/tools/editor/icons/2x/icon_segment_shape_2d.png Binary files differindex 11b28d81df..43d5d837cc 100644 --- a/tools/editor/icons/2x/icon_segment_shape_2d.png +++ b/tools/editor/icons/2x/icon_segment_shape_2d.png diff --git a/tools/editor/icons/icon_capsule_shape_2d.png b/tools/editor/icons/icon_capsule_shape_2d.png Binary files differindex f4f990f72f..6f6554fbc7 100644 --- a/tools/editor/icons/icon_capsule_shape_2d.png +++ b/tools/editor/icons/icon_capsule_shape_2d.png diff --git a/tools/editor/icons/icon_circle_shape_2d.png b/tools/editor/icons/icon_circle_shape_2d.png Binary files differindex 281658b062..7865ed3dbe 100644 --- a/tools/editor/icons/icon_circle_shape_2d.png +++ b/tools/editor/icons/icon_circle_shape_2d.png diff --git a/tools/editor/icons/icon_concave_polygon_shape_2d.png b/tools/editor/icons/icon_concave_polygon_shape_2d.png Binary files differindex 71d8e62dbc..2e87eea5aa 100644 --- a/tools/editor/icons/icon_concave_polygon_shape_2d.png +++ b/tools/editor/icons/icon_concave_polygon_shape_2d.png diff --git a/tools/editor/icons/icon_convex_polygon_shape_2d.png b/tools/editor/icons/icon_convex_polygon_shape_2d.png Binary files differindex 283bc68786..e449c6930f 100644 --- a/tools/editor/icons/icon_convex_polygon_shape_2d.png +++ b/tools/editor/icons/icon_convex_polygon_shape_2d.png diff --git a/tools/editor/icons/icon_line_shape_2d.png b/tools/editor/icons/icon_line_shape_2d.png Binary files differindex a4fb049f3f..e31722d69c 100644 --- a/tools/editor/icons/icon_line_shape_2d.png +++ b/tools/editor/icons/icon_line_shape_2d.png diff --git a/tools/editor/icons/icon_ray_shape_2d.png b/tools/editor/icons/icon_ray_shape_2d.png Binary files differindex 2f102bbb91..c91a63570d 100644 --- a/tools/editor/icons/icon_ray_shape_2d.png +++ b/tools/editor/icons/icon_ray_shape_2d.png diff --git a/tools/editor/icons/icon_rectangle_shape_2d.png b/tools/editor/icons/icon_rectangle_shape_2d.png Binary files differindex eb123c6f6a..002730b942 100644 --- a/tools/editor/icons/icon_rectangle_shape_2d.png +++ b/tools/editor/icons/icon_rectangle_shape_2d.png diff --git a/tools/editor/icons/icon_segment_shape_2d.png b/tools/editor/icons/icon_segment_shape_2d.png Binary files differindex c90255a405..8f3771be7a 100644 --- a/tools/editor/icons/icon_segment_shape_2d.png +++ b/tools/editor/icons/icon_segment_shape_2d.png diff --git a/tools/editor/icons/source/icon_capsule_shape_2d.svg b/tools/editor/icons/source/icon_capsule_shape_2d.svg index 03ac3f8255..13c6648368 100644 --- a/tools/editor/icons/source/icon_capsule_shape_2d.svg +++ b/tools/editor/icons/source/icon_capsule_shape_2d.svg @@ -40,8 +40,8 @@ showgrid="true" inkscape:current-layer="layer1" inkscape:document-units="px" - inkscape:cy="10.812487" - inkscape:cx="8.3752367" + inkscape:cy="10.749987" + inkscape:cx="2.8752365" inkscape:zoom="31.999999" inkscape:pageshadow="2" inkscape:pageopacity="0.0" @@ -73,7 +73,7 @@ inkscape:groupmode="layer" inkscape:label="Layer 1"> <path - style="opacity:1;fill:#a5b7f4;fill-opacity:0.98823529;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + style="opacity:1;fill:#68b6ff;fill-opacity:1;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" d="M 8 1 A 4 4 0 0 0 4 5 L 4 11 A 4 4 0 0 0 8 15 A 4 4 0 0 0 12 11 L 12 5 A 4 4 0 0 0 8 1 z M 8 3 A 2.0000174 2.0000174 0 0 1 10 5 L 10 11 A 2.0000174 2.0000174 0 0 1 8 13 A 2.0000174 2.0000174 0 0 1 6 11 L 6 5 A 2.0000174 2.0000174 0 0 1 8 3 z " transform="translate(0,1036.3622)" id="path4135" /> diff --git a/tools/editor/icons/source/icon_circle_shape_2d.svg b/tools/editor/icons/source/icon_circle_shape_2d.svg index 4f81ae0e62..56ac538672 100644 --- a/tools/editor/icons/source/icon_circle_shape_2d.svg +++ b/tools/editor/icons/source/icon_circle_shape_2d.svg @@ -1,21 +1,82 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" sodipodi:docname="icon_circle_shape_2d.svg" inkscape:export-ydpi="90" inkscape:export-xdpi="90" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_box_shape.png" inkscape:version="0.91 r13725" version="1.1" id="svg2" viewBox="0 0 16 16" height="16" width="16"> - <sodipodi:namedview inkscape:snap-smooth-nodes="false" inkscape:object-nodes="false" inkscape:snap-intersection-paths="false" inkscape:object-paths="false" inkscape:window-maximized="1" inkscape:window-y="0" inkscape:window-x="0" inkscape:window-height="1055" inkscape:window-width="1920" inkscape:snap-center="true" inkscape:snap-object-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:bbox-nodes="true" inkscape:bbox-paths="true" inkscape:snap-bbox="true" units="px" showgrid="true" inkscape:current-layer="layer1" inkscape:document-units="px" inkscape:cy="11.031037" inkscape:cx="-1.7366522" inkscape:zoom="22.627416" inkscape:pageshadow="2" inkscape:pageopacity="0.0" borderopacity="1.0" bordercolor="#666666" pagecolor="#ffffff" id="base"> - <inkscape:grid id="grid3336" type="xygrid" empspacing="4" /> - </sodipodi:namedview> - <defs id="defs4" /> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g transform="translate(0,-1036.3622)" id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1"> - <path style="opacity:1;fill:none;fill-opacity:1;stroke:#a5b7f3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.98823529" d="m 8,1038.3622 a 6,6 0 0 0 -6,6 6,6 0 0 0 6,6 6,6 0 0 0 6,-6 6,6 0 0 0 -6,-6 z" id="path4157" inkscape:connector-curvature="0" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + sodipodi:docname="icon_circle_shape_2d.svg" + inkscape:export-ydpi="90" + inkscape:export-xdpi="90" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_box_shape.png" + inkscape:version="0.91 r13725" + version="1.1" + id="svg2" + viewBox="0 0 16 16" + height="16" + width="16"> + <sodipodi:namedview + inkscape:snap-smooth-nodes="false" + inkscape:object-nodes="false" + inkscape:snap-intersection-paths="false" + inkscape:object-paths="false" + inkscape:window-maximized="1" + inkscape:window-y="27" + inkscape:window-x="0" + inkscape:window-height="1016" + inkscape:window-width="1920" + inkscape:snap-center="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:bbox-nodes="true" + inkscape:bbox-paths="true" + inkscape:snap-bbox="true" + units="px" + showgrid="true" + inkscape:current-layer="layer1" + inkscape:document-units="px" + inkscape:cy="10.942649" + inkscape:cx="-9.5148271" + inkscape:zoom="22.627416" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base"> + <inkscape:grid + id="grid3336" + type="xygrid" + empspacing="4" /> + </sodipodi:namedview> + <defs + id="defs4" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + transform="translate(0,-1036.3622)" + id="layer1" + inkscape:groupmode="layer" + inkscape:label="Layer 1"> + <path + style="opacity:1;fill:none;fill-opacity:1;stroke:#68b6ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + d="m 8,1038.3622 a 6,6 0 0 0 -6,6 6,6 0 0 0 6,6 6,6 0 0 0 6,-6 6,6 0 0 0 -6,-6 z" + id="path4157" + inkscape:connector-curvature="0" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_concave_polygon_shape_2d.svg b/tools/editor/icons/source/icon_concave_polygon_shape_2d.svg index 79e71425f7..624105e5a8 100644 --- a/tools/editor/icons/source/icon_concave_polygon_shape_2d.svg +++ b/tools/editor/icons/source/icon_concave_polygon_shape_2d.svg @@ -1,21 +1,83 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_concave_polygon_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="22.627416" inkscape:cx="-0.53108971" inkscape:cy="10.128279" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <path style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" d="m 14,1050.3622 -12,0 0,-12 6,6 6,-6 z" id="path4139" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccc" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_concave_polygon_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="31.999999" + inkscape:cx="9.3658684" + inkscape:cy="6.9741169" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 14,1050.3622 -12,0 0,-12 6,6 6,-6 z" + id="path4139" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccc" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_convex_polygon_shape_2d.svg b/tools/editor/icons/source/icon_convex_polygon_shape_2d.svg index fc84cf931b..3b55df7fba 100644 --- a/tools/editor/icons/source/icon_convex_polygon_shape_2d.svg +++ b/tools/editor/icons/source/icon_convex_polygon_shape_2d.svg @@ -1,21 +1,83 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_convex_polygon_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="22.627416" inkscape:cx="-0.53108971" inkscape:cy="10.128279" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <path style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" d="m 14,1050.3622 -12,0 0,-6 6,-6 6,6 z" id="path4139" inkscape:connector-curvature="0" sodipodi:nodetypes="cccccc" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_convex_polygon_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="31.999999" + inkscape:cx="3.9121254" + inkscape:cy="7.1557164" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 14,1050.3622 -12,0 0,-6 6,-6 6,6 z" + id="path4139" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccccc" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_line_shape_2d.svg b/tools/editor/icons/source/icon_line_shape_2d.svg index 551439b4f3..6a8ab39ef3 100644 --- a/tools/editor/icons/source/icon_line_shape_2d.svg +++ b/tools/editor/icons/source/icon_line_shape_2d.svg @@ -1,23 +1,95 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_line_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="22.627416" inkscape:cx="-0.53108971" inkscape:cy="10.128279" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <path style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.39215687" d="m 1,1037.3622 14,14" id="path4139" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> - <path sodipodi:nodetypes="cc" inkscape:connector-curvature="0" id="path4201" d="m 3,1039.3622 10,10" style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.58823532" /> - <path style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" d="m 5,1041.3622 6,6" id="path4203" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_line_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.627416" + inkscape:cx="0.66215304" + inkscape:cy="8.0069586" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <path + style="fill:#68b6ff;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.39215687;fill-opacity:1" + d="m 1,1037.3622 14,14" + id="path4139" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path4201" + d="m 3,1039.3622 10,10" + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.58823532" /> + <path + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 5,1041.3622 6,6" + id="path4203" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_ray_shape_2d.svg b/tools/editor/icons/source/icon_ray_shape_2d.svg index e6ac9c40fa..7ffc2ff3b5 100644 --- a/tools/editor/icons/source/icon_ray_shape_2d.svg +++ b/tools/editor/icons/source/icon_ray_shape_2d.svg @@ -1,22 +1,89 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_ray_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="1" inkscape:cx="-0.53108971" inkscape:cy="10.128279" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <path style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" d="m 8,1038.3622 0,12" id="path4203" inkscape:connector-curvature="0" sodipodi:nodetypes="cc" /> - <path sodipodi:nodetypes="ccc" inkscape:connector-curvature="0" id="path4224" d="m 5,1047.3622 3,3 3,-3" style="fill:none;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.98823529" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_ray_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="16" + inkscape:cx="-3.626492" + inkscape:cy="10.256723" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <path + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + d="m 8,1038.3622 0,12" + id="path4203" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" /> + <path + sodipodi:nodetypes="ccc" + inkscape:connector-curvature="0" + id="path4224" + d="m 5,1047.3622 3,3 3,-3" + style="fill:none;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_rectangle_shape_2d.svg b/tools/editor/icons/source/icon_rectangle_shape_2d.svg index dd0941fe85..d362944e7a 100644 --- a/tools/editor/icons/source/icon_rectangle_shape_2d.svg +++ b/tools/editor/icons/source/icon_rectangle_shape_2d.svg @@ -1,21 +1,86 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_rectangle_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="52.6875" inkscape:cx="8" inkscape:cy="8" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <rect style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:0.98823529;fill-rule:evenodd;stroke:#a5b7f3;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.98823529;marker:none;enable-background:accumulate" id="rect4226" width="12" height="7.9999824" x="2" y="1040.3622" rx="1.7382812e-05" ry="1.7382812e-05" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_rectangle_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="52.6875" + inkscape:cx="4.6595492" + inkscape:cy="7.9620403" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <rect + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:0.98823529;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" + id="rect4226" + width="12" + height="7.9999824" + x="2" + y="1040.3622" + rx="1.7382812e-05" + ry="1.7382812e-05" /> + </g> </svg> diff --git a/tools/editor/icons/source/icon_segment_shape_2d.svg b/tools/editor/icons/source/icon_segment_shape_2d.svg index 99c8f80e9b..b509a31362 100644 --- a/tools/editor/icons/source/icon_segment_shape_2d.svg +++ b/tools/editor/icons/source/icon_segment_shape_2d.svg @@ -1,21 +1,82 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="16" height="16" viewBox="0 0 16 16" id="svg2" version="1.1" inkscape:version="0.91 r13725" inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" inkscape:export-xdpi="90" inkscape:export-ydpi="90" sodipodi:docname="icon_segment_shape_2d.svg"> - <defs id="defs4" /> - <sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="52.6875" inkscape:cx="8" inkscape:cy="8" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="true" units="px" inkscape:snap-bbox="true" inkscape:bbox-paths="true" inkscape:bbox-nodes="true" inkscape:snap-bbox-edge-midpoints="true" inkscape:snap-bbox-midpoints="true" inkscape:snap-object-midpoints="true" inkscape:snap-center="true" inkscape:window-width="1920" inkscape:window-height="1055" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" inkscape:object-paths="true" inkscape:snap-intersection-paths="true" inkscape:object-nodes="true" inkscape:snap-smooth-nodes="true" inkscape:snap-midpoints="true"> - <inkscape:grid type="xygrid" id="grid3336" /> - </sodipodi:namedview> - <metadata id="metadata7"> - <rdf:RDF> - <cc:Work rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-1036.3622)"> - <path style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#a4b6f2;fill-opacity:1;fill-rule:evenodd;stroke:#a5b7f4;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:0.98823529;marker:none;enable-background:accumulate" d="m 2,1050.3622 12,-12" id="path4268" inkscape:connector-curvature="0" /> - </g> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + viewBox="0 0 16 16" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + inkscape:export-filename="/home/djrm/Projects/godot/tools/editor/icons/icon_collision_shape_2d.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + sodipodi:docname="icon_segment_shape_2d.svg"> + <defs + id="defs4" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="52.6875" + inkscape:cx="5.5326216" + inkscape:cy="8.6453144" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + units="px" + inkscape:snap-bbox="true" + inkscape:bbox-paths="true" + inkscape:bbox-nodes="true" + inkscape:snap-bbox-edge-midpoints="true" + inkscape:snap-bbox-midpoints="true" + inkscape:snap-object-midpoints="true" + inkscape:snap-center="true" + inkscape:window-width="1920" + inkscape:window-height="1016" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:object-paths="true" + inkscape:snap-intersection-paths="true" + inkscape:object-nodes="true" + inkscape:snap-smooth-nodes="true" + inkscape:snap-midpoints="true"> + <inkscape:grid + type="xygrid" + id="grid3336" /> + </sodipodi:namedview> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,-1036.3622)"> + <path + style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#68b6ff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1;marker:none;enable-background:accumulate" + d="m 2,1050.3622 12,-12" + id="path4268" + inkscape:connector-curvature="0" /> + </g> </svg> diff --git a/tools/editor/plugins/tile_map_editor_plugin.cpp b/tools/editor/plugins/tile_map_editor_plugin.cpp index d686c37d1a..316077c5d5 100644 --- a/tools/editor/plugins/tile_map_editor_plugin.cpp +++ b/tools/editor/plugins/tile_map_editor_plugin.cpp @@ -34,6 +34,7 @@ #include "canvas_item_editor_plugin.h" #include "tools/editor/editor_settings.h" +#include "tools/editor/editor_scale.h" void TileMapEditor::_notification(int p_what) { @@ -50,6 +51,12 @@ void TileMapEditor::_notification(int p_what) { rotate_270->set_icon(get_icon("Rotate270","EditorIcons")); } break; + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + + if (is_visible()) { + _update_palette(); + } + } break; } } @@ -205,16 +212,16 @@ void TileMapEditor::_update_palette() { if (tiles.empty()) return; + float min_size = EDITOR_DEF("tile_map/preview_size", 64); + min_size *= EDSCALE; + int hseparation = EDITOR_DEF("tile_map/palette_item_hseparation",8); + bool show_tile_names = bool(EDITOR_DEF("tile_map/show_tile_names", true)); - palette->set_max_columns(0); - palette->add_constant_override("hseparation", 6); + palette->add_constant_override("hseparation", hseparation*EDSCALE); + palette->add_constant_override("vseparation", 8*EDSCALE); - float min_size = EDITOR_DEF("tile_map/preview_size",64); palette->set_fixed_icon_size(Size2(min_size, min_size)); - palette->set_fixed_column_width(min_size*3/2); - palette->set_icon_mode(ItemList::ICON_MODE_TOP); - palette->set_max_text_lines(2); - + palette->set_fixed_column_width(min_size * MAX(size_slider->get_val(), 1)); String filter = search_box->get_text().strip_edges(); @@ -228,10 +235,14 @@ void TileMapEditor::_update_palette() { name = "#"+itos(E->get()); } - if (filter != "" && name.findn(filter) == -1) + if (filter != "" && !filter.is_subsequence_ofi(name)) continue; - palette->add_item(name); + if (show_tile_names) { + palette->add_item(name); + } else { + palette->add_item(String()); + } Ref<Texture> tex = tileset->tile_get_texture(E->get()); @@ -252,7 +263,7 @@ void TileMapEditor::_update_palette() { if (selected != -1) set_selected_tile(selected); else - palette->select(0, true); + palette->select(0); } void TileMapEditor::_pick_tile(const Point2& p_pos) { @@ -1348,6 +1359,9 @@ TileMapEditor::TileMapEditor(EditorNode *p_editor) { palette = memnew( ItemList ); palette->set_v_size_flags(SIZE_EXPAND_FILL); palette->set_custom_minimum_size(Size2(mw,0)); + palette->set_max_columns(0); + palette->set_icon_mode(ItemList::ICON_MODE_TOP); + palette->set_max_text_lines(2); add_child(palette); // Add menu items @@ -1458,6 +1472,9 @@ void TileMapEditorPlugin::make_visible(bool p_visible) { TileMapEditorPlugin::TileMapEditorPlugin(EditorNode *p_node) { EDITOR_DEF("tile_map/preview_size",64); + EDITOR_DEF("tile_map/palette_item_hseparation",8); + EDITOR_DEF("tile_map/show_tile_names", true); + tile_map_editor = memnew( TileMapEditor(p_node) ); add_control_to_container(CONTAINER_CANVAS_EDITOR_SIDE, tile_map_editor); tile_map_editor->hide(); |