Skip to content

Commit

Permalink
fix: Fixed several performance and stability issues
Browse files Browse the repository at this point in the history
  • Loading branch information
freehuntx committed Dec 26, 2023
1 parent 11cfd7a commit af1c667
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 57 deletions.
34 changes: 25 additions & 9 deletions addons/matcha/MatchaPeer.gd
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
extends RefCounted
const Utils := preload("./lib/Utils.gd")

# Signals
signal connected
signal connecting_failed
signal disconnected
signal closed
signal sdp_created(sdp: String)

# Members
var announced := false
var peer_id: String
var offer_id: String
var _peer: WebRTCPeerConnection
var _gathered := false
var _connecting := false
var _connected := false
var _answered := false
var _type: String
var _local_sdp: String
var _remote_sdp: String
var _rtc_peer: WebRTCMultiplayerPeer
var _rtc_peer_id: int

var type:
get: return _type
var peer:
get: return _peer
var gathered:
get: return _gathered
var answered:
get: return _answered
var local_sdp:
get: return _local_sdp
var rtc_peer_id:
get: return _rtc_peer_id

# Constructor
func _init(rtc_peer: WebRTCMultiplayerPeer):
Expand All @@ -33,40 +45,46 @@ func _init(rtc_peer: WebRTCMultiplayerPeer):
_peer.session_description_created.connect(self._on_sdp_created)
_peer.ice_candidate_created.connect(self._on_icecandidate_created)
_rtc_peer.add_peer(_peer, _rtc_peer_id)
_poll()
Engine.get_main_loop().process_frame.connect(self._poll)

# Public methods
func close():
if _rtc_peer.has_peer(_rtc_peer_id):
_rtc_peer.remove_peer(_rtc_peer_id)
if _peer == null: return
_peer.close()
_peer = null
closed.emit()

func set_answer(remote_sdp: String):
assert(_type == "offer", "The peer is not an offer")
assert(_remote_sdp == "", "The offer was already answered")
assert(not _answered, "The offer was already answered")
_answered = true
_remote_sdp = remote_sdp
_peer.set_remote_description("answer", remote_sdp)
if _peer != null:
_peer.set_remote_description("answer", remote_sdp)

func create_offer() -> Error:
assert(_type == "", "The peer is already in use")
_type = "offer"
offer_id = Utils.gen_id()
var err := _peer.create_offer()
if err != OK:
close()
return err

func create_answer(remote_sdp: String):
assert(_peer != null, "The peer null")
assert(_type == "", "The peer is already in use")
_type = "answer"
_peer.set_remote_description("offer", remote_sdp)

# Private methods
func _poll():
_peer.poll()
if _peer == null: return

if not _gathered:
if _peer.get_gathering_state() == WebRTCPeerConnection.GATHERING_STATE_COMPLETE:
_gathered = true
_connecting = true
#_peer.set_local_description(_type, _local_sdp)
sdp_created.emit(_local_sdp)
if _connecting:
var state := _peer.get_connection_state()
Expand All @@ -87,8 +105,6 @@ func _poll():
close()
return

Engine.get_main_loop().create_timer(0.1).timeout.connect(self._poll)

func _on_sdp_created(type: String, sdp: String):
_local_sdp = sdp
_peer.set_local_description(_type, sdp)
Expand Down
90 changes: 43 additions & 47 deletions addons/matcha/MatchaRoom.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ const Utils := preload("res://addons/matcha/lib/Utils.gd")
const TrackerClient := preload("./tracker/TrackerClient.gd")
const MatchaPeer := preload("./MatchaPeer.gd")
const POOL_SIZE := 10
const POLL_INTERVAL := 0.1
const OFFER_TIMEOUT := 30

# Members
Expand All @@ -12,8 +11,7 @@ var _info_hash: String
var _peer_id := Utils.gen_id()
var _rtc_peer := WebRTCMultiplayerPeer.new()
var _rtc_peer_id: int
var _peers := {}
var _offers := {}
var _peers: Array[MatchaPeer] = []

# Getters
var rtc_peer:
Expand All @@ -36,79 +34,77 @@ func _init(options:={}) -> void:
tracker_client.failure.connect(self._on_failure.bind(tracker_client))
_tracker_clients.append(tracker_client)

_poll() # Starts the poll loop
Engine.get_main_loop().process_frame.connect(self._poll)

# Private methods
func _poll():
_rtc_peer.poll()
_cleanup_offers()
_create_offers()
_handle_offers_announcment()

Engine.get_main_loop().create_timer(POLL_INTERVAL).timeout.connect(self._poll) # Run poll in x seconds again
func _remove_offer(offer_id: String) -> void:
for peer in _peers:
if peer.offer_id != offer_id: continue
peer.close()
_peers.erase(peer)

func _cleanup_offers() -> void:
var current_time := Time.get_unix_time_from_system()
for offer in _offers.values():
if current_time - offer.created_at > OFFER_TIMEOUT:
offer.peer.close()
_offers.erase(offer.id)
func _remove_peer_id(peer_id: String) -> void:
for peer in _peers:
if peer.peer_id != peer_id: continue
peer.close()
_peers.erase(peer)

func _create_offer() -> void:
var offer = {
"id": Utils.gen_id(),
"peer": MatchaPeer.new(_rtc_peer),
"announced": false,
"created_at": Time.get_unix_time_from_system()
}
if offer.peer.create_offer() != OK:
return
_offers[offer.id] = offer
var offer_peer := MatchaPeer.new(_rtc_peer)
if offer_peer.create_offer() != OK: return
offer_peer.closed.connect(self._remove_offer.bind(offer_peer.offer_id))
_peers.append(offer_peer)

