# Class to manage low level communication with khaganat # # Copyright (C) 2019 AleaJactaEst # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # https://godotengine.org/qa/20026/to-send-a-udp-packet-godot-3 extends Control enum TCONNECTIONSTATE { NOTINITIALISED = 0, # nothing happened yet NOTCONNECTED = 1, # init() called AUTHENTICATE = 2, # connect() called, identified by the login server LOGIN = 3, # connecting to the frontend, sending identification SYNCHRONIZE = 4, # connection accepted by the frontend, synchronizing CONNECTED = 5, # synchronized, connected, ready to work PROBE = 6, # connection lost by frontend, probing for response STALLED = 7, # server is stalled DISCONNECT = 8, # disconnect() called, or timeout, or connection closed by frontend QUIT = 9} enum CLFECOMMON { SYSTEM_LOGIN_CODE = 0, SYSTEM_SYNC_CODE = 1, SYSTEM_ACK_SYNC_CODE = 2, SYSTEM_PROBE_CODE = 3, SYSTEM_ACK_PROBE_CODE = 4, SYSTEM_DISCONNECTION_CODE = 5, SYSTEM_STALLED_CODE = 6, SYSTEM_SERVER_DOWN_CODE = 7, SYSTEM_QUIT_CODE = 8, SYSTEM_ACK_QUIT_CODE = 9, NUMBITSINLONGACK = 512} var _connection_state var _socketUDP var _user_addr var _user_key var _user_id var _current_received_number var _last_received_number var _confirmed_received_number var _last_ack_bit var _ack_bit_mask var _long_ack_bit_field var _last_ack_in_long_ack var _latest_sync var _queue_message_system func _ready(): _current_received_number = 0 _last_received_number = 0 _confirmed_received_number = 0 _last_ack_bit = 0 _ack_bit_mask = 0 _connection_state = TCONNECTIONSTATE.NOTINITIALISED _socketUDP = PacketPeerUDP.new() _queue_message_system = Array() _long_ack_bit_field = preload("res://bitset.gdns").new() _last_ack_in_long_ack = 0 _latest_sync = 0 func send_system_login(user_addr, user_key, user_id, lang): var msgout = preload("res://bitstream.gdns").new() msgout.put_sint32(_current_received_number) msgout.put_bool(true) msgout.put_uint8(CLFECOMMON.SYSTEM_LOGIN_CODE) msgout.put_string_hexa32(user_addr) msgout.put_string_hexa32(user_key) msgout.put_string_hexa32(user_id) msgout.put_string(lang) print("[net_low_level:send_system_login] Send System Login :" + msgout.show()) var res = _socketUDP.put_packet(msgout.get_data()) if ( res != OK): print("[net_low_level:send_system_login] Error to send system login : " + str(res)) return _connection_state = TCONNECTIONSTATE.CONNECTED func send_system_sync(): var msgout = preload("res://bitstream.gdns").new() var curtime = OS.get_ticks_msec() msgout.put_uint8(CLFECOMMON.SYSTEM_ACK_SYNC_CODE) msgout.put_sint32(_last_received_number) msgout.put_sint32(_last_ack_in_long_ack) _long_ack_bit_field.write_serial(msgout) msgout.put_sint32(_latest_sync) _queue_message_system.append(msgout) func send_systemm_disconnect(): var msgout = preload("res://bitstream.gdns").new() msgout.put_sint32(_current_received_number) msgout.put_bool(true) msgout.put_uint8(CLFECOMMON.SYSTEM_DISCONNECTION_CODE) var res = _socketUDP.put_packet(msgout.get_data()) _connection_state == TCONNECTIONSTATE.QUIT if ( res != OK): print("[net_low_level:send_systemm_disconnect] Error to send system disconnection : " + str(res)) return func disconnect_server(): print("[net_low_level:disconnect_server] Disconnect") if _socketUDP: send_systemm_disconnect() print("[net_low_level:disconnect_server] Send disconnect to server") _socketUDP.close() func connect_to_server(user_addr, user_key, user_id): var connexion = load("res://assets/Scripts/Config/connexion.gd").connexion.new() var khaganat_host = connexion.get_khaganat_host() var khaganat_port = connexion.get_khaganat_port() var lang = connexion.get_language() print("[net_low_level:connect_to_server] prepare:" + str(khaganat_host) + ":" + str(khaganat_port)) _socketUDP.set_dest_address(khaganat_host, khaganat_port) send_system_login(user_addr, user_key, user_id, lang) func analyze_message_received(msgbytes): var msgin = preload("res://bitstream.gdns").new() msgin.put_data(msgbytes) #if msgin.size() == 0: # return _current_received_number = msgin.get_sint32() var system_mode = msgin.get_bool() print("[net_low_level:analyze_message_received] Tick:" + str(_current_received_number) + ", Mode:" + str(system_mode) + ", Size:" + str(msgin.size())) if system_mode: print("[net_low_level:analyze_message_received] System Mode") if _current_received_number > _last_received_number + 1: print("[net_low_level:analyze_message_received] lost message : " + str(_last_received_number + 1 - _current_received_number)) elif _current_received_number == _last_received_number: print("[net_low_level:analyze_message_received] Server re-send last message") return elif _current_received_number < _last_received_number: print("[net_low_level:analyze_message_received] Server re-send old message") return var ackBool = false var ackBit = 0 if not system_mode: match _connection_state: TCONNECTIONSTATE.CONNECTED: ackBool = true ackBit = 1 TCONNECTIONSTATE.SYNCHRONIZE: ackBool = true ackBit = 1 _: ackBool = false if _current_received_number - _last_received_number < 32: _ack_bit_mask <<= _current_received_number - _last_received_number _ack_bit_mask |= _last_ack_bit << (_current_received_number - _last_received_number - 1) elif (_current_received_number - _last_received_number) == 32 and _last_ack_bit != 0: _ack_bit_mask = 0x80000000 else: _ack_bit_mask = 0x00000000 _last_ack_bit = ackBit for i in range(_last_received_number + 1, _current_received_number): _long_ack_bit_field.clear_bit(i & 511) # (512 - 1) mask 9bit _long_ack_bit_field.put(_current_received_number & 511, ackBool) # (512 - 1) mask 9bit if _last_ack_in_long_ack <= (_last_received_number - 512): _last_ack_in_long_ack = _last_received_number - 511 # (512 - 1) mask 9bit print("[net_low_level:analyze_message_received] _last_ack_in_long_ack:" + str(_last_ack_in_long_ack) + ", _long_ack_bit_field:" + _long_ack_bit_field.show()) var message = msgin.get_uint8() print("[net_low_level:analyze_message_received] Message type:" + str(message) ) match message: CLFECOMMON.SYSTEM_LOGIN_CODE: pass CLFECOMMON.SYSTEM_SYNC_CODE: var synchronize = msgin.get_uint32() var stime = msgin.get_sint64() _latest_sync = msgin.get_uint32() var msg_xml = msgin.get_array_uint8(16) var database_xml = msgin.get_array_uint8(16) print("[net_low_level:analyze_message_received] synchronize:" + str(synchronize) + " stime:" + str(stime) + " latest_sync:" + str(_latest_sync)) var num = "" for item in msg_xml: num += str(item) + "." print(num) num = "" for item in database_xml: num += str(item) + "." print(num) send_system_sync() CLFECOMMON.SYSTEM_STALLED_CODE: pass CLFECOMMON.SYSTEM_PROBE_CODE: pass CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: pass _: print("[net_low_level:analyze_message_received] Message type unknown (" + str(message) + ")") _last_received_number = _current_received_number func _process(delta): if _connection_state == TCONNECTIONSTATE.NOTINITIALISED: return if _connection_state == TCONNECTIONSTATE.NOTCONNECTED: return if _connection_state == TCONNECTIONSTATE.QUIT: return if _queue_message_system.size() > 0: var msgout = _queue_message_system.pop_front() print("[net_low_level:_process] Send data system") _socketUDP.put_packet(msgout.get_data()) if _socketUDP.get_available_packet_count() == 0: return var msgbytes = _socketUDP.get_packet() if msgbytes.size() > 0: analyze_message_received(msgbytes) func _exit_tree(): disconnect_server()