├── .gitignore ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── engine.cfg ├── lib ├── ReadWriteLock.gd ├── shared │ ├── client.gd │ ├── constants.gd │ └── server.gd ├── tcp │ ├── client.gd │ └── server.gd └── udp │ ├── client.gd │ └── server.gd └── preview ├── client_tab.gd ├── client_tab.xscn ├── index.xscn ├── preview.gd ├── res └── FiraMono-Medium.fnt ├── server_tab.gd └── server_tab.xscn /.gitignore: -------------------------------------------------------------------------------- 1 | # Dolphin generates this file, not needed 2 | .directory 3 | 4 | # Some text editors 5 | *~ 6 | 7 | # Temporary test-related files 8 | tmp.* 9 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ## Thanks to: 2 | - Mozilla - for making the Fira font family, which is used in the test sample. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 KOBUGE-Games 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # godot-networking-module 2 | KOBUGE's Godot networking library 3 | 4 | [docs](https://github.com/KOBUGE-Games/godot-networking-module/wiki) 5 | 6 | -------------------------------------------------------------------------------- /engine.cfg: -------------------------------------------------------------------------------- 1 | [application] 2 | 3 | name="Multiplayer" 4 | main_scene="res://preview/index.xscn" 5 | -------------------------------------------------------------------------------- /lib/ReadWriteLock.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Reference 3 | 4 | var write_lock = Mutex.new() 5 | var read_lock = Mutex.new() 6 | var read_count = 0 7 | 8 | func lock_write(): 9 | write_lock.lock() 10 | 11 | func unlock_write(): 12 | write_lock.unlock() 13 | 14 | func lock_read(): 15 | read_lock.lock() 16 | read_count += 1 17 | if read_count == 1: 18 | write_lock.lock() 19 | read_lock.unlock() 20 | 21 | func unlock_read(): 22 | read_lock.lock() 23 | read_count -= 1 24 | if read_count == 0: 25 | write_lock.unlock() 26 | read_lock.unlock() 27 | 28 | 29 | -------------------------------------------------------------------------------- /lib/shared/client.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Reference 3 | 4 | const ReadWriteLock = preload("../ReadWriteLock.gd") 5 | const constants = preload("constants.gd") 6 | 7 | signal connect() 8 | signal disconnect() 9 | signal message(message) 10 | 11 | var connection 12 | var M_connection = Mutex.new() 13 | 14 | var message_queue = [] 15 | var _internal_message_queue = [] 16 | var M_message_queue = Mutex.new() 17 | 18 | var running 19 | var M_running = Mutex.new() 20 | 21 | var loop_thread = Thread.new() 22 | 23 | func _start_connection(host, port): 24 | pass # Virtual 25 | 26 | func _stop_connection(): 27 | pass # Virtual 28 | 29 | func _update_connection(): 30 | pass # Virtual 31 | 32 | func _send_messages(messages): 33 | pass # Virtual 34 | 35 | func connect_to(host, port): 36 | M_running.lock() 37 | if not running: 38 | M_connection.lock() 39 | 40 | running = true 41 | _start_connection(host, port) 42 | loop_thread.start(self, "loop") 43 | 44 | M_connection.unlock() 45 | 46 | M_running.unlock() 47 | 48 | func send(message): 49 | M_message_queue.lock() 50 | 51 | message_queue.push_back(message) 52 | 53 | M_message_queue.unlock() 54 | 55 | func _send_internal(message): 56 | _internal_message_queue.push_back(message) 57 | 58 | func stop(): 59 | M_running.lock() 60 | 61 | running = false 62 | 63 | M_running.unlock() 64 | 65 | loop_thread.wait_to_finish() 66 | 67 | func loop(data): 68 | while true: 69 | M_connection.lock() 70 | 71 | M_running.lock() 72 | if not running: 73 | M_running.unlock() 74 | 75 | _stop_connection() 76 | 77 | M_connection.unlock() 78 | break; 79 | else: 80 | M_running.unlock() 81 | 82 | _update_connection() 83 | 84 | M_message_queue.lock() 85 | 86 | var _messages = message_queue 87 | message_queue = [] 88 | 89 | M_message_queue.unlock() 90 | 91 | _send_messages(_messages) 92 | 93 | _messages = _internal_message_queue 94 | _internal_message_queue = [] 95 | _send_messages(_messages) 96 | 97 | M_connection.unlock() 98 | OS.delay_msec(constants.TICK_TIME) 99 | -------------------------------------------------------------------------------- /lib/shared/constants.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Reference 3 | 4 | const TICK_TIME = 100 5 | 6 | const UDP_COMMAND = "__$COMMAND$__" 7 | const UDP_TIMEOUT = 5*1000 8 | const UDP_SEND_ACK = 500 9 | 10 | 11 | -------------------------------------------------------------------------------- /lib/shared/server.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Reference 3 | 4 | const ReadWriteLock = preload("../ReadWriteLock.gd") 5 | const constants = preload("constants.gd") 6 | 7 | class RawMessage: 8 | var target = null 9 | var data = {} 10 | 11 | signal connect(id) 12 | signal disconnect(id) 13 | signal message(id, message) 14 | 15 | var server 16 | var M_server = Mutex.new() 17 | 18 | var running = false 19 | var M_running = Mutex.new() 20 | 21 | var connections = {} 22 | var next_id = 0 23 | var RW_connections = ReadWriteLock.new() 24 | 25 | var message_queue = [] 26 | var _internal_message_queue = [] 27 | var M_message_queue = Mutex.new() 28 | 29 | var loop_thread = Thread.new() 30 | 31 | func _start_server(port): 32 | pass # Virtual 33 | 34 | func _stop_server(): 35 | pass # Virtual 36 | 37 | func _update_connections(): 38 | pass # Virtual 39 | 40 | func _send_messages(messages): 41 | pass # Virtual 42 | 43 | func start(port): 44 | M_running.lock() 45 | if not running: 46 | M_server.lock() 47 | 48 | running = true 49 | _start_server(port) 50 | loop_thread.start(self, "loop") 51 | 52 | M_server.unlock() 53 | 54 | M_running.unlock() 55 | 56 | func send_to(id, data): 57 | var message = RawMessage.new() 58 | message.target = id 59 | message.data = data 60 | 61 | M_message_queue.lock() 62 | 63 | message_queue.push_back(message) 64 | 65 | M_message_queue.unlock() 66 | 67 | func send_to_all(data): 68 | var message = RawMessage.new() 69 | message.data = data 70 | 71 | M_message_queue.lock() 72 | 73 | message_queue.push_back(message) 74 | 75 | M_message_queue.unlock() 76 | 77 | func _send_internal(id, data): 78 | var message = RawMessage.new() 79 | message.target = id 80 | message.data = data 81 | 82 | _internal_message_queue.push_back(message) 83 | 84 | func stop(): 85 | M_running.lock() 86 | 87 | running = false 88 | 89 | M_running.unlock() 90 | 91 | loop_thread.wait_to_finish() 92 | 93 | func loop(data): 94 | while true: 95 | M_server.lock() 96 | 97 | M_running.lock() 98 | if not running: 99 | M_running.unlock() 100 | 101 | _stop_server() 102 | 103 | M_server.unlock() 104 | break; 105 | else: 106 | M_running.unlock() 107 | 108 | RW_connections.lock_write() 109 | 110 | _update_connections() 111 | 112 | RW_connections.unlock_write() 113 | 114 | RW_connections.lock_read() 115 | M_message_queue.lock() 116 | 117 | var _messages = message_queue 118 | message_queue = [] 119 | 120 | M_message_queue.unlock() 121 | 122 | _send_messages(_messages) 123 | 124 | _messages = _internal_message_queue 125 | _internal_message_queue = [] 126 | _send_messages(_messages) 127 | 128 | RW_connections.unlock_read() 129 | 130 | M_server.unlock() 131 | OS.delay_msec(constants.TICK_TIME) 132 | -------------------------------------------------------------------------------- /lib/tcp/client.gd: -------------------------------------------------------------------------------- 1 | 2 | extends "../shared/client.gd" 3 | 4 | const ReadWriteLock = preload("../ReadWriteLock.gd") 5 | 6 | class Connection: 7 | var stream 8 | var packet_peer 9 | var connected = false 10 | static func create(stream): 11 | var new_self = new() 12 | new_self.stream = stream 13 | 14 | new_self.packet_peer = PacketPeerStream.new() 15 | new_self.packet_peer.set_stream_peer(stream) 16 | 17 | return new_self 18 | 19 | func _start_connection(host, port): 20 | var stream = StreamPeerTCP.new() 21 | stream.connect(host, port) 22 | 23 | connection = Connection.create(stream) 24 | 25 | func _stop_connection(): 26 | emit_signal("disconnect") 27 | connection.stream.disconnect() 28 | connection = null 29 | 30 | func _update_connection(): 31 | var status = connection.stream.get_status() 32 | 33 | if status == StreamPeerTCP.STATUS_CONNECTED and not connection.connected: 34 | connection.connected = true 35 | emit_signal("connect") 36 | if status == StreamPeerTCP.STATUS_ERROR or status == StreamPeerTCP.STATUS_NONE and connection.connected: 37 | connection.connected = false 38 | emit_signal("disconnect") 39 | 40 | while connection.packet_peer.get_available_packet_count(): 41 | emit_signal("message", connection.packet_peer.get_var()) 42 | 43 | func _send_messages(messages): 44 | for message in messages: 45 | connection.packet_peer.put_var(message) 46 | 47 | -------------------------------------------------------------------------------- /lib/tcp/server.gd: -------------------------------------------------------------------------------- 1 | 2 | extends "../shared/server.gd" 3 | 4 | class Connection: 5 | var id = -1 6 | var stream 7 | var packet_peer 8 | var connected = false 9 | static func create(id, stream): 10 | var new_self = new() 11 | new_self.id = id 12 | new_self.stream = stream 13 | 14 | new_self.packet_peer = PacketPeerStream.new() 15 | new_self.packet_peer.set_stream_peer(stream) 16 | 17 | return new_self 18 | 19 | func _init(): 20 | server = TCP_Server.new() 21 | 22 | func _start_server(port): 23 | server.listen(port) 24 | 25 | func _stop_server(): 26 | server.stop() 27 | 28 | func _update_connections(): 29 | while server.is_connection_available(): 30 | var stream = server.take_connection() 31 | var connection = Connection.create(next_id, stream) 32 | 33 | connections[next_id] = connection 34 | next_id += 1 35 | 36 | for id in connections: 37 | var connection = connections[id] 38 | var status = connection.stream.get_status() 39 | 40 | if status == StreamPeerTCP.STATUS_CONNECTED and not connection.connected: 41 | connection.connected = true 42 | emit_signal("connect", connection.id) 43 | if status == StreamPeerTCP.STATUS_ERROR or status == StreamPeerTCP.STATUS_NONE and connection.connected: 44 | connection.connected = false 45 | emit_signal("disconnect", connection.id) 46 | connections.erase(connection.id) 47 | 48 | 49 | while connection.packet_peer.get_available_packet_count(): 50 | emit_signal("message", connection.id, connection.packet_peer.get_var()) 51 | 52 | func _send_messages(messages): 53 | for message in messages: 54 | if message.target != null: 55 | if connections.has(message.target): 56 | connections[message.target].packet_peer.put_var(message.data) 57 | else: 58 | for id in connections: 59 | connections[id].packet_peer.put_var(message.data) 60 | -------------------------------------------------------------------------------- /lib/udp/client.gd: -------------------------------------------------------------------------------- 1 | 2 | extends "../shared/client.gd" 3 | 4 | const ReadWriteLock = preload("../ReadWriteLock.gd") 5 | 6 | class Connection: 7 | var packet_peer 8 | 9 | var last_ack_sent = null 10 | var last_ack_received = null 11 | 12 | static func create(packet_peer): 13 | var new_self = new() 14 | 15 | new_self.packet_peer = packet_peer 16 | 17 | return new_self 18 | 19 | func _start_connection(host, port): 20 | var packet_peer = PacketPeerUDP.new() 21 | packet_peer.set_send_address(host, port) 22 | #_send_internal() 23 | 24 | connection = Connection.create(packet_peer) 25 | 26 | func _stop_connection(): 27 | emit_signal("disconnect") 28 | connection.packet_peer.close() 29 | connection = null 30 | 31 | func _update_connection(): 32 | var timestamp = OS.get_ticks_msec() 33 | # var status = connection.stream.get_status() 34 | # 35 | # if status == StreamPeerTCP.STATUS_CONNECTED and not connection.connected: 36 | # connection.connected = true 37 | # emit_signal("connect") 38 | # if status == StreamPeerTCP.STATUS_ERROR or status == StreamPeerTCP.STATUS_NONE and connection.connected: 39 | # connection.connected = false 40 | # emit_signal("disconnect") 41 | 42 | while connection.packet_peer.get_available_packet_count(): 43 | var message = connection.packet_peer.get_var() 44 | if message.has(constants.UDP_COMMAND): 45 | var command = message[constants.UDP_COMMAND] 46 | if command == "ACK": 47 | if connection.last_ack_received == null: 48 | emit_signal("connect") 49 | connection.last_ack_received = timestamp 50 | else: 51 | emit_signal("message", message) 52 | 53 | if connection.last_ack_sent == null or timestamp - connection.last_ack_sent > constants.UDP_SEND_ACK: 54 | var ack = {} 55 | ack[constants.UDP_COMMAND] = "ACK" 56 | _send_internal(ack) 57 | 58 | connection.last_ack_sent = timestamp 59 | 60 | if connection.last_ack_recetived != null and timestamp - connection.last_ack_recetived > constants.UDP_TIMEOUT: 61 | emit_signal("disconnect") 62 | 63 | func _send_messages(messages): 64 | for message in messages: 65 | connection.packet_peer.put_var(message) 66 | 67 | -------------------------------------------------------------------------------- /lib/udp/server.gd: -------------------------------------------------------------------------------- 1 | 2 | extends "../shared/server.gd" 3 | 4 | var address_id_map = {} 5 | 6 | class Connection: 7 | var id = -1 8 | var host 9 | var port 10 | 11 | var last_ack_received = null 12 | var last_ack_sent = null 13 | 14 | func _init(_id, _host, _port): 15 | id = _id 16 | host = _host 17 | port = _port 18 | 19 | func _init(): 20 | server = PacketPeerUDP.new() 21 | 22 | func _start_server(port): 23 | server.listen(port) 24 | 25 | func _stop_server(): 26 | server.close() 27 | address_id_map = {} 28 | 29 | func _update_connections(): 30 | var timestamp = OS.get_ticks_msec() 31 | while server.get_available_packet_count(): 32 | var host = server.get_packet_ip() 33 | var port = server.get_packet_port() 34 | var address = str(host, ":", port) 35 | 36 | if not address_id_map.has(address) or not connections.has(address_id_map[address]): 37 | print(address) 38 | var connection = Connection.new(next_id, host, port) 39 | 40 | address_id_map[address] = next_id 41 | connections[next_id] = connection 42 | 43 | next_id += 1 44 | 45 | var connection = connections[address_id_map[address]] 46 | 47 | var message = server.get_var() 48 | 49 | if message.has(constants.UDP_COMMAND): 50 | var command = message[constants.UDP_COMMAND] 51 | if command == "ACK": 52 | if connection.last_ack_received == null: 53 | emit_signal("connect", connection.id) 54 | connection.last_ack_received = timestamp 55 | else: 56 | emit_signal("message", connection.id, message) 57 | 58 | for id in connections: 59 | var connection = connections[id] 60 | if connection.last_ack_sent == null or timestamp - connection.last_ack_sent > constants.UDP_SEND_ACK: 61 | var ack = {} 62 | ack[constants.UDP_COMMAND] = "ACK" 63 | _send_internal(id, ack) 64 | 65 | connection.last_ack_sent = timestamp 66 | 67 | if connection.last_ack_received != null and timestamp - connection.last_ack_received > constants.UDP_TIMEOUT: 68 | var ack = {} 69 | ack[constants.UDP_COMMAND] = "ACK" 70 | _send_internal(id, ack) 71 | emit_signal("disconnect", id) 72 | connections.erase(id) 73 | 74 | 75 | func _send_messages(messages): 76 | for message in messages: 77 | if message.target != null: 78 | if connections.has(message.target): 79 | var connection = connections[message.target] 80 | server.set_send_address(connection.host, connection.port) 81 | server.put_var(message.data) 82 | else: 83 | for id in connections: 84 | var connection = connections[id] 85 | server.set_send_address(connection.host, connection.port) 86 | server.put_var(message.data) 87 | -------------------------------------------------------------------------------- /preview/client_tab.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Control 3 | 4 | var client = preload("res://lib/udp/client.gd").new() 5 | 6 | func _ready(): 7 | get_node("Buttons/Send").connect("pressed", self, "send") 8 | get_node("Buttons/Toggle").connect("toggled", self, "toggle") 9 | 10 | get_parent().prepare_results_console(get_node("Result")) 11 | 12 | client.connect("connect", self, "client_connected") 13 | client.connect("message", self, "new_message") 14 | client.connect("disconnect", self, "client_disconnected") 15 | 16 | func toggle(state): 17 | if state: 18 | get_node("Buttons/Toggle").set_text("Stop client") 19 | client.connect_to("127.0.0.1", 8760) 20 | add_result("Client is running!") 21 | else: 22 | get_node("Buttons/Toggle").set_text("Start client") 23 | add_result("Client stopped!") 24 | client.stop() 25 | 26 | func new_message(msg): 27 | add_result(str("Received: ", msg.to_json())) 28 | 29 | if msg.has("chat"): 30 | add_result(str("<", msg.from, "> : ", msg.chat)) 31 | 32 | func send(): 33 | var possible_messages = [ 34 | {"get": "inventory"}, 35 | {"action": "attack", "target": "rat-42"}, 36 | {"action": "skill", "skill": "FireBall", "target": "snow_troll-3"}, 37 | {"chat": "Hello Server!"} 38 | ] 39 | var msg = possible_messages[rand_range(0, possible_messages.size())] 40 | client.send(msg) 41 | add_result(str("Sent message: ", msg.to_json())) 42 | 43 | func client_connected(): 44 | add_result("Connected!") 45 | 46 | func client_disconnected(): 47 | add_result("Disconnected!") 48 | 49 | func add_result(result): 50 | get_node("Result").set_text(str(get_node("Result").get_text(), "\n", result)) 51 | -------------------------------------------------------------------------------- /preview/client_tab.xscn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | "conn_count" 8 | 0 9 | "conns" 10 | 11 | "editable_instances" 12 | 13 | 14 | "names" 15 | 16 | "Client" 17 | "anchor/right" 18 | "anchor/bottom" 19 | "margin/left" 20 | "margin/top" 21 | "margin/right" 22 | "margin/bottom" 23 | "focus/ignore_mouse" 24 | "focus/stop_mouse" 25 | "size_flags/horizontal" 26 | "size_flags/vertical" 27 | "script/script" 28 | "__meta__" 29 | "Control" 30 | "Buttons" 31 | "rect/min_size" 32 | "Toggle" 33 | "toggle_mode" 34 | "text" 35 | "flat" 36 | "Button" 37 | "Send" 38 | "Result" 39 | "custom_fonts/font" 40 | "TextEdit" 41 | 42 | "node_count" 43 | 5 44 | "node_paths" 45 | 46 | 47 | "nodes" 48 | -1, -1, 13, 0, -1, 12, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 1, 7, 2, 8, 3, 9, 4, 10, 4, 11, 5, 12, 6, 0, 0, 0, 13, 14, -1, 7, 1, 0, 6, 7, 15, 8, 7, 2, 8, 3, 9, 4, 10, 4, 0, 1, 0, 20, 16, -1, 10, 1, 0, 6, 9, 15, 10, 7, 2, 8, 3, 9, 4, 10, 4, 17, 3, 18, 11, 19, 2, 0, 1, 0, 20, 21, -1, 11, 1, 0, 4, 12, 6, 13, 15, 10, 7, 2, 8, 3, 9, 4, 10, 4, 17, 2, 18, 14, 19, 2, 0, 0, 0, 24, 22, -1, 8, 1, 0, 2, 0, 4, 15, 7, 2, 8, 3, 9, 4, 10, 16, 23, 17, 0 49 | "variants" 50 | 51 | 1 52 | 4 53 | False 54 | True 55 | 2 56 | 57 | 58 | "__editor_plugin_screen__" 59 | "2D" 60 | "__editor_plugin_states__" 61 | 62 | "2D" 63 | 64 | "ofs" 65 | -1078.74, -345.453 66 | "snap_grid" 67 | False 68 | "snap_offset" 69 | 0, 0 70 | "snap_pixel" 71 | False 72 | "snap_relative" 73 | False 74 | "snap_rotation" 75 | False 76 | "snap_rotation_offset" 77 | 0 78 | "snap_rotation_step" 79 | 0.261799 80 | "snap_show_grid" 81 | False 82 | "snap_step" 83 | 10, 10 84 | "zoom" 85 | 0.54036 86 | 87 | "3D" 88 | 89 | "ambient_light_color" 90 | 0.15, 0.15, 0.15, 1 91 | "default_light" 92 | True 93 | "default_srgb" 94 | False 95 | "deflight_rot_x" 96 | 0.942478 97 | "deflight_rot_y" 98 | 0.628319 99 | "fov" 100 | 45 101 | "show_grid" 102 | True 103 | "show_origin" 104 | True 105 | "viewport_mode" 106 | 1 107 | "viewports" 108 | 109 | 110 | "distance" 111 | 4 112 | "listener" 113 | True 114 | "pos" 115 | 0, 0, 0 116 | "use_environment" 117 | False 118 | "use_orthogonal" 119 | False 120 | "x_rot" 121 | 0 122 | "y_rot" 123 | 0 124 | 125 | 126 | "distance" 127 | 4 128 | "listener" 129 | False 130 | "pos" 131 | 0, 0, 0 132 | "use_environment" 133 | False 134 | "use_orthogonal" 135 | False 136 | "x_rot" 137 | 0 138 | "y_rot" 139 | 0 140 | 141 | 142 | "distance" 143 | 4 144 | "listener" 145 | False 146 | "pos" 147 | 0, 0, 0 148 | "use_environment" 149 | False 150 | "use_orthogonal" 151 | False 152 | "x_rot" 153 | 0 154 | "y_rot" 155 | 0 156 | 157 | 158 | "distance" 159 | 4 160 | "listener" 161 | False 162 | "pos" 163 | 0, 0, 0 164 | "use_environment" 165 | False 166 | "use_orthogonal" 167 | False 168 | "x_rot" 169 | 0 170 | "y_rot" 171 | 0 172 | 173 | 174 | "zfar" 175 | 500 176 | "znear" 177 | 0.1 178 | 179 | "Anim" 180 | 181 | "visible" 182 | False 183 | 184 | 185 | "__editor_run_settings__" 186 | 187 | "custom_args" 188 | "-l $scene" 189 | "run_mode" 190 | 0 191 | 192 | 193 | 70 194 | 0, 70 195 | 32 196 | 128, 32 197 | "Start client" 198 | 36 199 | 68 200 | "Send message" 201 | 74 202 | 3 203 | 204 | 205 | "version" 206 | 2 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /preview/index.xscn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | "conn_count" 7 | 0 8 | "conns" 9 | 10 | "editable_instances" 11 | 12 | 13 | "names" 14 | 15 | "Playground" 16 | "anchor/right" 17 | "anchor/bottom" 18 | "focus/ignore_mouse" 19 | "focus/stop_mouse" 20 | "size_flags/horizontal" 21 | "size_flags/vertical" 22 | "__meta__" 23 | "Control" 24 | "Tabs" 25 | "tab_align" 26 | "tabs_visible" 27 | "script/script" 28 | "symbol_color" 29 | "outgoing_color" 30 | "incoming_color" 31 | "TabContainer" 32 | "Add" 33 | "anchor/left" 34 | "margin/left" 35 | "margin/top" 36 | "margin/right" 37 | "margin/bottom" 38 | "toggle_mode" 39 | "click_on_press" 40 | "text" 41 | "flat" 42 | "items" 43 | "MenuButton" 44 | 45 | "node_count" 46 | 3 47 | "node_paths" 48 | 49 | 50 | "nodes" 51 | -1, -1, 8, 0, -1, 7, 1, 0, 2, 0, 3, 1, 4, 2, 5, 3, 6, 3, 7, 4, 0, 0, 0, 16, 9, -1, 12, 1, 0, 2, 0, 3, 1, 4, 2, 5, 3, 6, 3, 10, 0, 11, 2, 12, 5, 13, 6, 14, 7, 15, 8, 0, 0, 0, 28, 17, -1, 15, 18, 0, 1, 0, 19, 9, 20, 10, 21, 10, 22, 11, 3, 1, 4, 2, 5, 3, 6, 3, 23, 1, 24, 2, 25, 12, 26, 1, 27, 13, 0 52 | "variants" 53 | 54 | 1 55 | False 56 | True 57 | 2 58 | 59 | "__editor_plugin_screen__" 60 | "2D" 61 | "__editor_plugin_states__" 62 | 63 | "2D" 64 | 65 | "ofs" 66 | -374.285, -298.683 67 | "snap_grid" 68 | False 69 | "snap_offset" 70 | 0, 0 71 | "snap_pixel" 72 | False 73 | "snap_relative" 74 | False 75 | "snap_rotation" 76 | False 77 | "snap_rotation_offset" 78 | 0 79 | "snap_rotation_step" 80 | 0.261799 81 | "snap_show_grid" 82 | False 83 | "snap_step" 84 | 10, 10 85 | "zoom" 86 | 0.66342 87 | 88 | "3D" 89 | 90 | "ambient_light_color" 91 | 0.15, 0.15, 0.15, 1 92 | "default_light" 93 | True 94 | "default_srgb" 95 | False 96 | "deflight_rot_x" 97 | 0.942478 98 | "deflight_rot_y" 99 | 0.628319 100 | "fov" 101 | 45 102 | "show_grid" 103 | True 104 | "show_origin" 105 | True 106 | "viewport_mode" 107 | 1 108 | "viewports" 109 | 110 | 111 | "distance" 112 | 4 113 | "listener" 114 | True 115 | "pos" 116 | 0, 0, 0 117 | "use_environment" 118 | False 119 | "use_orthogonal" 120 | False 121 | "x_rot" 122 | 0 123 | "y_rot" 124 | 0 125 | 126 | 127 | "distance" 128 | 4 129 | "listener" 130 | False 131 | "pos" 132 | 0, 0, 0 133 | "use_environment" 134 | False 135 | "use_orthogonal" 136 | False 137 | "x_rot" 138 | 0 139 | "y_rot" 140 | 0 141 | 142 | 143 | "distance" 144 | 4 145 | "listener" 146 | False 147 | "pos" 148 | 0, 0, 0 149 | "use_environment" 150 | False 151 | "use_orthogonal" 152 | False 153 | "x_rot" 154 | 0 155 | "y_rot" 156 | 0 157 | 158 | 159 | "distance" 160 | 4 161 | "listener" 162 | False 163 | "pos" 164 | 0, 0, 0 165 | "use_environment" 166 | False 167 | "use_orthogonal" 168 | False 169 | "x_rot" 170 | 0 171 | "y_rot" 172 | 0 173 | 174 | 175 | "zfar" 176 | 2000 177 | "znear" 178 | 0.1 179 | 180 | "Anim" 181 | 182 | "visible" 183 | False 184 | 185 | 186 | "__editor_run_settings__" 187 | 188 | "custom_args" 189 | "-l $scene" 190 | "run_mode" 191 | 0 192 | 193 | 194 | 195 | 255, 255, 255, 1 196 | 0, 1, 1, 1 197 | 255, 0, 255, 1 198 | 24 199 | -1 200 | 21 201 | "+" 202 | 203 | 204 | 205 | "version" 206 | 2 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /preview/preview.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Control 3 | 4 | var tab_count = 0 5 | const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 6 | 7 | export var symbol_color = Color(255,255,255) 8 | export var outgoing_color = Color(255,0,255) 9 | export var incoming_color = Color(0,255,255) 10 | 11 | 12 | func _ready(): 13 | var add_menu = get_node("../Add").get_popup() 14 | add_menu.add_item("Add server", 0) 15 | add_menu.add_item("Add client", 1) 16 | add_menu.connect("item_pressed", self, "add_tab") 17 | 18 | add_tab(0) 19 | add_tab(1) 20 | 21 | randomize() 22 | func add_tab(type): 23 | var new_tab 24 | if type == 0: 25 | new_tab = preload("./server_tab.xscn") 26 | elif type == 1: 27 | new_tab = preload("./client_tab.xscn") 28 | new_tab = new_tab.instance() 29 | new_tab.set_name(str(new_tab.get_name(), " ", characters[tab_count])) 30 | tab_count += 1 31 | add_child(new_tab) 32 | 33 | func prepare_results_console(console): 34 | console.set_text(str("----", console.get_parent().get_name(), " log----")) 35 | console.set_readonly(true) 36 | console.set_wrap(true) 37 | console.set_max_chars(20) 38 | console.set_syntax_coloring(true) 39 | console.set_symbol_color(symbol_color) 40 | console.add_keyword_color("Sent", outgoing_color) 41 | console.add_keyword_color("message", outgoing_color) 42 | console.add_keyword_color("response", outgoing_color) 43 | console.add_keyword_color("Received", incoming_color) -------------------------------------------------------------------------------- /preview/res/FiraMono-Medium.fnt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KOBUGE-Incubator/gdscript-networking-library/6376253159c007438192e40bcb05bcb35f5f9cbf/preview/res/FiraMono-Medium.fnt -------------------------------------------------------------------------------- /preview/server_tab.gd: -------------------------------------------------------------------------------- 1 | 2 | extends Control 3 | 4 | var server = preload("res://lib/udp/server.gd").new() 5 | 6 | func _ready(): 7 | get_node("Buttons/Send/Submit").connect("pressed", self, "send") 8 | get_node("Buttons/Toggle").connect("toggled", self, "toggle") 9 | 10 | get_node("Buttons/Send/Target").add_item("All connected clients", 0) 11 | 12 | get_parent().prepare_results_console(get_node("Result")) 13 | 14 | server.connect("connect", self, "client_connected") 15 | server.connect("message", self, "new_message") 16 | server.connect("disconnect", self, "client_disconnected") 17 | 18 | func toggle(state): 19 | if state: 20 | get_node("Buttons/Toggle").set_text("Stop server") 21 | server.start(8760) 22 | add_result("Server is running!") 23 | else: 24 | get_node("Buttons/Toggle").set_text("Start server") 25 | add_result("Server stopped!") 26 | server.stop() 27 | 28 | func new_message(id, msg): 29 | add_result(str("Received: ", msg.to_json())) 30 | 31 | var response = false 32 | 33 | if msg.has("get") and msg.get == "inventory": 34 | response = {"items": ["Old boots", "Raw fish", "Lenses", "Hat", "Rusty dagger"]} 35 | if msg.has("action") and msg.action == "skill": 36 | response = {"error": "Not enough mana!"} 37 | if msg.has("action") and msg.action == "attack": 38 | response = {"ok": true} 39 | if msg.has("chat"): 40 | add_result(str("<", id, "> : ", msg.chat)) 41 | var message = {"chat": msg.chat, "from": id} 42 | server.send_to_all(message) 43 | add_result(str("Sent message: ", message.to_json(), "\n")) 44 | 45 | if response != false: 46 | add_result(str("Sent response: ", response.to_json(), "\n")) 47 | server.send_to(id, response) 48 | 49 | func send(): 50 | var possible_messages = [ 51 | {"dead": str("rat-", floor(rand_range(0, 42)))}, 52 | {"move": str("rat-", floor(rand_range(0, 42))), "amount": [0, 1]}, 53 | {"move": str("rat-", floor(rand_range(0, 42))), "amount": [0, -1]}, 54 | {"move": str("rat-", floor(rand_range(0, 42))), "amount": [1, 0]}, 55 | {"move": str("rat-", floor(rand_range(0, 42))), "amount": [-1, 0]}, 56 | {"notification": str("A new level was added to the collection!"), "from": "!level_bot"} 57 | ] 58 | var msg = possible_messages[rand_range(0, possible_messages.size())] 59 | 60 | var target = get_node("Buttons/Send/Target").get_selected_ID() 61 | if target == 0: 62 | server.send_to_all(msg) 63 | else: 64 | server.send_to(target-1, msg) 65 | 66 | add_result(str("Sent message: ", msg.to_json())) 67 | 68 | func client_connected(id): 69 | get_node("Buttons/Send/Target").add_item(str("<",id,">"), id+1) 70 | add_result(str("<", id, "> Connected!")) 71 | 72 | func client_disconnected(id): 73 | add_result(str("<", id, "> Disconnected!")) 74 | 75 | func add_result(result): 76 | get_node("Result").set_text(str(get_node("Result").get_text(), "\n", result)) 77 | -------------------------------------------------------------------------------- /preview/server_tab.xscn: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | "conn_count" 8 | 0 9 | "conns" 10 | 11 | "editable_instances" 12 | 13 | 14 | "names" 15 | 16 | "Server" 17 | "anchor/right" 18 | "anchor/bottom" 19 | "margin/left" 20 | "margin/top" 21 | "margin/right" 22 | "margin/bottom" 23 | "focus/ignore_mouse" 24 | "focus/stop_mouse" 25 | "size_flags/horizontal" 26 | "size_flags/vertical" 27 | "script/script" 28 | "__meta__" 29 | "Control" 30 | "Buttons" 31 | "rect/min_size" 32 | "Toggle" 33 | "toggle_mode" 34 | "text" 35 | "flat" 36 | "Button" 37 | "Send" 38 | "Submit" 39 | "Target" 40 | "anchor/left" 41 | "align" 42 | "selected" 43 | "items" 44 | "OptionButton" 45 | "Result" 46 | "custom_fonts/font" 47 | "TextEdit" 48 | 49 | "node_count" 50 | 7 51 | "node_paths" 52 | 53 | 54 | "nodes" 55 | -1, -1, 13, 0, -1, 12, 1, 0, 2, 0, 3, 1, 4, 1, 5, 1, 6, 1, 7, 2, 8, 3, 9, 4, 10, 4, 11, 5, 12, 6, 0, 0, 0, 13, 14, -1, 7, 1, 0, 6, 7, 15, 8, 7, 2, 8, 3, 9, 4, 10, 4, 0, 1, 0, 20, 16, -1, 10, 1, 0, 6, 9, 15, 10, 7, 2, 8, 3, 9, 4, 10, 4, 17, 3, 18, 11, 19, 2, 0, 1, 0, 13, 21, -1, 8, 1, 0, 4, 12, 6, 13, 15, 10, 7, 2, 8, 3, 9, 4, 10, 4, 0, 3, 0, 20, 22, -1, 11, 1, 14, 2, 0, 5, 15, 15, 10, 7, 2, 8, 3, 9, 4, 10, 4, 17, 2, 18, 16, 19, 2, 0, 3, 0, 28, 23, -1, 13, 24, 14, 1, 0, 2, 0, 3, 17, 7, 2, 8, 3, 9, 4, 10, 4, 17, 2, 19, 2, 25, 18, 26, 19, 27, 20, 0, 0, 0, 31, 29, -1, 8, 1, 0, 2, 0, 4, 21, 7, 2, 8, 3, 9, 4, 10, 14, 30, 22, 0 56 | "variants" 57 | 58 | 1 59 | 4 60 | False 61 | True 62 | 2 63 | 64 | 65 | "__editor_plugin_screen__" 66 | "Script" 67 | "__editor_plugin_states__" 68 | 69 | "2D" 70 | 71 | "ofs" 72 | -279.919, -271.487 73 | "snap_grid" 74 | False 75 | "snap_offset" 76 | 0, 0 77 | "snap_pixel" 78 | False 79 | "snap_relative" 80 | False 81 | "snap_rotation" 82 | False 83 | "snap_rotation_offset" 84 | 0 85 | "snap_rotation_step" 86 | 0.261799 87 | "snap_show_grid" 88 | False 89 | "snap_step" 90 | 10, 10 91 | "zoom" 92 | 0.814506 93 | 94 | "3D" 95 | 96 | "ambient_light_color" 97 | 0.15, 0.15, 0.15, 1 98 | "default_light" 99 | True 100 | "default_srgb" 101 | False 102 | "deflight_rot_x" 103 | 0.942478 104 | "deflight_rot_y" 105 | 0.628319 106 | "fov" 107 | 45 108 | "show_grid" 109 | True 110 | "show_origin" 111 | True 112 | "viewport_mode" 113 | 1 114 | "viewports" 115 | 116 | 117 | "distance" 118 | 4 119 | "listener" 120 | True 121 | "pos" 122 | 0, 0, 0 123 | "use_environment" 124 | False 125 | "use_orthogonal" 126 | False 127 | "x_rot" 128 | 0 129 | "y_rot" 130 | 0 131 | 132 | 133 | "distance" 134 | 4 135 | "listener" 136 | False 137 | "pos" 138 | 0, 0, 0 139 | "use_environment" 140 | False 141 | "use_orthogonal" 142 | False 143 | "x_rot" 144 | 0 145 | "y_rot" 146 | 0 147 | 148 | 149 | "distance" 150 | 4 151 | "listener" 152 | False 153 | "pos" 154 | 0, 0, 0 155 | "use_environment" 156 | False 157 | "use_orthogonal" 158 | False 159 | "x_rot" 160 | 0 161 | "y_rot" 162 | 0 163 | 164 | 165 | "distance" 166 | 4 167 | "listener" 168 | False 169 | "pos" 170 | 0, 0, 0 171 | "use_environment" 172 | False 173 | "use_orthogonal" 174 | False 175 | "x_rot" 176 | 0 177 | "y_rot" 178 | 0 179 | 180 | 181 | "zfar" 182 | 500 183 | "znear" 184 | 0.1 185 | 186 | "Anim" 187 | 188 | "visible" 189 | False 190 | 191 | 192 | "__editor_run_settings__" 193 | 194 | "custom_args" 195 | "-l $scene" 196 | "run_mode" 197 | 0 198 | 199 | 200 | 70 201 | 0, 70 202 | 32 203 | 128, 32 204 | "Start server" 205 | 36 206 | 68 207 | 3 208 | 2 209 | "Send message to:" 210 | -2 211 | 0 212 | -1 213 | 214 | 215 | 74 216 | 217 | 218 | "version" 219 | 2 220 | 221 | 222 | 223 | --------------------------------------------------------------------------------