Engine.get_main_loop().create_timer(OFFER_TIMEOUT).timeout.connect(self._remove_offer.bind(offer_peer.offer_id))

func _create_offers() -> void:
if _offers.size() > 0: return
var unanswered_offers := _peers.filter(func(p): return p.type == "offer" and not p.answered)
if unanswered_offers.size() > 0: return # There are ongoing offers. Dont refresh the pool.

for i in range(POOL_SIZE):
_create_offer()

func _cleanup_peer_id(peer_id: String):
if peer_id in _peers:
_peers.erase(peer_id)

func _handle_offers_announcment():
if _offers.size() == 0: return # No announcements needed if we have no offers
var unannounced_offers := _peers.filter(func(p): return p.type == "offer" and not p.announced)
if unannounced_offers.size() == 0: return # There are no offers to announce

var announce_offers := [] # The array we need for the tracker offer announcements
for offer in _offers.values():
if not offer.peer.gathered: return # If we have ungathered offers we are not ready yet to announce.
if offer.announced: return # If we have announced offers something is wrong. We stop the announcement then.
announce_offers.append({ "offer_id": offer.id, "offer": { "type": "offer", "sdp": offer.peer.local_sdp } })
for offer in _offers.values():
offer.announced = true # Mark the offer as announced
for offer_peer in unannounced_offers:
if not offer_peer.gathered: return # If we have ungathered offers we are not ready yet to announce.
announce_offers.append({ "offer_id": offer_peer.offer_id, "offer": { "type": "offer", "sdp": offer_peer.local_sdp } })
for offer_peer in unannounced_offers:
offer_peer.announced = true # Mark the offer as announced
for tracker_client in _tracker_clients: # Announce the offers via every tracker
tracker_client.announce(_info_hash, announce_offers)

func _on_offer(offer: Dictionary, tracker_client: TrackerClient) -> void:
if offer.peer_id in _peers: return # Ignore the offer if we know the peer already
for peer in _peers:
if peer.peer_id == offer.peer_id: return

var peer := MatchaPeer.new(_rtc_peer)
peer.disconnected.connect(self._cleanup_peer_id.bind(offer.peer_id))
peer.connecting_failed.connect(self._cleanup_peer_id.bind(offer.peer_id))
peer.peer_id = offer.peer_id
peer.offer_id = offer.offer_id
peer.sdp_created.connect(self._send_answer_sdp.bind(offer.peer_id, offer.offer_id, tracker_client))
peer.closed.connect(self._remove_offer.bind(offer.offer_id))
peer.create_answer(offer.sdp)
_peers[offer.peer_id] = peer
_peers.append(peer)

func _on_answer(answer: Dictionary, tracker_client: TrackerClient) -> void:
if not answer.offer_id in _offers: return
var offer = _offers[answer.offer_id]
_offers.erase(answer.offer_id)

if answer.peer_id in _peers: return

offer.peer.disconnected.connect(self._cleanup_peer_id.bind(answer.peer_id))
offer.peer.connecting_failed.connect(self._cleanup_peer_id.bind(answer.peer_id))
_peers[answer.peer_id] = offer.peer
offer.peer.set_answer(answer.sdp)
var offer: MatchaPeer
for peer in _peers:
if peer.peer_id == answer.peer_id: return
if peer.type != "offer" or peer.offer_id != answer.offer_id or peer.answered: continue
offer = peer
break

if offer == null: return
offer.peer_id = answer.peer_id
offer.set_answer(answer.sdp)

func _send_answer_sdp(answer_sdp: String, peer_id: String, offer_id: String, tracker_client: TrackerClient):
tracker_client.send_answer(_info_hash, {
Expand Down
2 changes: 2 additions & 0 deletions examples/lobby/lobby.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
extends Node2D

6 changes: 6 additions & 0 deletions examples/lobby/lobby.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://d0yexpwulxnyb"]

[ext_resource type="Script" path="res://examples/lobby/lobby.gd" id="1_vobbu"]

[node name="Lobby" type="Node2D"]
script = ExtResource("1_vobbu")
2 changes: 1 addition & 1 deletion export_presets.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=true
html/export_icon=true
html/custom_html_shell=""
html/head_include=""
html/head_include="<script src=\"coi-serviceworker.js\"></script>"
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
Expand Down
4 changes: 4 additions & 0 deletions root.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ extends Node2D
func _on_bobble_btn_pressed():
get_parent().add_child(load("res://examples/bobble/bobble.tscn").instantiate())
get_parent().remove_child(self)

func _on_lobby_btn_pressed():
get_parent().add_child(load("res://examples/lobby/lobby.tscn").instantiate())
get_parent().remove_child(self)
9 changes: 9 additions & 0 deletions root.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,13 @@ offset_right = 612.0
offset_bottom = 239.0
text = "Start bobble"

[node name="lobby_btn" type="Button" parent="."]
visible = false
offset_left = 515.0
offset_top = 267.0
offset_right = 608.0
offset_bottom = 298.0
text = "Start lobby"

[connection signal="pressed" from="bobble_btn" to="." method="_on_bobble_btn_pressed"]
[connection signal="pressed" from="lobby_btn" to="." method="_on_lobby_btn_pressed"]

0 comments on commit af1c667

Please sign in to comment.