瀏覽代碼

fix downloader

sl@cccfr.de 2 年之前
父節點
當前提交
4bf28baf4b
共有 14 個文件被更改,包括 446 次插入49 次删除
  1. 29 15
      ChunkSystem.gd
  2. 23 0
      Downloader_UI.gd
  3. 1 1
      Main.gd
  4. 54 5
      Main.tscn
  5. 22 0
      Messenger.gd
  6. 6 3
      Player.gd
  7. 1 1
      Player.tscn
  8. 99 0
      Player_bak.gd
  9. 12 4
      Tile.gd
  10. 29 4
      TopoLoader.gd
  11. 10 2
      chunk.gd
  12. 6 6
      common.gd
  13. 152 8
      downloader.gd
  14. 2 0
      project.godot

+ 29 - 15
ChunkSystem.gd

@@ -6,7 +6,7 @@ onready var Player = $"../Player"
 var resolution = 512
 var scaleFact = 1000
 var radius = 1737.4*scaleFact
-var renderDistance = 0.2
+var renderDistance = 3
 
 var chunkIndex = {}
 
@@ -18,30 +18,41 @@ func _ready():
 # Called every frame. 'delta' is the elapsed time since the previous frame.
 func _process(delta):
 	var playerSpos = Player.getSpos()
-	var playerChunkPos = Vector2(int(playerSpos.x), int(playerSpos.y))
+	var playerChunkPos = posToChunPos(playerSpos)
 	var playerInterChunkPos = Vector2(playerSpos.x - playerChunkPos.x, playerSpos.y - playerChunkPos.y)
 	
-	var dir = Vector2()
+	for lon in range(playerChunkPos.y-renderDistance, playerChunkPos.y+renderDistance):
+		for lat in range(playerChunkPos.x-renderDistance, playerChunkPos.x+renderDistance):
+			if not (chunkIndex.has(lon) and chunkIndex[lon].has(lat)):
+				add_chunk(Vector2(lat,lon))
+				
+	for lon in chunkIndex:
+		for lat in chunkIndex[lon]:
+			if playerChunkPos.distance_to(Vector2(lat,lon)) > renderDistance*3:
+				remove_chunk(Vector2(lat,lon))
+	#var dir = Vector2()
 	
-	if playerInterChunkPos.x < renderDistance:
-		dir = dir+Vector2(-1,0)
-	elif playerInterChunkPos.x > 1-renderDistance:
-		dir = dir+Vector2(1,0)
+	#if playerInterChunkPos.x < renderDistance:
+	#	dir = dir+Vector2(-1,0)
+	#elif playerInterChunkPos.x > 1-renderDistance:
+	#	dir = dir+Vector2(1,0)
 	
-	if playerInterChunkPos.y < renderDistance:
-		dir = dir+Vector2(0,-1)
-	elif playerInterChunkPos.y > 1-renderDistance:
-		dir = dir+Vector2(0,1)
+	#if playerInterChunkPos.y < renderDistance:
+	#	dir = dir+Vector2(0,-1)
+	#elif playerInterChunkPos.y > 1-renderDistance:
+	#	dir = dir+Vector2(0,1)
 		
-	add_chunk(playerChunkPos+dir)
-	add_chunk(playerChunkPos+Vector2(0,dir.y))
-	add_chunk(playerChunkPos+Vector2(dir.x,0))
+	#add_chunk(playerChunkPos+dir)
+	#add_chunk(playerChunkPos+Vector2(0,dir.y))
+	#add_chunk(playerChunkPos+Vector2(dir.x,0))
 	
 	#for i in [Vector2(1,0),Vector2(1,1),Vector2(0,1),Vector2(-1,0),Vector2(0,-1),Vector2(-1,-1),Vector2(1,-1),Vector2(-1,1)]:
 	#	var dist = Vector2(playerSpos.x, playerSpos.y).distance_to(playerChunkPos+i)
 	#	if dist < renderDistance:
 	#		add_chunk(playerChunkPos+i)
 
