Enet communication within threads

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
User avatar
sherpal
Prole
Posts: 17
Joined: Wed Sep 14, 2016 9:29 am
Location: Belgium

Enet communication within threads

Post by sherpal »

Hello,

I'd like to set up communication using enet for a multiplayer game.
My idea, maybe it's a bad one (in that case you can tell me why, but I'd still want to know what is going wrong here), is to put all communication within love.thread's, and then to send them back to the main process, using channels.

I'm first trying to set up a simple "ping-pong" protocol. Thanks to that thread (viewtopic.php?t=76757), I was able to do it within the main process, i.e. within the ping-pong file (see below). Then, I tried to sort of translate that protocol by using love.threads.
The problem now is that when I launch client.lua after having launched server.lua (again, see below), both program crash nearly instantly.

The only hypothesis I have is that the while true loops in the thread are "too fast" making too much communication. But I tried to cut that using stuff like

Code: Select all

t = os.time() + 2
repeat until os.time() >= t
but that doesn't change anything.

As I'm new to networking, and to threads, maybe there is something obvious that I miss.

My computer is a windows 7 64 bytes, and I tried on a windows 10 64 bytes, it does the same...

Thank you verry much for you answers!
Here are the code snippets. The "true" in the call of the initialize method makes the program a server, and a call with "false" would make it the client, see attached. Don't pay attention to the "things_to_print", it just takes care of the display of strings.

Code: Select all

-- main.lua
require "things_to_print"
pingpong = require "pingpong.pingpong"


function love.load()
	pingpong:initialize(true)
end

function love.update(dt)
	pingpong:onUpdate()
end

function love.draw()
	things_to_print:print()
end

Code: Select all

-- pingpong
local pingpong = {}

function pingpong:initialize(server)
	self.server = server
	
	if server then
		self.thread = love.thread.newThread('pingpong/simple_server.lua')
	else
		self.thread = love.thread.newThread('pingpong/simple_client.lua')
	end
	
	self.channel_receive = love.thread.newChannel()
	self.channel_send = love.thread.newChannel()
	
	self.thread:start(self.channel_send, self.channel_receive)
end

function pingpong:onUpdate()
	local err = self.thread:getError()
	if err then things_to_print:add("error", err, 10, 30) end

	local event = self.channel_receive:pop()
	
	things_to_print:add('updatepingpong', 'Update: ' .. os.date(), 10, 225)
	
	while event do
		local t = os.date()
		things_to_print:add(
			'message_received',
			'message received at ' .. t .. ': ' .. event.type .. ', ' .. event.data,
			10, 300
		)
		self.channel_send({peer = event.peer, message = 'hello'})
		event = self.channel_receive:pop()
	end
end

return pingpong

Code: Select all

-- server.lua
require "enet"

local channel_receive, channel_send = ...

local host = enet.host_create("*:6789")


while true do
	local m = channel_receive:pop()
	
	if m and m.peer and m.message then
		m.peer:send(m.message)
	end

	local event = host:service(100)
	
	while event do
		channel_send:push(event)
		event = host:service()
	end
end

Code: Select all

-- client.lua
require "enet"

local channel_receive, channel_send = ...

local host = enet.host_create()
local server = host:connect("localhost:6789")

while true do
	local m = channel_receive:pop()
	
	if m and m.peer and m.message then
		m.peer:send(m.message)
	end
	
	local event = host:service(100)

	while event do
		channel_send:push(event)
		event = host:service()
	end
end
Attachments
server.love
(1.98 KiB) Downloaded 119 times
client.love
(1.99 KiB) Downloaded 126 times
User avatar
Tjakka5
Party member
Posts: 243
Joined: Thu Dec 26, 2013 12:17 pm

Re: Enet communication within threads

Post by Tjakka5 »

While looking trough your code I saw you still use Thread:getError, this function is deprecated; you should use the love.threaderror callback instead.

Also, I don't know how effective it would be to do networking in threads.
I can see it useful when using http to download files and such, but I'm pretty sure that enet has a internal buffer which should be read from.
The only thing happening then is that you're getting the info a tad later. That needs to be sent trough channels, that should be put in a buffer, and that should be read from again in the main thread.

I might be wrong on this though. Someone else with more knowledge about networking should give feedback on this.
User avatar
sherpal
Prole
Posts: 17
Joined: Wed Sep 14, 2016 9:29 am
Location: Belgium

Re: Enet communication within threads

Post by sherpal »

Thank you for your answer, Tjakka5.

I didn't notice for the getError method, thanks for letting me know.


I admit that my protocol using a channel in addition does not look appealing in this form. Here, if we schematize what I'm doing, it's looking like this:
Main Thread <--channel--> Communication Thread <--Enet--> Other computer communication thread <--channel--> other computer Main Thread.

Now, my final goal is to make a multiplayer game, in which one computer, in addition to be a player, would be the server that manage all computations. So I would typically do:
- Main thread for displaying to the screen
- A second communication thread, that will take care of receiving (and sending) messages from (to) other players (the clients)
- A third thread, that will take care of the game computations

By putting the communications in a specific thread, I make it so that messages from other players to the server do not interact with the "displaying thread" of the server computer (I don't know if I'm clear enough now).

Of course, I could put the communication and the computations inside the same thread, but that's the same problem... The drawback isof this method is that there is one more step in this protocol, but the advantage is that it is much more symmetric, hence easier to implement.
User avatar
pgimeno
Party member
Posts: 3607
Joined: Sun Oct 18, 2015 2:58 pm

Re: Enet communication within threads

Post by pgimeno »

I've swapped channel_receive and channel_send to put them in the order they appear in the thread code, and it worked for me.

Code: Select all

        self.thread:start(self.channel_receive, self.channel_send)


Edit: Never mind, I misunderstood how it worked and what was to be expected.
User avatar
sherpal
Prole
Posts: 17
Joined: Wed Sep 14, 2016 9:29 am
Location: Belgium

Re: Enet communication within threads

Post by sherpal »

pgimeno wrote:I've swapped channel_receive and channel_send to put them in the order they appear in the thread code, and it worked for me.

Code: Select all

        self.thread:start(self.channel_receive, self.channel_send)


Edit: Never mind, I misunderstood how it worked and what was to be expected.
Thanks for having tried! :awesome:
On the one hand, I'm disappointed that it wasn't a solution, but on the other hand, I'm glad 'cause if that was a solution, I would not understand anything anymore :crazy:
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 1 guest