test-client-godot/assets/Scripts/Network/net_low_level.gd
2019-12-12 17:32:49 +01:00

296 lines
No EOL
11 KiB
GDScript

# 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 <http://www.gnu.org/licenses/>.
# 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
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 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 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()