+func posToChunPos(pos):
+	return Vector2(int(pos.x), int(pos.y))
 
 func add_chunk(chunkPos = Vector2()):
 	var lat = int(chunkPos.x)
@@ -57,9 +68,12 @@ func add_chunk(chunkPos = Vector2()):
 	chunkIndex[lon][lat] = chunk
 
 func remove_chunk(chunkPos = Vector2()):
+	print("removing chunk %s" %chunkPos)
 	var lat = int(chunkPos.x)
 	var lon = int(chunkPos.y)
 	if chunkIndex.has(lon) and chunkIndex[lon].has(lat):
 		chunkIndex[lon][lat].queue_free()
-		chunkIndex[lon].delete(lat)
+		chunkIndex[lon].erase(lat)
+	if len(chunkIndex[lon]) == 0 :
+		chunkIndex.erase(lon)
 

+ 23 - 0
Downloader_UI.gd

@@ -0,0 +1,23 @@
+extends Panel
+
+
+# Declare member variables here. Examples:
+# var a = 2
+# var b = "text"
+
+
+# Called when the node enters the scene tree for the first time.
+func _ready():
+	Messenger.connect("statusUpdate", self, "_on_status_updated")
+
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+#func _process(delta):
+#	pass
+
+func _on_status_updated(download):
+	rect_size = $VBoxContainer.rect_size
+	$VBoxContainer/name.text = download._file_name
+	$VBoxContainer/size.text = "size: %s"%download._file_size
+	$VBoxContainer/downloaded.text = "downloaded: %s"%download._downloaded_size
+	$VBoxContainer/ProgressBar.value = download._downloaded_percent

+ 1 - 1
Main.gd

@@ -16,7 +16,7 @@ extends Spatial
 
 #var resolution = 512
 var radius = 1737.4*1000
-var startPos = Vector3(24,312,radius)
+var startPos = Vector3(0,90,radius)
 
 #var curlat = 0
 #var curlon = 0

+ 54 - 5
Main.tscn

@@ -1,10 +1,11 @@
-[gd_scene load_steps=11 format=2]
+[gd_scene load_steps=12 format=2]
 
 [ext_resource path="res://Main.gd" type="Script" id=1]
 [ext_resource path="res://Player.tscn" type="PackedScene" id=2]
 [ext_resource path="res://ChunkSystem.gd" type="Script" id=3]
 [ext_resource path="res://tex/eso.jpg" type="Texture" id=4]
 [ext_resource path="res://shader/ground_shader.tres" type="Material" id=5]
+[ext_resource path="res://Downloader_UI.gd" type="Script" id=6]
 
 [sub_resource type="PanoramaSky" id=9]
 panorama = ExtResource( 4 )
@@ -42,9 +43,12 @@ __meta__ = {
 environment = SubResource( 10 )
 
 [node name="DirectionalLight" type="DirectionalLight" parent="WorldEnvironment"]
-transform = Transform( 1, 0, 0, 0, 0.81189, 0.583811, 0, -0.583811, 0.81189, 14.964, 0, 0 )
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 1.02188, 36.5425, 0 )
 visible = false
 
+[node name="OmniLight" type="OmniLight" parent="WorldEnvironment"]
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 2.10944, 4.63251, 4.7882 )
+
 [node name="Player" parent="." instance=ExtResource( 2 )]
 
 [node name="Chunks" type="Spatial" parent="."]
