LAN search

BlitzMax Forums/BlitzMax Beginners Area/LAN search

PowerPC603(Posted 2012) [#1]
Hi all,

I want to test some networking stuff using BMax.

I can get the server and client running if the server's IP is known by the client program, like hardcoding it into the code of the client.

But, I want the client to search for the server itself in a LAN.
It's certainly possible if I would give my game to someone else, that his server won't run on the exact same IP as mine.
Or if me and my son play the game, he would want to host the server and I cannot connect to it, because the IP of the server is hardcoded to the computer which I'm playing on.

We play the game "C&C Generals" quite often, it has some sort of lobby that lists all servers and players on the network.
I want to have such a system as well, or just that it scans the network for servers on a specific port and just accepts the first one it finds.

I've also looked into BNet, GNet, TNet, ..., but I prefer to not use any of those libs.
GNet seems to add the server to some list on the internet, from which it retreives data like the name of the game, name of server, ... (correct me if I'm wrong).
I want to have my server private, so it cannot be found outside the LAN.

I've got this already:
Server:
SuperStrict

Graphics 400, 300, 0



Global Players:Int



Type TServer
	Field Port:Int = 9876
	Field Socket:TSocket
	Field SocketStream:TSocketStream
	Field Stream:TStream

	Method New()
		' Try to create a new network socket
		Socket = CreateTCPSocket()
		' Check if it was successfull
		If Socket = Null Then RuntimeError "Failed to create socket"
		' Check if the port can be bound to the socket
		If Not BindSocket(Socket, Port) Then RuntimeError "Failed to bind port 10001 to socket"
		' Start listening on the socket for new connections (maximum new connections simultanously at any time = 8, since there are maximum 8 monopoly players)
		SocketListen(Socket, 8)
	End Method

	Method Process()
		If SocketAccept(Socket) <> Null Then
			Print "New connection detected"
			Players = Players + 1
		EndIf
	End Method

	Method CleanUp()
		CloseSocket(Socket)
	End Method
End Type

' Create a new TServer and setup the socket for listening
Global Server:TServer = New TServer



' Main loop
While Not KeyHit(KEY_ESCAPE)
	Cls

	Server.Process()

	DrawText "Connections detected: " + Players, 10, 10

	Flip
Wend

Server.CleanUp()

End


Client:
SuperStrict

Graphics 400, 300, 0



Type TClient
	Field IP:Int = (192*256*256*256) + (168*256*256) + (123*256) + 7
	Field Port:Int = 9876
	Field Socket:TSocket
	Field Stream:TStream

	Method New()
		' Try to create a new network socket
		Socket = CreateTCPSocket()
		' Check if it was successfull
		If Socket = Null Then RuntimeError "Failed to create socket"
		' Try to connect to the server using broadcasting
		If ConnectSocket(Socket, IP, Port) Then Print "Server found" Else End
	End Method

	Method CleanUp()
		CloseSocket(Socket)
	End Method
End Type

' Create a new TClient
Global Client:TClient = New TClient

While Not KeyHit(KEY_ESCAPE)
	DrawText "Server found...", 10, 10
	Flip
Wend

Client.CleanUp()

End


The IP is the client is hardcoded here to 192.168.123.7, this is the IP of my laptop.
If I would run this code on my other computer, it will fail as the IP's don't match.

I also tried to use a For-Next loop to cycle through all IP's (last digit ranging from 0 to 254), using ConnectSocket, but it freezes the client if no computer is found with any of those IP's.
As soon as it starts connecting to 192.168.123.0 (or any other IP, except 192.168.123.7), it just freezes and I have to kill the client's process using taskmanager.

I also tried creating a stream on the client, but it didn't detect the server, and the server didn't see the incoming stream from the client.

I'm already searching throughout the forum and using google without success for over 3 hours now.


Derron(Posted 2012) [#2]
You have to broadcast to your lan.

In your case: 192.168.123.255

It may be required to enable broadcast-mode.
If using GNET (which you don't) as base you will have to make some tweaks which I have circumvented with some parts BNetEx.

Your "Announce packet" which you send to your broadcast-address (which can differ from x.x.x.255 but most times will be that *.255) should contain your magic header and the game information:
- server ip
- game title
- free/occupied slots
- ...


The most important comes at the end:
As you already found out: the PC freezes if sending packets to x.x.x.0-x.x.x.254.
That is because you send by "TCP/IP" - a protocol needing responses from the remote host ("did you receive my package?" ... waiting ...waiting ... "yes"/timeout)

Send your packets by using "UDP".
So in your case (if TCP is needed that much - instead of developing reliable/ordered udp-thingies) make something like an "info channel" in UDP and your "game channel" in TCP.
The first can then be used for update notifications, game announcements, lobby chats ...


bye
Ron

edit: to clearify something: you can send to .0-.254 instead of broadcasting to .255. But then you have to take care of not blocking the sockets.
- I had some problems with an older BNetEx-Implementation here on linux and had to delay the announcepackets every 20 IPs (0-19,100ms delay, 20-39, 100ms ...) else they did not get received by virtual-box-windows-clients.
To come along with that problem: only delete old "announcements" after a "maximum looptime" * factorBiggerThanOne.

Last edited 2012


PowerPC603(Posted 2012) [#3]
Many people are struggling with this, as I found out by searching the forums.

I also tried using the broadcast address (192.168.123.255), I forgot to mention that.
Using the above code, the client just ends instead of waiting and crashing on ConnectSocket.
Also, the server didn't see the connection coming in.

I tried searching my router-settings for some broadcast-enable option, but all I found was enabling the SSID broadcast of the router itself.
There seems to be no option to allow broadcast messages in the LAN.

I just tried UDP (I just modified CreateTCPSocket into CreateUDPSocket), but now I didn't get any response, not even with the correct address.

I find this stuff to be confusing.
You can do this for example:
' Try to create a new network socket
Socket = CreateTCPSocket()
' Turn the socket into a stream
Stream = CreateSocketStream(Socket)
' Send data over the stream
Stream.Send( xxxxx )


But where would it be sent? The target IP and port haven't been specified yet and I see no option to do this, other than using ConnectSocket, where you can specify the IP and port.
So I don't know how to send "announce packets", as not even the broadcast address can be set anywhere.

It was way easier using Blitz3D's system. I got a server-client system setup there in less than an hour or so (just using a fixed address, not searching the server as I want to do here).

The game I'm trying to create is "Monopoly" in 3D with a maximum of 4 players for now.
One player creates a network game (this option sets up the server, which runs in the game itself, like C&C Generals, and connects the client immediately to the server), the other computers could just run the game and detect the server, which will allow them to connect to the server.
So, there won't be a separate server.exe file to be started or whatever.

Last edited 2012

Last edited 2012


Derron(Posted 2012) [#4]
That is why I mentioned BnetEx (blitzmax udp wrapper thingy).

With Blitzmax-original UDP-functions I wasn't able to make it work how I'd liked it to work.

With UDP you declare the target-port but not the one you are using (sender Port).
So to get your sending port you have to send that port back from the client (may be in the answer to the announce packet).
To see what things are send/receives - install Wireshark (you can even write a LUA-Packet-Analyzer for YOUR application - so wireshark can log "Login Attempt" instead of "packet data XYZ").
Install it on both computers - so you see on which side the problem occours. Tip: filter the log by your local IPs ... so you don't log the http/.../-traffic.

Your router is not responsible for broadcasting. The NIC of your "server" has to be configured right (will be in most cases).
Prior to new firewalls in windows/linux-systems you were able to "ping -b x.x.x.255" and get responses from all connected systems.

Another thing to take into consideration:
Server is on LAN ... connected to Router
Client is on WLAN ... connected wireless to WLAN-AP which is connected to the Router
Client B is on LAN ... connected to WLAN-AP

Problems which can occour: different Subnet-Masks, different Subs (x.x.1.0/255 and x.x.2.0/255) - if this is the case, broadcasting will not work without additional work or nice AP/routers which cascade that things).

Another problem occours if IPv4 gets abandoned from your OS ... IPv6 has no broadcast-mode (but that will take some years from now).


As the code I use is a bit more complex (event driven, package management) I'm not able to give complete sources now:

So it is modified, untested, shortened (some parts missing).
The "TNetworkPacket"-Type is something to make the packet-data better manageable (contains flags for reliability, order, eventType, ...).

[bbcode]
import "external/bnetex.bmx" 'Version: 1.70 Beta

Type TGameNetwork
Global localPort:int = 4544 ' for normal ingame packets
Global infoPort:int = 4543 ' only for lan (up to now) - announcements
...
'to interact with clients/host
field infoStream:TUDPStream = Null
...
Field announceEnabled:int = 0
Field announceTitle:string = "unknown" ' title used when announcing
Field announceTime:Int = -1 ' Main Announcement Timer
Field announceTimer:Int = 750 ' Main Announcement Timer
Field announceToLan:int = 1


Method InitInfoStream()
if infoStream then return
If infoStream = Null Then infoStream = new TUDPStream
if infoStream = null then RuntimeError "Couldn't create UDP stream for LAN game announcements"
infoStream.Init()
infoStream.SetLocalPort(self.infoPort)
infoStream.SetRemotePort(self.infoPort)
End Method

Method StartAnnouncing(title:string, toLan:int = 1)
self.InitInfoStream()
self.announceEnabled= true
self.announceTitle = title
self.announceTime = Millisecs()
self.announceToLan = toLan
End Method

Method FindGames()
'if inGame then return
if not infoStream then print "init stream";self.InitInfoStream()

'custom implementation...you will have to take care yourself
local packet:TNetworkPacket = self.ReceiveInfoPackets()
if packet <> null
local obj:TNetworkObject = TNetworkObject.FromPacket(packet)
if obj and callbackInfoChannel then callbackInfoChannel(obj)
endif
End Method

Method Update:int()
if self.isServer then self.SendGameAnnouncement()
self.FindGames()


if self.server then self.server.Update()
if self.client
if self.pingTime < Millisecs()
self.client.Ping()
self.pingTime = Millisecs()+self.pingTimer
endif
self.client.Update()
endif
End Method

Method SendGameAnnouncement()
if not self.announceEnabled then return
if self.announceTime > Millisecs() then return
if self.GetFreeSlot() = 0 then return

self.announceTime = Millisecs() + self.announceTimer
local obj:TNetworkObject = TNetworkObject.Create(NET_ANNOUNCEGAME)
If self.announceToLan
'Print "NET: announcing a LAN game to " +GetBroadcastIP(self.GetMyIP())
obj.setInt(1, self.GetFreeSlot())
obj.setString(2, self.announceTitle)
obj.setInt(3, self.GetMyIP())
obj.setInt(4, self.localPort)
self.SendInfoPacket(obj.toPacket(), GetBroadcastIP(self.GetMyIP()), self.infoPort)
Else
rem

Print "NET: announcing a ONLINE game "
Local Onlinestream:TStream = ReadStream("http::www.youronlinewebsite.xx/lobby.php?action=AddGame&Titel="+UrlEncode(Game.title)+"&IP="+OnlineIP+"&Port="+host.port+"&Spieler="+Self.getFreeSlot()+"&Hashcode="+LastOnlineHashCode)
Local timeouttimer:Int = MilliSecs()+5000 '5 seconds okay?
Local timeout:Byte = False
If Not Onlinestream Then Throw ("Not Online?")

While Not Eof(Onlinestream) Or timeout
If timeouttimer < MilliSecs() Then timeout = True
Local responsestring:String = ReadLine(Onlinestream)
If responsestring <> Null AND responsestring <> "UPDATEDGAME" Then LastOnlineHashCode = responsestring
Wend
CloseStream Onlinestream
endrem
EndIf
End Method

Method ReceiveInfoPackets:TNetworkPacket()
if infoStream.RecvAvail()
While infoStream.RecvMsg() ; Wend
Local size:Int = infoStream.Size()
If size
Local data:Byte[size]
local packet:TNetworkPacket = New TNetworkPacket
packet._bank.resize(size)
MemCopy(packet._bank.buf(),infoStream.RecvBuffer,size)
infoStream.Flush()
packet.ip = infoStream.MessageIP
packet.port = infoStream.MessagePort
return packet
EndIf
endif
return null
End Method

Method SendInfoPacket:int(packet:TNetworkPacket, ip:string, port:int)
'set broadcast (for linux)
infoStream.SetBroadcast(true)

if packet and packet._bank.size()
Local length:int = packet._bank.size()
if length > 0 and not infoStream.write(packet._bank.buf(), length) then throw "Net: Error writing to Networkmessage buffer " + length
if not infoStream.SendUDPMsg( HostIP(ip), port ) then throw "Net: Error sending udp message to " + ip+ " :" + port
endif
return true
End Method

...

End Type
[/bbcode]

Maybe some parts of the code above will help.


bye
Ron


Zeke(Posted 2012) [#5]
You can use RakNet:


http://www.byrathon.com/omat/lan.zip


PowerPC603(Posted 2012) [#6]
I searched some more and found this:
http://www.blitzmax.com/Community/posts.php?topic=75694
The source-code of Ghislain (post 8) got me started.
His code was in one exe, but I've succesfully split it up into a separate server and client exe.



I've got something working already, using UDP.

The server broadcasts a message every second to 192.168.123.255.
The client already picks it up and displays the message on-screen.

But using SocketRemoteIP doesn't return the server's IP.
This is not directly a problem, because I can add the server's IP in the message itself as text (or just the last digits of the server's IP will do fine as well).

Right now, the server can already broadcast the game-name (Monopoly3D) as a byte array (I've written a function to convert a string to a byte array, I found no built-in function or method that does this) and the client reads it and displays it on-screen.

By using the IP in the message (I'm still writing code to get that out of it), I can probably create a new TCP-stream to connect to the server.

So, I'm using an UDP broadcast message to send the IP of the server as text throughout the entire network, so every client in the network will know the IP of the server by analyzing the broadcast-message.
In turn, the client will need to use that info to setup a TCP-connection to the server.

When it's fully functional, I can post the code.



EDIT: After testing a bit more, I've finally made it working completely, but the code still needs some refining.

When the server is started, the broadcast message is constructed (Game-name and server-ip are stored) and sent over the network once every second using UDP to address 192.168.123.255.

The client scans it and extracts the game-name and the server-ip.
If the game-name isn't correct, there might be another application using the same port, so skip it.
If the game-name is correct, use the server-ip from the message to establish a connection to the server using TCP.
Also send the player's name to the server, using the new TCP connection.

The server checks for new TCP connections, sees a new player and creates a new TPlayer instance, in which the Socket is stored.

The server also loops through all players and checks if that player sent some data.
Currently, the sent data is stored as the name of the player.

The name of every player is also printed onscreen on the server's window.

Last edited 2012


PowerPC603(Posted 2012) [#7]
This is my current code:

Server:


Client:


This works perfectly and I just tested it with both computers at home.
The server broadcasts a message throughout the entire network and runs on my laptop.

The clients run on both the laptop and desktop pc.
They listen for a broadcast message.
As soon as the message is found, the client creates a TCP connection to the server after analyzing the broadcast message, which holds the IP of the server.
Also, the client sends the name of the player to the server as a command ("Nam PowerPC603" => command = "Nam", playername = "PowerPC603").
As soon as the client established the connection, the client stops scanning for the broadcast message, as the server has been found already.

The server accepts the connection and creates a new TPlayer instance.
The server also loops through all players and prints their name on the screen.
When looping through all players, it checks if any player has sent data to the server.
If there is data, the message is analyzed and processed.
There is only one command built-in right now ("Nam").
When the first 3 characters of the player's message equals "Nam", the rest of the message is stored in the player's account as his name.

The server also checks if a client disconnects.
In such a case, the player's instance will be deleted and will no longer be processed.

Last edited 2012


Derron(Posted 2012) [#8]
			' Only broadcast every second
			If Time > (BroadcastLastTime + 1000) Then
				' Send the broadcast message
				SocketUDP.Send(Varptr BroadcastMsg[0], BroadcastMsg.length)

				' Update the timer
				BroadcastLastTime = BroadcastLastTime + 1000
			EndIf


means: first broadcast is not done at 0 seconds, but 1 second (time > broad... + 1000)

means: If time since last processing is > 1000, decrease "lastTime" by 1000 and check again next time.

means: if your app hangs for 20 seconds, it broadcasts 20 times (each time "Broadcast()" is run).

Better is to use (except you intented it that way)

			' Only broadcast every second
			' first broadcast is done on first "Broadcast()"-call
			' to add a delay - .. > BroadcastLastTime + delay
			If Time > BroadcastLastTime Then
				' Send the broadcast message
				SocketUDP.Send(Varptr BroadcastMsg[0], BroadcastMsg.length)

				' Update the timer and add 1000 ms to wait
				BroadcastLastTime = Millisecs() + 1000
			EndIf


bye
Ron


edit:
to make your code more manageable:
- make a type like "TConnection" with fields both (client and server) share
- extend both of them from it
- move the helper-function TextToBytes/BytesToText ... to that TConnection
- within TClient.new and TServer.new use "super.new()" - so your TConnection.new can handle some of the initialization.

What can also be a nice thing to have: in my application each Network"client" has fields for server and client. Each of them can get chosen to handle as new server (maybe server drops out - internetgames ...)

Last edited 2012


PowerPC603(Posted 2012) [#9]
I'm still revising the code, but I'll look into your suggestions.
I'm still new to network code, but I grasp the basic concepts about it.
I'm also creating new methods to enable/disable broadcasting, getting the server'sip to calculate the broadcast ip (bitwise or the serverip with 255), and much more.

This code is currently the minimum code required to broadcast a message and allow clients to find the server on their own, without the need to preset the server's ip in the client code.
And it should be easy to follow for other people as well to start with something like this.



The broadcasting will stop when the game is actually started.
Since it's a monopoly game, all players need to connect to the server before the game is started.
As soon as the game starts, no new connections will be allowed (since such a game won't allow new players to join in the middle of the game).
Broadcasting won't be needed then, as well as AcceptPlayers.

Broadcasting 20 times in a row won't be a problem as well.
The client will change later to hold a list of servers, instead of connecting to the first monopoly server it finds.

Once the list is created, new broadcast messages will still be processed by the client, and it will update the server-list when there is a change (new server or server gone).
Otherwise, the list won't be updated.
But that will change later when I have the basic interface of the game working to allow players to create a game or to join one (the lobby of the game).



In case of drop-out of the server:
You mean that every client should start his own server?
And all players connect to one server, which will be the master-server?
And the master-server transmits his status (in case of monoply: the owner of each property and the money of each player and who's turn it is) every few seconds to all other servers?

So in case the master-server drops out, another server can take over where the master-server dropped out? And of course, the new master-server needs to contact each player to re-connect to him?

This would get quite complex I guess.



I guess I'll make one server program which can process several "games" of monopoly.
All players connect to this server and choose a game to join.
So one player will have to start the server application and the client as well.

In case of MMO's: there is also only one server. If it drops out, all players are disconnected and have to wait for the server to come back online before they can continue playing.

Last edited 2012


Derron(Posted 2012) [#10]
In normal "games" you don't have to start a server.exe

If the "Master-Server" (aka "host") is dropping out, there should be an algorithm (or just "second joined client" - as first was the host) which decides who is hosting next - all clients should come to the same conclusion who is hosting. Then they just "swap" the currently used server to the new ones.
Then all of them await the "please welcome our new host - me :D"-packet, ACKnowledge it and then game processing can continue.
From then all data the new host has is used as master data.

There should not be information loss as all clients store data of the board-properties, bought/sell-logs and so on.
What might be a bit out of synchronicity is "movement data" (data each client calculates by itself but gets "overwritten" by data from the master/host).


I know that an mmporpg does not do it that way - but they use that for a) being able to charge money :D and b) for holding additional / dynamic data.

I also admin that older games did not allow for changing host (I remember WarCraft 3 ...there additional .exe-tools added methods of reconnection ingame if some lost the connection).
Which reminds me to: if you plan to allow "play over internet" - also allow for reconnect within XX seconds.
For doing so - each game should get a unique hash - and each client should get a "game+client"-hash.
So a person with a different IP is able to authentificate and send a "join"-message to the host (even if he was the host and is now only a client connecting to the new host).

I know that this sounds complicated - and you are not forced to use the suggestions - some of them will certainly be of use if you intend to make it available to a broad mass of customers/gamers/players...

I'm also creating new methods to enable/disable broadcasting, getting the server'sip to calculate the broadcast ip (bitwise or the serverip with 255), and much more.


As I mentioned "BnetEx" - it does already include functions for doing it. Think GNet also uses some methods.
The problem here is: it is not x.x.x.255 everytime - so you have to ask the OS/system what broadcast-ip is used.

Another problem which can occour is: Notebooks are having LAN and WLAN most of the time. If both are enabled you could end up having more than one NIC your game could use.
Which one to take?
- for that case you could
a) make a option-dropdownbox with selectability
b) make a config-file with a "useIP=XXXX" or "useNIC=1/2/..."
c) listen on both for announcements (get ready for potential trouble)
d) own ideas?

For myself I use option b) for being the easiest one.

bye
Ron


PowerPC603(Posted 2012) [#11]
The game won't be sold, as it's just a learning project to combine networking and 3D stuff all together for myself.

The game will mostly run at home.

I could make the project open-source and upload the entire game WITH source-code to the internet for anyone to use. Then they can make moifications for themselves if needed.



In case of server-dropouts: if one player hosts the game and his game crashes, the server will crash as well.
Or it may happen that the player just leaves the game.

Either way, that player is disconnected from the game.
Another player's game could take over the hosting of the game, but without the player that left.

Monopoly games usually don't allow players to join mid-game, so reconnecting him (if this version would allow it) would give several difficulties:

1) Reserve the disconnected player's properties to give it back if he re-connects.
Doing this would disallow the other players to buy those properties again, since they are reserved for the disconnected player, so the server can give them back if he reconnects.

This option is not good, as the disconnected player might just leave the game and do something else, ruining the game for the other players who can't buy the properties anymore.



2) Replace the disconnected player by a computer player and transfer the disconnected player's properties to the computer player.
This isn't good as well, because the disconnected player might want to rejoin (in case his game just crashed).
So he cannot play anymore, as he lost his properties, money, ...



3) Just give all properties of the disconnected player back to the bank, so the other players can buy them again.

Not good either, as the disconnected player might want to rejoin the game.
Since his properties were given back to the bank, he would start with a blank account and would go bankrupt in no-time anyway if the game is near the end.
This option could also be exploited by players who are facing bankruptcy.
They would just disconnect, rejoin and they start with a blank account with the default amount of money.


So, either option I choose isn't good anyway.



I think I'll just allow one player to host the game, and let others to connect to his server.

In case a player drops out, his properties are given back to the bank and the player cannot rejoin the game.
If the hosting player drops out, it's game over for everyone, as they all lose their connection.



It's my first BIG game project (I created a few smaller ones without networking stuff and were never quite finished), so I don't want to start writing backup-servers, doing anything weird with the rules, ...

It's only a project to learn something about networking, multiple player handling, 3D stuff with animations, creating an interface, AI players, ...

Last edited 2012


Derron(Posted 2012) [#12]
About the reconnection thing:

if a connection drops ... give that client (or the server) XX seconds time to settle / reconnect. If no reconnect attempt : drop him from the game.

Why do I say this?
Do you handle 100% CPU usage?
Do you handle Alt-Tab ?

And the one I was frightened the most:
Dragging a windowed application (so: not in fullscreen) in Windows (i run it in Windows XP). The problem here: the app is halted then if you are not running your logic loop within another thread.
This means: if one of your players is moving the window around: no networkpackets will get received meanwhile also no updates will be done during that period.

For me that problem was reason enough to dive a bit into threading (and looking for help here on the forums :D).

If all of your clients are running linux you will be fine as I did not have such problems with dragged windows :D.


I did not test TCP a long time - but as TCP needs ACKs for all packets they send - each "disturbance" on receivers side will let the sender "halt/wait" until they timeout or send. There you see another reason for threading network things.
Please care for such problems BEFORE running occasional into them (like I did) as it makes planning of your code easier and the code later manageable.

Test the above things by moving the window of one of your connected clients (move it for some seconds). Test what happens if you (ungracefully) crash an app (so connections are not getting closed nicely). ...


I would be pleased to see pictures of the progress you are doing (except you would then violate some copyrights).


bye
Ron


PowerPC603(Posted 2012) [#13]
Currently, the only code I have is the code posted above.
I have no interface yet, no 3D models, no other code for the game, nothing (except the network code above).

I'll look into threads. I didn't think about that yet.
When we would run the game at home, we would always run it fullscreen.

I'll be using the Xors3D engine for the 3D stuff, as well as the interface.
It still supports ImageBuffers, on which you can draw your stuff in an update-function, then draw the entire image at once, without having to draw everything separately each frame, which would run slower I guess.
Drawing an image which has already 20 lines of text printed on it, will draw faster than drawing the rectangle first, then all the lines of text separately.

I guess I could post some screenshots later on when I have the 3D scene up and running.
All the 3D models, textures and such will be created by myself.

The only copyright violation would be the layout of the board and the names of the properties.
But then again, all other monopoly clones use the same layout and property-names.
So I don't really know if it would violate copyrights or not.



EDIT:
I've checked out threading and how it basically works.
The main graphics display should run in the main thread.
These things need threading:
- the entire server-part of the host
- the entire game-logic

The server-part will process each player and check for new messages.
These messages will be processed in the server, which takes actions based on the message from every client.
The server will also send data back to every client to let each player know if there is a change in state (property bought, player x is moving, ...)

The client will receive those messages and process them as well.
The threaded client-part would need to set some variables to allow the graphics display to change based on the value of those variables.
So, even if the window gets dragged, both server and client parts of the game run in separate threads and won't freeze.
Also the game-logic won't freeze, only the display will freeze.
But the display (3D graphics onscreen) will get updated automatically when the window is not longer dragged, if the variables are used properly.



I think a thread can be seen as a separate program without a window, but has access to the variables and other data of the main program.
So, every thread could use a main loop which never ends.
Using global variables, you could control the threads by setting data into them from within the main thread and letting the thread use those variables to do something.

So, in fact, I could write a function with a main loop that never ends and run that function in a thread.
That way, the thread will never end as well.
Inside that loop, i could put the broadcast code to broadcast a message throughout the entire network every second.

Then, even if the player moves his window around (which freezes the main thread), or if something else happens which causes the main thread to "hang" for 20 seconds as you mentioned earlier, the broadcast message is still sent every second throughout the network.

Is this correct?



EDIT:
I've just tested my server and client app by moving the windows around.
I've added a line of text to the clients window that displays the current time (millisecs).

When moving the server window around, nothing happens to the client. The client just keeps updating his graphics display as usual.
When moving the client window around, only the graphics display freezes.
I get no crash whatsoever.
Neither the server or client crashes.

But currently, no data is being sent after connection has been established. Maybe that's the reason why the client keeps updating his graphics display when moving the server around.
I'll test this when more of the game is ready and data is actively being sent and received regularly.

Last edited 2012


PowerPC603(Posted 2012) [#14]
I've knocked up some threading code myself.

First I started with a un-threaded build to print the millisecs to the BMax IDE's output tab (using Print).
When moving the window around, the output stopped printing the millisecs.
But, somewhere in the background, sending the printed text still happened.
Because when I stopped moving the window around, the output didn't continue where it left off (leaving a gap in the values), but also displayed all millisecs in between while I was moving the window.

Then I created this code:


In here, the function "PrintData" is executed a a separate thread.
The thread is also controlled using 2 variables ("Printing" and "ExitThread") by the main thread.
The "Printing" variable allows the thread to print it's data (or blocks it).
"Exitthread" allows the thread's main loop to end.
When I moved the window around, the thread kept printing the millisecs to the output tab of BMax.


PowerPC603(Posted 2012) [#15]

I would be pleased to see pictures of the progress you are doing (except you would then violate some copyrights).


I've got a screenshot already from some parts of the board.
It's not yet finished, as not all squares are implemented yet.

Right now, it only uses 4 different meshes and 2 textures.
2 meshes are textured, the other 2 only have a color on them.
All properties (the ones which you can buy and put houses on) are all merged into one mesh and use only 1 texture.
The corner squares (Go, jail, free parking and the cop) are also merged into one mesh and use the other texture.
The center square of the board only has a color applied to it, as well as the sides of the board.

The textures are not finished yet either, as the corners still need actual pictures instead of plain text.
The center of the board will change as well, it will get an actual texture.

Rendering full-screen (1440x900 resolution) gives me about 1000fps using Xors3D.



Last edited 2012


Derron(Posted 2012) [#16]
The 1000fps should be gone if you implement some kind of lighting, shadowing... and more complex objects (pegs/player figures, buildings).

2D-Interface will also swallow some performance.


Keep up the work.


bye
Ron


Scaremonger(Posted 2012) [#17]
Applications that locate their hosts using broadcast addresses are not a good idea. The traffic is broadcast to all hosts on the network and with a lot of nodes doing this, it can create a lot of traffic. Also simply using a 255 in the last octet of an address will not work in all situations!

There are various technologies that clients use to identify a server; SLP (RFC2165/2608) was the standards-track answer to the problem, but many applications simply query a specific DNS record and this is certainly the easiest to implement and does not restrict the application to working on a single subnet based network.

For example: Your client is on 192.168.111.1 and your server on 10.10.10.10 with a router in-between. The broadcast method would not work.

In your DNS (or hostfile on the client if you wish), your define a record called 'myapp' which returns address 10.10.10.10.

Your blitzmax application simply does this to identify the server:

local address% = hostip( "myapp" )



Nate the Great(Posted 2012) [#18]
There are various technologies that clients use to identify a server; SLP (RFC2165/2608) was the standards-track answer to the problem


I have been playing around with networking as well... what exactly is SLP and how would I use it?


For example: Your client is on 192.168.111.1 and your server on 10.10.10.10 with a router in-between. The broadcast method would not work.

In your DNS (or hostfile on the client if you wish), your define a record called 'myapp' which returns address 10.10.10.10.


If you have to create a record, wouldn't that require some sort of hard coding for the server IP? I don't understand this very well.


PowerPC603(Posted 2012) [#19]
You would still need to know and hardcode the server's IP into the client.
That's not what I'm after.

The client also has the server built-in, like many games that allow playing over a LAN.

If I would play on my own computer (laptop), the IP is 192.168.123.7.
The IP of the desktop computer is 192.168.123.1, where my son plays the game.

In one scenario, I could host the game on the laptop and my son joins me.
So he needs to know the server will be located on 192.168.123.7.
And my client needs to connect to this server as well (or use 127.0.0.1).

In another scenario, my son hosts the game, so my laptop needs to connect to his IP, which is 192.168.123.1.

So hardcoding the IP isn't an option, as the server may run on any IP in the network.
Also running a local DNS won't work, as the game could be hosted by anyone on the LAN.
The game could also be hosted by several players on a big LAN, so hardcoding one IP won't work.
The client should list all hosted servers on the LAN to allow the client to connect to a specific host, selected by the player.

The server also won't be registered on the internet, so I won't have a DNS to the server.
And it won't run 24/7, as the server only runs when the hosting player starts a new game.



If this would be an MMORPG, it would be alot easier like you say.
Then the server needs to be hosted somewhere and run 24/7 on a static IP or an address that can be resolved by a DNS.
Such a server has a big database to hold all information of every player, item, skill, ...
Moving such a database around isn't simple and requires the server to be located always on the same address.

Then it would simply be a matter of hardcoding the IP or DNS name to the server and broadcasting would not be needed, as every client knows that the server is on a static address.

Checking if the server is running could simply be done by sending a connection-message to the server and let the server respond by a acknowledge-message using UDP.
TCP could crash the game if the server is down as the client would never stop trying to connect to the server.

Last edited 2012