Jump to content

Networking


Laurens
 Share

Recommended Posts

Hi everyone,

 

Lately I have been reading up on networking and it occurred to me that in order for me to receive events I need to call WaitNetwork and specify a timeout. I suppose this means that it will halt all further execution (updating, rendering) until it either times out or receives an event. It could be easily solved with multithreading but I have heard rumours that this does not work well with the engine or should I be fine since I would only be doing networking in the other thread?

 

Thanks!

Link to comment
Share on other sites

So I just confirmed that WaitNetwork is indeed blocking all further executing. Specifying a timeout of 1ms in a loop is still unacceptable due to timer inaccuracy so the only way out I see now is multithreading. Should I run into any trouble if the only thing I do in the other thread is networking? Any actions taken upon network activity (for example, a "ENTITY_CREATED" event is sent across the network) would still be handled in the main thread.

 

Thanks!

Link to comment
Share on other sites

My way around that was to implement a third party winsock library. I'm using it in a multi-threaded fashion, but when you call it to check for messages on a socket, if there's no data waiting, it returns immediately with a value of 0, and if there is data waiting, it returns a value 1, in which case then instruct the library to get the packet that's waiting. Whilst that would work fine single-threaded, if you have lots of sockets then then you have to wait longer whilst you check them all for newly arrived packets.

 

I don't know, but being entity related, I can guess that you may get setMemBit errors occuring if you use a second thread dedicated for networking. If the first thread is trying to access an entity at the exact same time.

LE Version: 2.50 (Eventually)

Link to comment
Share on other sites

I'm using SDL_net, because I need SDL for many other things which LE doesn't support also, like Joysticks, Forcefeedback, Console Controllers, etc...

With SDL_net I have reliable and fast cross-platform UDP networking.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

I'm using SDL_net, because I need SDL for many other things which LE doesn't support also, like Joysticks, Forcefeedback, Console Controllers, etc...

With SDL_net I have reliable and fast cross-platform UDP networking.

 

Have you managed to get SDL and leadwerks to render to the same window? If so I'd like to see example code (sorry to hijack the thread).

 

Threading with mutexs around LW commands would be best way to handle networking using LW commands. The boost stuff is good for threads, I've got example code if you need it, although not with network stuff.

Link to comment
Share on other sites

Have you managed to get SDL and leadwerks to render to the same window? If so I'd like to see example code (sorry to hijack the thread).
I open a SDL window, and then somehow hook the LE window to use it. It's part of bigger game projects, but I could make a simple demo sometime.

Ryzen 9 RX 6800M ■ 16GB XF8 Windows 11 ■
Ultra ■ LE 2.53DWS 5.6  Reaper ■ C/C++ C# ■ Fortran 2008 ■ Story ■
■ Homepage: https://canardia.com ■

Link to comment
Share on other sites

Hi,

 

Thanks for the replies all <_<

 

I have decided to take a stab at multithreading using the LE commands as I have taken special care not to call on entities or anything in both threads. I do have a problem connecting two hosts even though it looks like I'm doing everything just like in the example on the wiki yet none of the networking events ever trigger. Netwerks initializes just fine as well and turning of my (Windows) firewall does not make a difference.

 

This is my server setting up the host and calling the Update method in a new thread.

 

const bool Game::Initialize()
{
host = CreateHost(0, 7777);

_beginthread(Game::Update, 0, reinterpret_cast<void*>(this));

return true;
}

 

This is the update method that should capture all networking events:

 

void Game::Update()
{
while (true)
{
	TEvent event;
	WaitNetEvent(host, &event);

	if (event.id == EVENT_CONNECT)
	{
		peers.push_back(event.source);
	}

	if (event.id == EVENT_PACKETRECEIVED)
	{
		// TODO
	}

	if (event.id == EVENT_DISCONNECT)
	{
		peers.remove(event.source);
	}
}
}

 

This is the test code I use:

 

THost client = CreateHost(0, 7778);
TPeer server = ConnectHost(client, HostIp("127.0.0.1"), 7777);

while (!AppTerminate() && !KeyHit())
{
	TPacket packet = CreatePacket();
	WriteLine(packet, (byte*)"Hello world!");
	SendPacket(client, server, packet);

	UpdateFramework();
	RenderFramework();

	Flip();
}

 

Any clue as to what is wrong here?

 