@@ -56,9 +60,6 @@ __meta__ = {
 [node name="UI" type="Control" parent="."]
 margin_right = 40.0
 margin_bottom = 40.0
-__meta__ = {
-"_edit_use_anchors_": false
-}
 
 [node name="1" type="TextEdit" parent="UI"]
 margin_right = 333.0
@@ -96,6 +97,54 @@ margin_right = 789.0
 margin_bottom = 87.0
 text = "4"
 
+[node name="Downloader" type="Panel" parent="UI"]
+margin_left = 29.333
+margin_top = 97.0045
+margin_right = 294.33
+margin_bottom = 194.005
+script = ExtResource( 6 )
+
+[node name="VBoxContainer" type="VBoxContainer" parent="UI/Downloader"]
+margin_right = 264.0
+margin_bottom = 94.0
+
+[node name="Title" type="Label" parent="UI/Downloader/VBoxContainer"]
+margin_right = 264.0
+margin_bottom = 14.0
+text = "Downloader"
+align = 1
+
+[node name="Panel" type="HSeparator" parent="UI/Downloader/VBoxContainer"]
+margin_top = 18.0
+margin_right = 264.0
+margin_bottom = 22.0
+
+[node name="name" type="Label" parent="UI/Downloader/VBoxContainer"]
+margin_top = 26.0
+margin_right = 264.0
+margin_bottom = 40.0
+text = "name:"
+align = 1
+
+[node name="size" type="Label" parent="UI/Downloader/VBoxContainer"]
+margin_top = 44.0
+margin_right = 264.0
+margin_bottom = 58.0
+text = "size:"
+align = 1
+
+[node name="downloaded" type="Label" parent="UI/Downloader/VBoxContainer"]
+margin_top = 62.0
+margin_right = 264.0
+margin_bottom = 76.0
+text = "downloaded:"
+align = 1
+
+[node name="ProgressBar" type="ProgressBar" parent="UI/Downloader/VBoxContainer"]
+margin_top = 80.0
+margin_right = 264.0
+margin_bottom = 94.0
+
 [node name="reference" type="MeshInstance" parent="."]
 visible = false
 mesh = SubResource( 5 )

+ 22 - 0
Messenger.gd

@@ -0,0 +1,22 @@
+extends Node
+
+
+# Declare member variables here. Examples:
+# var a = 2
+# var b = "text"
+
+signal statusUpdate
+signal fileDownloaded
+
+# Called when the node enters the scene tree for the first time.
+func _ready():
+	pass # Replace with function body.
+
+func _on_statusUpdate(update):
+	emit_signal("statusUpdate", update)
+	
+func _on_file_downloaded(download):
+	emit_signal("fileDownloaded", download)
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+#func _process(delta):
+#	pass

+ 6 - 3
Player.gd

@@ -149,13 +149,16 @@ func _physics_process(delta):
 	#velocity = velocity.linear_interpolate(vertDir * (jump_speed_sprint if move_sprint else jump_speed), move_acceleration * delta)
 	#if !is_on_floor():
 		#velocity -= up * gravaty
-		
 	# limit Speed
 	if velocity.length() > move_speed_max:
 		velocity = velocity.normalized() * move_speed_max
-
-	velocity = move_and_slide(velocity, up)
 	
+	move_and_slide(velocity, up)
+	#var collide = move_and_collide(velocity, true, true, true)
+	#if collide:
+#		velocity = Vector3()
+	#else:
+	#	transform.origin += velocity
 	
 	
 	

+ 1 - 1
Player.tscn

@@ -10,7 +10,7 @@
 script = ExtResource( 1 )
 
 [node name="Camera" type="Camera" parent="."]
-transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.67414, -1.02577 )
+transform = Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.22788, -0.984102 )
 far = 50000.0
 
 [node name="CollisionShape" type="CollisionShape" parent="."]

+ 99 - 0
Player_bak.gd

