# 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} const NUM_BITS_IN_LONG_ACK = 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 _latest_probe_time var _latest_probe var _latest_probes var _quit_id var _update_time var _queue_message_system var _khaganat_host = "localhost" var _khaganat_port = "47851" 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() _long_ack_bit_field.resize(512) _last_ack_in_long_ack = 0 _latest_sync = 0 _latest_probe_time = 0 _latest_probe = 0 _latest_probes = Array() _update_time = 0 _quit_id = 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()) # To connect you need send 1st message 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() msgout.put_sint32(_current_received_number) msgout.put_bool(true) 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_system_ack_probe(): var msgout = preload("res://bitstream.gdns").new() msgout.put_sint32(_current_received_number) msgout.put_bool(true) msgout.put_uint8(CLFECOMMON.SYSTEM_ACK_PROBE_CODE) msgout.put_sint32(_latest_probes.size()) for data in _latest_probes: msgout.put_sint32(data) _latest_probes.clear() #_queue_message_system.append(msgout) var res = _socketUDP.put_packet(msgout.get_data()) if ( res != OK): print("[net_low_level:send_system_quit] Error to send system quit : " + str(res)) return func send_system_quit(): # TODO - check why we send quit_id var msgout = preload("res://bitstream.gdns").new() _quit_id += 1 msgout.put_sint32(_current_received_number) msgout.put_bool(true) msgout.put_uint8(CLFECOMMON.SYSTEM_QUIT_CODE) msgout.put_sint32(_quit_id) #_queue_message_system.append(msgout) _connection_state == TCONNECTIONSTATE.QUIT var res = _socketUDP.put_packet(msgout.get_data()) if ( res != OK): print("[net_low_level:send_system_quit] Error to send system quit : " + str(res)) return 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 set_khaganat_server(host, port): _khaganat_host = host _khaganat_port = port func connect_to_server(user_addr, user_key, user_id): var connexion = load("res://assets/Scripts/Config/connexion.gd").connexion.new() 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 decode_system_message(msgin): 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 hexa = load("res://assets/Scripts/Tools/hexa.gd").new() 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) print(hexa.ArrayIntToStringHexa(msg_xml)) num = "" for item in database_xml: num += str(item) + "." print(num) print(hexa.ArrayIntToStringHexa(database_xml)) if not msg.is_correct_md5(hexa.ArrayIntToStringHexa(msg_xml)): print("[net_low_level:analyze_message_received] Wrong MD5 for msg.xml") # TODO - we need quit with message error and go to login return send_system_sync() CLFECOMMON.SYSTEM_STALLED_CODE: pass CLFECOMMON.SYSTEM_PROBE_CODE: _latest_probe_time = _update_time _latest_probe = msgin.get_sint32() _latest_probes.append(_latest_probe) send_system_ack_probe() CLFECOMMON.SYSTEM_SERVER_DOWN_CODE: pass _: print("[net_low_level:analyze_message_received] Message type unknown (" + str(message) + ")") func decode_normal_message(msgin): pass func analyze_message_received(msgbytes): # khanat-opennel-code/code/ryzom/server/src/frontend_service/fe_receive_sub.cpp:769 void CFeReceiveSub::handleReceivedMsg( CClientHost *clienthost ) _update_time = OS.get_ticks_msec() var msgin = preload("res://bitstream.gdns").new() msgin.put_data(msgbytes) _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 & (NUM_BITS_IN_LONG_ACK -1)) _long_ack_bit_field.put(_current_received_number & (NUM_BITS_IN_LONG_ACK-1), ackBool) if _last_ack_in_long_ack <= (_last_received_number - NUM_BITS_IN_LONG_ACK): _last_ack_in_long_ack = _last_received_number - NUM_BITS_IN_LONG_ACK + 1 _last_received_number = _current_received_number if system_mode: decode_system_message(msgin) else: decode_normal_message(msgin) func _process(delta): var max_read = 10 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 (" + str(msgout.size()) + ", " + msgout.show() + ")" ) _socketUDP.put_packet(msgout.get_data()) if _latest_probes.size() > 0: send_system_ack_probe() while _socketUDP.get_available_packet_count() > 0 and max_read > 0: var msgbytes = _socketUDP.get_packet() if msgbytes.size() > 0: analyze_message_received(msgbytes) func _exit_tree(): disconnect_server()