Transfering Large files through Multiplayer

Godot Version

4.2.2 stable

Question

Hi im trying to make a simple file sending program
choose the file you want no matter the size
and send it to the connected peer
but i cant find a way to send it i thought
of breaking the file into parts but i dont know how

How large are the files you’re looking to send? How long are users willing to wait for when files are being downloaded?

As a protocol, UDP is not designed for large file transfers. It favors low latency over high throughput. While modern Internet connections make it possible to achieve relatively fast download speeds through UDP, it’s still easily 10× slower than an HTTP download could be on the same connection.

HTTP requests should be preferred instead. However, for direct player-to-player file sending, this would require creating an HTTP server on one player’s end, which is not ideal. In this case, you could open a raw TCP connection alongside the ongoing multiplayer connection (which uses ENet, which itself runs on UDP).

3 Likes

What kind of data is it? It will work best if you could serialize the data.

ENET has a reliable udp option that will send data reliably and in order. Each packet will max out on the MTU size. Which is typically 1500 bytes and ENET/Godot should chunk it up for you, but I’d bet Godot might not like a singularly large data array and may throw some warnings.

You could also estimate how long it would take based on MTU size. You could maybe also setup multiple channels to increase throughput.

here is a rough draft without any special features. this send a 6.5 MB file in ~2 seconds on local host

@rpc("any_peer","reliable")
func transfer(data:PackedByteArray, filename:String):
	print("write_file: ", Time.get_ticks_msec())
	var cfile=FileAccess.open("res://client_" + filename,FileAccess.WRITE )
	cfile.store_buffer(data)
	pass

func transfer_file():
	print("Send_file: ", Time.get_ticks_msec())
	var file := FileAccess.open("res://invsisble_man.mp4",FileAccess.READ)
	print(file.get_length())
	transfer.rpc(FileAccess.get_file_as_bytes("res://invsisble_man.mp4"),"invsisble_man.mp4")
	pass

There were no warnings or errors.

here is a slightly more complex and incomplete version that can give a percentage to completion and is chunked.

enum TransferCmd{
	START,
	STOP
}
var transfer_buffer: = PackedByteArray()
var output_name: String = ""
var file_size: int = 0
var in_session: bool = false

@rpc("any_peer","reliable")
func session(cmd:TransferCmd, filename:String, size:int):
	match(cmd):
		TransferCmd.START:
			output_name = filename
			file_size = size
			in_session = true
			transfer_buffer.clear()
			start_session.rpc()
			pass
		TransferCmd.STOP:
			pass

@rpc("any_peer","reliable")
func transfer_chunk(data:PackedByteArray):
	transfer_buffer.append_array(data)
	
	$Label2.text = str(float(transfer_buffer.size()) / float(file_size))
	if file_size == transfer_buffer.size():
		print("write_file: ", Time.get_ticks_msec())
		var cfile=FileAccess.open("res://client_" + output_name,FileAccess.WRITE )
		cfile.store_buffer(transfer_buffer)
	pass

signal start_ack
@rpc("any_peer","reliable")
func start_session():
	print("start ack")	
	start_ack.emit()
	pass

func transfer_file():
	print("Send_file: ", Time.get_ticks_msec())
	transfer_buffer = FileAccess.get_file_as_bytes("res://invsisble_man.mp4")
	print(transfer_buffer.size())
	session.rpc(TransferCmd.START,"invsisble_man.mp4", transfer_buffer.size() )
	await start_ack
	print("starting transfer")
	var chunks:int = ceilf(float(transfer_buffer.size()) / 1024.0 )
	print("chunks to send ", chunks)
	for i in range(chunks):
		var start_idx:int = i * 1024
		var end_idx:int = start_idx + 1024
		var slice := transfer_buffer.slice(start_idx,end_idx)
		transfer_chunk.rpc(slice)
	pass

sorry for taking so long. im making this program so when me and my friends or siblings want to send games or updates of games we can send them to each other easily without group names and configuring ethernet options so the files size should be 70 gb atleast
Also i forgot to mention that it is local networking only computers that are connected to the router

Thanks. ill try it in my code and see if it will work

Oh, you should look into running your own git server. Like gitea.

Also a shared network drive would be better. Then using Godot, if you are both on the same network.