@@ -0,0 +1,99 @@
+extends KinematicBody
+
+
+# physics
+var moveSpeed : float = 5.0
+
+# cam look
+var minLookAngle : float = -90.0
+var maxLookAngle : float = 90.0
+var lookSensitivity : float = 10.0
+
+var ray: RayCast
+
+# vectors
+var vel : Vector3 = Vector3()
+var mouseDelta : Vector2 = Vector2()
+
+onready var camera : Camera = get_node("Camera")
+
+func _ready():
+	move_and_slide(Common.latLonToGlobal(0,0,1750), Vector3.UP)
+
+func _process(delta):
+	# rotate the camera along the x axis
+	camera.rotation_degrees.x -= mouseDelta.y * lookSensitivity * delta
+	
+	# clamp camera x rotation axis
+	camera.rotation_degrees.x = clamp(camera.rotation_degrees.x, minLookAngle, maxLookAngle)
+	
+	# rotate the player along their y-axis
+	rotation_degrees.y -= mouseDelta.x * lookSensitivity * delta
+	
+	# reset the mouseDelta vector
+	mouseDelta = Vector2()
+	var origin = get_global_transform().origin
+	.get_parent().get_node("UI/2").text="latlonhight: "+str(Common.globalToLatLon(origin.x,origin.y,origin.z, origin.length()))+","+str(origin.length())
+ 
+func _input(event):
+	if event is InputEventMouseMotion and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
+		mouseDelta = event.relative
+
+func _physics_process(delta):
+ 
+	# reset the x and z velocity
+	vel.x = 0
+	vel.y = 0
+	vel.z = 0
+	
+	var input = Vector3()
+	
+	# movement inputs
+	if Input.is_action_pressed("move_forward"):
+		input.y -= 1
+	if Input.is_action_pressed("move_backward"):
+		input.y += 1
+	if Input.is_action_pressed("move_left"):
+		input.x -= 1
+	if Input.is_action_pressed("move_right"):
+		input.x += 1
+	if Input.is_action_pressed("move_up"):
+		input.z += 1
+	if Input.is_action_pressed("move_down"):
+		input.z -= 1
+		
+	input = input.normalized()
+	
+	# get the forward and right directions
+	var forward = global_transform.basis.z
+	var right = global_transform.basis.x
+	var up = global_transform.basis.y
+	
+	var relativeDir = (forward * input.y + right * input.x + up * input.z)
+	
+	var moveSpeed2 = moveSpeed
+	if Input.is_action_pressed("sprint"):
+		moveSpeed2 = moveSpeed * 3
+	# set the velocity
+	vel.x = relativeDir.x * moveSpeed2
+	vel.z = relativeDir.z * moveSpeed2
+	vel.y = relativeDir.y * moveSpeed2
+	
+	var gravity = self.global_transform.origin.normalized()
+	# move the player
+	vel = move_and_slide(vel, Vector3.UP)
+	#ray = ray.look_at(self.global_transform.origin, Vector3.UP)
+	
+	var pos = self.global_transform.origin
+	var polPos = Common.globalToLatLon(pos.x,pos.y,pos.z)
+	var rotationDeg=get_rotation_degrees().x
+	var posNorm=abs(pos.normalized().y)
+	var targetRot = posNorm * 90
+	#if rotationDeg < targetRot:
+		#rotate_x(1)
+	#elif rotationDeg > targetRot:
+		#rotate_x(-1)
+	
+	.get_parent().get_node("UI/1").text="x:%s y:%s z:%s" %[pos.x, pos.y, pos.z]
+	#.get_node("UI/3").text="LatLonPos=="+str(latLonToGlobal( self.global_transform.origin.x,self.global_transform.origin.y))
+	

+ 12 - 4
Tile.gd

@@ -2,6 +2,7 @@ extends MeshInstance
 
 var Point = preload("res://point.tscn")
 var TopoLoader = preload("res://TopoLoader.gd")
+var tl
 var TextureLoader = preload("res://TextureLoader.gd")
 
 var groundShader = preload("res://shader/ground_shader.tres") as ShaderMaterial
@@ -17,7 +18,9 @@ var scaleFact:int = 1
 var origin:Vector3
 var center:Vector3
 
-func setParams(latMinIN, latMaxIN, lonMinIN, lonMaxIN, resolutionIN, radiusIN:float, scaleIN):	
+var initialized = false
+
+func initialize(latMinIN, latMaxIN, lonMinIN, lonMaxIN, resolutionIN, radiusIN:float, scaleIN):	
 	latMin = latMinIN
 	latMax = latMaxIN
 	lonMin = lonMinIN
