Skip to content

Commit

Permalink
Improved connectivity check and support multiple reading/writing
Browse files Browse the repository at this point in the history
  • Loading branch information
Abdera7mane committed Jan 12, 2022
1 parent 604b143 commit ca8c91e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 42 deletions.
70 changes: 60 additions & 10 deletions addons/Discord RPC/DiscordRPC.gd
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ signal rpc_error(error)
# Connection states
enum {
DISCONNECTED,
CONNECTIING,
CONNECTING,
CONNECTED,
DISCONNECTING
}
Expand All @@ -113,13 +113,22 @@ enum {
ERR_CLIENT_NOT_FOUND
}

const PING_INTERVAL_MS: int = 5_000
const PING_TIMEOUT_MS: int = 10_000


# Discord RPC version
const VERSION: int = 1

const DISCORD_API_ENDPOINT: String = "https://discord.com/api/%s"

var _ipc: IPC setget __set
var _modules: Dictionary setget __set
var _next_ping: int setget __set
var _last_ping: int setget __set
var _sent_ping: bool setget __set
var _recieved_pong: bool setget __set
var _ping_nonce: String setget __set

# Current status of DiscordRPC instance
var status: int = DISCONNECTED setget __set
Expand Down Expand Up @@ -156,7 +165,7 @@ func establish_connection(_client_id: int) -> void:
return

client_id = _client_id
status = CONNECTIING
status = CONNECTING
set_process(true)
for i in range(10):
var path = IPC.get_pipe_path(i)
Expand All @@ -169,7 +178,7 @@ func establish_connection(_client_id: int) -> void:
emit_signal("rpc_error", ERR_CLIENT_NOT_FOUND)
shutdown()

# Weather connected to a Discord client or not
# Whether to connected to a Discord client or not
func is_connected_to_client() -> bool:
return _ipc.is_open() and status != DISCONNECTED

Expand Down Expand Up @@ -248,11 +257,19 @@ func unsubscribe(event: String, arguments: Dictionary = {}) -> void:

# Closes the current connection to the discord client
func shutdown() -> void:
status = DISCONNECTING
_ipc.close()
status = DISCONNECTED
set_process(false)
emit_signal("rpc_closed")
if status != DISCONNECTED:
set_process(false)
status = DISCONNECTING
_ipc.close()
status = DISCONNECTED
_next_ping = 0
_last_ping = 0
_sent_ping = false
_recieved_pong = false
_ping_nonce = ""
client_id = 0
scopes = []
emit_signal("rpc_closed")

func install_module(module: IPCModule) -> void:
if not _modules.has(module.name):
Expand All @@ -274,13 +291,14 @@ func ipc_call(function: String, arguments: Array = []):

func _handshake() -> void:
if self.status == CONNECTED:
push_error("Already handshacked !")
push_error("Already sent a handshake !")
return

var request: IPCPayload = IPCUtil.HandshakePayload.new(VERSION, self.client_id)
var response: IPCPayload = yield(self._ipc.send(request), "completed")
if response.op_code != IPCPayload.OpCodes.CLOSE and not response.is_error():
status = CONNECTED
_next_ping = OS.get_ticks_msec() + PING_INTERVAL_MS
emit_signal("rpc_ready", response.data["user"])
return
emit_signal("rpc_error", ERR_HANDSHAKE)
Expand All @@ -295,17 +313,49 @@ func _process(_delta: float) -> void:
_ipc.poll()
if not _ipc.is_open():
shutdown()
return

if status != CONNECTED:
return

var current_ticks: int = OS.get_ticks_msec()
if not _sent_ping and _next_ping <= current_ticks:
var payload: IPCPayload = IPCPayload.new()
payload.op_code = IPCPayload.OpCodes.PING
_ping_nonce = payload.nonce
_ipc.send(payload)
_sent_ping = true
_last_ping = current_ticks

elif _sent_ping:
if not _recieved_pong and _last_ping + PING_TIMEOUT_MS <= current_ticks:
shutdown()
elif _recieved_pong:
_sent_ping = false
_recieved_pong = false
_next_ping = current_ticks + PING_INTERVAL_MS

func _on_data(payload: IPCPayload) -> void:
if payload.is_error():
push_error("IPC: Recieved error code: %d: %s" % [payload.get_error_code(), payload.get_error_messsage()])

emit_signal("raw_data", payload)

match payload.op_code:
IPCPayload.OpCodes.CLOSE:
shutdown()
IPCPayload.OpCodes.PING:
var reply: IPCPayload = IPCPayload.new()
reply.op_code = IPCPayload.OpCodes.PONG
_ipc.send(reply)
IPCPayload.OpCodes.PONG:
_recieved_pong = payload.nonce == _ping_nonce

var signal_name = payload.event.to_lower()
if payload.command == "DISPATCH" and has_signal(signal_name):
if payload.command == DiscordRPCUtil.Commands.DISPATCH and has_signal(signal_name):
callv("emit_signal", [signal_name] + payload.data.values())


func _to_string() -> String:
return "[DiscordRPC:%d]" % self.get_instance_id()

Expand Down
35 changes: 3 additions & 32 deletions addons/Discord RPC/ipc/IPC.gd
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ class_name IPC

signal data_recieved(payload)

const PING_INTERVAL_MS: int = 5_000
const PING_TIMOUT_MS: int = 10_000

var _pipe: IPCPipe
var _responses_pool: Array
var _requests_pool: Array
Expand Down Expand Up @@ -91,7 +88,7 @@ func is_open() -> bool:
func close() -> void:
if _pipe:
_pipe.close()
if _io_thread and _io_thread.is_active():
if _io_thread and _io_thread.is_active() and _io_thread.is_alive():
_semaphore.post()
_io_thread.wait_to_finish()
_pipe = null
Expand All @@ -113,46 +110,20 @@ func _connection_loop(data: Array) -> void:
var semaphore: Semaphore = data[1]
var pipe: IPCPipe = data[2]

var next_ping: int = OS.get_ticks_msec() + PING_INTERVAL_MS
var ping_nonce: String
var sent_ping: bool
var recieved_pong: bool
var last_ping: int

while pipe.is_open():
if pipe.has_reading():
while pipe.has_reading():
var payload: IPCPayload = self.scan()
if sent_ping and payload.nonce == ping_nonce:
recieved_pong = true
continue
mutex.lock()
self._responses_pool.append(payload)
mutex.unlock()
if payload.op_code == IPCPayload.OpCodes.CLOSE:
break

elif self._requests_pool.size() > 0:
while self._requests_pool.size() > 0:
mutex.lock()
self.post(self._requests_pool.pop_back())
mutex.unlock()

# Test if the connection is still active
if not sent_ping and OS.get_ticks_msec() >= next_ping:
var payload: IPCPayload = IPCPayload.new()
payload.op_code = IPCPayload.OpCodes.PING
ping_nonce = payload.nonce
post(payload)
sent_ping = true
last_ping = OS.get_ticks_msec()

if not recieved_pong:
if OS.get_ticks_msec() >= last_ping + PING_TIMOUT_MS:
break
else:
sent_ping = false
recieved_pong = false
next_ping = OS.get_ticks_msec() + PING_INTERVAL_MS

semaphore.wait()

static func get_pipe() -> IPCPipe:
Expand Down

0 comments on commit ca8c91e

Please sign in to comment.