Many thanks!

Link to comment
Share on other sites

I created a sample application which does not run on my computer but does seem to work for the example on the wiki. I would be very grateful if anyone could tell me if this is running for them, and even more grateful if anyone could tell me what I am doing wrong.

 

Thanks!

 

#include <process.h>

#include "engine.h"
#include "netwerks.h"

bool keepRunning = true;
THost host;

void __cdecl Update(void* params)
{
while (keepRunning)
{
	TEvent event;
	WaitNetEvent(host, &event);

	if (event.id == EVENT_CONNECT)
	{
		printf("Client connected.");
	}

	if (event.id == EVENT_PACKETRECEIVED)
	{
		printf("Packet received.");
	}

	if (event.id == EVENT_DISCONNECT)
	{
		printf("Client disconnected.");
	}
}
}

int main()
{
InitializeNetwerks();
Initialize();
SetAppTitle("Game Code");
Graphics(1280, 800);
CreateFramework();

host = CreateHost();

_beginthread(Update, 0, 0);

THost client = CreateHost(0, 7778);
TPeer server = ConnectHost(client, HostIp("127.0.0.1"));

while (!AppTerminate() && !KeyHit())
{
	TPacket p = CreatePacket();
	WriteLine(p, (byte*)"Hello world!");
	SendPacket(client, server, p);

	UpdateFramework();
	RenderFramework();

	Flip();
}

keepRunning = false;

Terminate();
TerminateNetwerks();

return EXIT_SUCCESS;
}

Link to comment
Share on other sites

Wouldn't that only prevent a particular section of my[/code] to be thread safe? Say I make the entire Update method mutually exclusive. The first time the method is called it gets a lock. A second call would wait until the lock is released but I never make a second call to Update.

 

Maybe I get how mutex' work wrong. If I encapsulate all LW calls in a mutex, would that prevent LW commands from getting called concurrently? If I have two threads executing a different method but still calling LW commands from those methods, wouldn't I still be calling LW methods concurrently, even if I surround those methods in a mutex. If the former is the case than I can never send a packet from the main loop and listen for incoming packets (WaitNetEvent) in a second thread, making the entire library useless.

 

I think however that is not how it works. It would only prevent my code getting called twice which I am not doing anyway.

 

Thanks for the response though!

Link to comment
Share on other sites

I just tried the LUA example Josh posted on the Wiki by dragging server.lua and client.lua on engine.exe, only to get a "CreateHost: attempted to call a nill value" (or something of that sort). Is it safe to say networking is completely broken and I should file a bug report?

Link to comment
Share on other sites

Wouldn't that only prevent a particular section of my[/code] to be thread safe? Say I make the entire Update method mutually exclusive. The first time the method is called it gets a lock. A second call would wait until the lock is released but I never make a second call to Update.

 

Maybe I get how mutex' work wrong. If I encapsulate all LW calls in a mutex, would that prevent LW commands from getting called concurrently? If I have two threads executing a different method but still calling LW commands from those methods, wouldn't I still be calling LW methods concurrently, even if I surround those methods in a mutex. If the former is the case than I can never send a packet from the main loop and listen for incoming packets (WaitNetEvent) in a second thread, making the entire library useless.

 

I think however that is not how it works. It would only prevent my code getting called twice which I am not doing anyway.

 

Thanks for the response though!

 

Indeed - you can have no LW command issued in a thread while another LW is occurring in the main thread.

 

You handle it this way:-

 

 

mainLoop:-
{
 gameLogic(with non LW commands)
 mutex(LW) 
 {
    updateworld
    networkstuff
    renderworld
 }
 render none LW stuff
 flip
}

thread:-

loop:-
{
  non LW commands
  mutex(LW)
  {
     LW network commands - which cannot block or wait
  }
  sleep
}

Link to comment
Share on other sites

Just tried mutex' first. Same end-result sadly. I then even tried creating two projects, a server that purely listens and does nothing else LW related and a client that just connects, sends some data and disconnects, nothing else. Still the same result (that is no result). I used PortQryV2 to confirm that the port is actually open and it is, so CreateHost seems to work and I suspect the problem must be with ConnectHost.

 

I am about to give up on this and switch to RakNet unless Josh could take a look at this and confirm whether it is truly a bug or not.

 

Many thanks for the help everyone!

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...