@@ -25,6 +28,14 @@ func setParams(latMinIN, latMaxIN, lonMinIN, lonMaxIN, resolutionIN, radiusIN:fl
 	resolution = resolutionIN
 	radius = radiusIN
 	scaleFact = scaleIN
+	tl = TopoLoader.new()
+	add_child(tl)
+	var fileReady = tl.open(latMin, lonMin)
+	if fileReady and not fileReady is GDScriptFunctionState :
+		initialized = true
+		return true
+	else:
+		return false
 
 func _ready():
 	pass
@@ -127,9 +138,6 @@ func genIndex():
 	return indices
 		
 func genSphereCoords():
-	var tl = TopoLoader.new()
-	add_child(tl)
-	tl.open(latMin, lonMin)
 	tl.cacheArea(latMin, latMax, lonMin, lonMax)
 	var verts = PoolVector3Array()
 	var size = (resolution+1) * (resolution+1)#(lonMax-lonMin)*resolution * (latMax-latMin)*resolution

+ 29 - 4
TopoLoader.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var Downloader = preload("res://downloader.gd")
+#var Downloader = preload("res://downloader.gd")
 
 var filePrefix = "user://topo/"
 var formatString = "SLDEM2015_%s_%02d%s_%02d%s_%03d_%03d_FLOAT.IMG"
@@ -20,6 +20,9 @@ var lonMax = 45
 
 var cache = {}
 var file: File
+var fileReady = false
+
+signal fileReady
 
 # Called when the node enters the scene tree for the first time.
 func open(latMinIN, lonMinIn):
@@ -33,10 +36,29 @@ func open(latMinIN, lonMinIn):
 		orientation = "S"
 		
 	file = File.new()
-	print("opening topo: "+filePrefix+getFilename())
-	var err = file.open(filePrefix+getFilename(), File.READ)
-	if err != 0:
+	print("opening topo: "+getFilepath())
+	var err = file.open(getFilepath(), File.READ)
+	if err == 7:
+		Downloader.connect("file_downloaded", self, "_http_request_completed")
 		Downloader.download(getFilename(), "topo")
+		var dl = Downloader.Download.new()
+		while not dl._file_name == getFilepath():
+			dl = yield(Messenger, "fileDownloaded")
+		file.open(getFilepath(), File.READ)
+		fileReady = true
+		emit_signal("fileReady")
+		return true
+	elif err != 0:
+		push_error("error opening file err:%s" %err)
+		return false
+	else:
+		fileReady = true
+		return true
+
+func _http_request_completed(download):
+	fileReady = true
+	emit_signal("fileReady")
+
 func close():
 	file.close()
 	#queue_free()
@@ -100,6 +122,9 @@ func getFilename():
 	else:
 		properties = [resolution, latMin, orientation, latMax, orientation, lonMin, lonMax]
 	return formatString % properties
+	
+func getFilepath():
+	return filePrefix+getFilename()
 
 # Called every frame. 'delta' is the elapsed time since the previous frame.
 #func _process(delta):

+ 10 - 2
chunk.gd

@@ -23,13 +23,21 @@ func setParams(latMinIN, latMaxIN, lonMinIN, lonMaxIN, resolutionIN, radiusIN:fl
 	resolution = resolutionIN
 	radius = radiusIN
 	scaleFact = scaleFactIN
-	
+
 # Called when the node enters the scene tree for the first time.
 func _ready():
-	.get_node("Mesh").setParams(latMin, latMax, lonMin, lonMax, resolution, radius, scaleFact)
+	print("chunk %s,%s initializing" %[latMin, lonMin])
+	var initialized = .get_node("Mesh").initialize(latMin, latMax, lonMin, lonMax, resolution, radius, scaleFact)
+	if not initialized:
+		var download = Downloader.Download.new()
+		while not download._file_name == .get_node("Mesh").tl.getFilepath():
+			print("awaiting file download for %s" %.get_node("Mesh").tl.getFilepath())
+			download = yield(Messenger, "fileDownloaded")
+			print(download)
 	.get_node("Mesh").genMesh()
 	.get_node("Origin").transform.origin = .get_node("Mesh").origin
 	.get_node("Center").transform.origin = .get_node("Mesh").center
+	print("chunk %s,%s ready" %[latMin, lonMin])
 	#.translate(.get_node("Mesh").origin)
 
 # Called every frame. 'delta' is the elapsed time since the previous frame.

+ 6 - 6
common.gd

@@ -5,10 +5,10 @@ func latLonToGlobal(spos: Vector3, offset:=Vector3(), origin:=Vector3()):
 	var height = spos.z + offset.z
 	var latPos = spos.x
 	var lonPos = spos.y
-	var lat = float(latPos+offset.x)*PI/180
+	var lat = float(latPos+offset.x)*PI/180.0
 	var w = cos(lat)
 	var y = sin(lat)
-	var lon = float(lonPos+offset.y)*PI/180
+	var lon = float(lonPos+offset.y)*PI/180.0
 	var x = sin(lon)
 	var z = cos(lon)
 	return Vector3(x * height * w, y*height, z * height * w)-origin
@@ -16,12 +16,12 @@ func latLonToGlobal(spos: Vector3, offset:=Vector3(), origin:=Vector3()):
 func globalToLatLon(pos: Vector3, offset=Vector3()):
 	var height=pos.length()
 	height = height + offset.z
-	var lon = 0
+	var lon : float = 0.0
 	if pos.z != 0: #and abs(x/z) < 1:
-		lon = atan(pos.x/pos.z)*180/PI
-	var lat = sign(pos.y)*90
+		lon = atan2(pos.x,pos.z)*180.0/PI
+	var lat : float = sign(pos.y)*90.0
 	if abs(pos.y) < height:
-		lat = asin(pos.y/height)*180/PI
+		lat = asin(pos.y/height)*180.0/PI
 	#print("x:"+str(x)+" y:"+str(y)+" z:"+str(z)))
 	#print("lat:"+str(lat)+" lon:"+str(lon))
 	return Vector3(lat, lon, height)

+ 152 - 8
downloader.gd

@@ -5,21 +5,165 @@ var kinds = {
 	"color": "" 
 }
 
+var downloads = {}
+var downloading = false
+
+signal file_downloaded
+signal downloads_finished
+signal stats_updated
+signal downloads_started
+
+export(bool)            var blind_mode : bool   = false
+
+var _headers   : Array = []
+
+var _last_method : int
+var _ssl         : bool = false
+
+var _file := File.new()
+
+var _timer : float = 0.0
+
+var currentDownload : Download
+class Download:
+	var _file_name : String
+	var _file_size : float
+	var _current_url       : String
+	var _kind : String
+	var _downloaded_percent : float = 0
+	var _downloaded_size    : float = 0
+
+func _init() -> void:
+	set_process(false)
+	connect("request_completed", self, "_on_request_completed")
+
+func get_stats() -> Download:
+	return currentDownload
+	
+func _reset() -> void:
+	currentDownload = Download.new()
+	
+func _downloads_done() -> void:
+	set_process(false)
+	_update_stats()
+	_file.close()
+	emit_signal("downloads_finished", currentDownload)
+	_reset()
+	downloading = false
+	
+func _update_stats() -> void:
+	if blind_mode == false:
+		_calculate_percentage()
+		emit_signal("stats_updated",currentDownload)
+	
+func _process(delta):
+	if not downloading:
+		if len(downloads) == 0:
+			set_process(false)
+			return
+		for kind in downloads:
+			for file in downloads[kind]:
+				var dl = downloads[kind][file]
+				currentDownload = dl
+				_download()
+	_timer += delta
+	if _timer > 1:
+		_update_stats()
+		_timer = 0.0
+		
+func _download():
+	ensureFolder(currentDownload._kind)
+	download_file = currentDownload._file_name
+	.connect("stats_updated", Messenger, "_on_statusUpdate")
+	_send_head_request()
+	print("downloading %s" %currentDownload._file_name)
+	downloading = true
+
 func ensureFolder(kind):
 	var dir = Directory.new()
 	dir.open("user://")
 	if not dir.dir_exists(kind):
 		dir.make_dir(kind)
-
+	
+	
 func download(file, kind):
+	set_process(true)
 	if not kinds.has(kind):
 		print("don't know kind %s"%kind)
 		return
+	if not downloads.has(kind):
+		downloads[kind] = {}
+	if downloads[kind].has(file):
+		return
+	var download = Download.new()
+	download._file_name = "user://%s/%s"%[kind, file]
+	download._current_url = make_filename(kind, file)
+	download._kind = kind
+	downloads[kind][file] = download
+	
+func make_filename(kind, file):
+	return kinds[kind]%file
+	
+func _send_head_request() -> void:
+	# The HEAD method only gets the head and not the body. Therefore, doesn't
+	#   download the file.
+	request(currentDownload._current_url, _headers, _ssl, HTTPClient.METHOD_HEAD)
+	_last_method = HTTPClient.METHOD_HEAD
+	
+	
+func _send_get_request() -> void:
+	var error = request(currentDownload._current_url, _headers, _ssl, HTTPClient.METHOD_GET)
+	if error == OK:
+		emit_signal("downloads_started")
+		_last_method = HTTPClient.METHOD_GET
+		set_process(true)
+	
+	elif error == ERR_INVALID_PARAMETER:
+		print("Given string isn't a valid url ")
+	elif error == ERR_CANT_CONNECT:
+		print("Can't connect to host")
+	
+func _on_request_completed(p_result,
+						   _p_response_code,
+						   p_headers,
+						   _p_body) -> void:
+	if p_result == RESULT_SUCCESS:
+		if _last_method == HTTPClient.METHOD_HEAD and blind_mode == false:
+			var regex = "(?i)content-length: [0-9]*"
+			var size  = _extract_regex_from_header(regex, p_headers.join(' '))
+			size = size.replace("Content-Length: ", "")
+			currentDownload._file_size = size.to_float()
+			_send_get_request()
+			
+		elif _last_method == HTTPClient.METHOD_HEAD and blind_mode == true:
+			_send_get_request()
+			
+		elif _last_method == HTTPClient.METHOD_GET:
+			emit_signal("file_downloaded", currentDownload)
+			Messenger._on_file_downloaded(currentDownload)
+			downloads[currentDownload._kind].erase(currentDownload._file_name)
+			if len(downloads[currentDownload._kind]) == 0:
+				downloads.erase(currentDownload._kind)
+			if len(downloads) == 0:
+				_downloads_done()
+				
+	elif p_result == RESULT_DOWNLOAD_FILE_CANT_OPEN:
+		push_error("Download cannot open file %s" %currentDownload._file_name)
+	else:
+		print("HTTP Request error: ", p_result)
+
+func _calculate_percentage() -> void:
+	var error : int
+	error = _file.open(download_file, File.READ)
+
+	if error == OK:
+		currentDownload._downloaded_size    = _file.get_len()
+		currentDownload._downloaded_percent = (currentDownload._downloaded_size / currentDownload._file_size) *100
+		
+func _extract_regex_from_header(p_regex  : String,
+								p_header : String) -> String:
+	var regex = RegEx.new()
+	regex.compile(p_regex)
 	
-	ensureFolder(kind)
-	var url = kinds[kind]%file
-	download_file = "user://%s/%s".format([kind, file])
-	# Perform a GET request.
-	var error = request(url)
-	if error != OK:
-		push_error("An error occurred in the HTTP request.")
+	var result = regex.search(p_header)
+	return result.get_string()

+ 2 - 0
project.godot

@@ -17,6 +17,8 @@ config/icon="res://icon.png"
 [autoload]
 
 Common="*res://common.gd"
+Downloader="*res://downloader.gd"
+Messenger="*res://Messenger.gd"
 
 [input]