Short GNet tutorial

BlitzMax Forums/BlitzMax Tutorials/Short GNet tutorial

deps(Posted 2005) [#1]
Quick tutorial about GNet.

Foreword

This is a short tutorial. It doesn't contain much but I hope it helps you if you don't know anything about GNet. And I'm not an expert on GNet or networking so don't ask too difficult questions to me. ;)
But this tutorial shows how to create a really simple server and a really simple client and how to move objects around.

If you need more information after reading this I suggest you read the manual. If you have any questions you should post them on the forum.

Ok, lets begin.

Behind the scenes

Everything in GNet is an object. You don't send updates or anything because it's taken care of.
When you create an object on one of the clients, a copy of this object is created on all other clients. And when you change the values in a local object, it's synced with all other copies.
Isn't it neat? :)

Writing a simple server

Strict
' GNET simple dedicated server

' Create a new host. We call it listen because that's what it does
Local listen:TGNetHost = CreateGNetHost()

' Make it listen to the connection. 8086 is the port we want to use but you can change this to whatever
' you want between 1024 and 65535 (if i remember correctly.)
If Not GNetListen( listen, 8086 ) Then
	Print "Unable to listen on port."
	End
EndIf


Print "Server started."
Local quit_server = False
While Not quit_server

	GNetSync(listen) ' Sync all created objects

	Local newpeer:TGNetPeer = GNetAccept( listen ) ' Is someone knocking on our port?
	
	If newpeer Then
		' A new player wants to join us.
		Print "New player"
		Print "Players onnected at the moment:"
		Local peerlist:TList = GNetPeers( listen )
		
		For Local p:TGNetPeer = EachIn peerlist
			Print "  - "+DottedIP( SocketRemoteIP(GNetPeerTCPSocket( p )) )
		Next
		
	EndIf
	

	' The stuff below isn't needed in this short tutorial. It just prints some debug information so we can see
	' that the server is actually working. :)	

	Local olist:TList = GNetObjects( listen ) ' Get all created objects in the game
	
	For Local o:TGnetObject = EachIn olist
		
		Local state = GNetObjectState( o ) ' Get the state of the object
			
		Select state
			Case GNET_CREATED 'Object has been created
				print "New object created"
			Case GNET_SYNCED 'Object is in sync
				' do something here if it is needed
				' But the object is in sync and everything is just fine.
			Case GNET_MODIFIED 'Object has been modified
				' The object is modified! But no worries. It will be synced automagically.
			Case GNET_CLOSED 'Object has been closed
				Print "Connection closed to a peer"
				' The object is no longer valid. The player left the game
			Case GNET_MESSAGE 'Object is a message Object
				' I haven't played around with this yet. So you need to read the manual
				' yourself about this. ;)
		
		EndSelect
		
	Next

Wend


That's basically it. Objects are synced just fine and some debug messages are printed when something interesting
is happening.

But we need a client too, so we can test this out...

Writing a simple client

strict
' Simple GNet client

Local server:TGNetHost = CreateGNetHost() ' Create a "host"

Local serverpeer:TGNetPeer = GNetConnect( server,"127.0.0.1",8086 ) ' Connect to the server

If Not serverpeer Then ' Aaaw crap! Start the server first! And check the ip and port!
	Print "Unable to connect to server."
	End
EndIf

Print "Sever contacted!" ' Yaaaay

' Now, create our player. Only needs to create a local one...
' NetObject is our own type. You can find it after the main loop.
Local localplayer:NetObject = NetObject.create(server,"DaBomb!",320,240)



' Some graphics...
Graphics 640,480,0


' The main loop
While Not KeyDown( KEY_ESCAPE )

	' --------- Logic

		GNetSync(server) ' Sync objects

		' Get objects
		Local olist:TList = GNetObjects( server )

		' We use this later when we draw players
		Local plist:TList = CreateList() ' Active players
		
		For Local o:TGnetObject = EachIn olist
		
			' The state the object is in
			Local state = GNetObjectState( o )
			
			Select state
				Case GNET_CREATED 'Object has been created
					If GetGNetInt( o, 0 ) = O_AVATAR Then
						plist.addlast( o ) ' we want to draw this later
						Print "Joined: "+GetGNetString(o,1)
						' Spawn some particles or something to show that
						' The player has arrived...
					EndIf
				Case GNET_SYNCED 'Object is in sync
					If GetGNetInt( o, 0 ) = O_AVATAR Then
						plist.addlast( o ) ' Draw it.
					endif
				Case GNET_MODIFIED 'Object has been modified
					If GetGNetInt( o, 0 ) = O_AVATAR Then
						plist.addlast( o ) ' Draw it anyway
					endif
				Case GNET_CLOSED 'Object has been closed
					' Spawn some particles or a sound effect or something.
					' This player is no more...
			
			EndSelect
		
		Next

		' We want to controll our local player
		If KeyDown( KEY_LEFT ) Then LocalPlayer.move(-1,0)
		If KeyDown( KEY_RIGHT ) Then LocalPlayer.move(1,0)
		If KeyDown( KEY_UP ) Then LocalPlayer.move(0,-1)
		If KeyDown( KEY_DOWN ) Then LocalPlayer.move(0,1)


	' --------- GFX
	
	Cls ' nice and clean

		' Draw connected players	
		Local y = 10
		For Local dp:TGnetObject = EachIn plist
			' Player list at top left
			Local tmp:String = GetGNetString( dp,1 ) ' Nickname
			DrawText tmp, 10,y
			y:+17
			
			' Draw a rectangle to show where the player is
			Local xpos = GetGNetInt(dp,2) ' get position
			Local ypos = GetGNetInt(dp,3)
			DrawRect xpos-3,ypos-12,7,12
			drawtext tmp, xpos,ypos-30
			
		Next
		
		' How many players are there?		
		DrawText "Players: "+plist.count(), 5,y
	
		
		' Some GNet statistics		
		DrawText "In/out: "+GNetTotalBytesIn()+"/"+GNetTotalBytesOut(),  0,465
	
	Flip ; FlushMem

Wend

End ' bye bye



' ----- our player TYPE

' So we know what type an object is...
Const O_AVATAR = 0

' Our type
Type NetObject

	Field nobj:TGNetObject ' The actual GNet object
	Field xpos:Int ' Position on the screen
	Field ypos:Int


	Function create:NetObject( connection:TGNetHost, name:String, x:Int, y:Int )
	
		Local no:NetObject = New NetObject
	
		no.nobj = CreateGNetObject( connection ) ' Create the GNet object
		
		SetGNetInt( no.nobj, 0, O_AVATAR ) ' We use slot 0 as an object type ID
		no.place(x,y) ' Place the object
		no.rename(name) ' rename it
	
		Return no ' Return it
	EndFunction


	

	Method place( x:Int, y:Int )
		xpos = x ' Save the position internally
		ypos = y
		SetGNetInt( nobj,2,x ) ' slot 2 and 3 are used for x and y position.
		SetGNetInt( nobj,3,y )	
	EndMethod

	Method Move( xoff:Int, yoff:Int )
		xpos:+xoff
		ypos:+yoff
		SetGNetInt( nobj,2,xpos )
		SetGNetInt( nobj,3,ypos )	
	EndMethod
	
	Method rename( name:String )
		SetGNetString( nobj,1,name ) ' slot 1 is used for the nickname
	EndMethod

EndType



Tadaaa. That wasn't so hard, was it?
We only update our local player, the new x,y values are synced across the network to the other connected players. And we don't have to do anything at all to update the copies the local player got. It's done by GNet.

Hope this tutorial was useful.


Beaker(Posted 2005) [#2]
Very useful. Thanks.


deps(Posted 2005) [#3]
I just found this: http://www.blitzbasic.com/Community/posts.php?topic=50966#568079

Contains a link to a more advanced example.


Apollonius(Posted 2007) [#4]
Any working version of this for the current versions?


Digital Anime(Posted 2009) [#5]
There is one in the code archives that works (Needs Maxgui to run):

Click here!

It's hard to find this example (made by WendellM) using the search so that's why I'm posting the link in the Tutorial section as well.


Hardcoal(Posted 2013) [#6]
although along time passed! thanks!

4 years in a forum is like 4 mintues.
besides, all this time betwin messages is a bit sad to me.
it just shows you how much priceless time really is.

remark:


GNet now uses ENet for low level comms. GNetAccept and all peer functions have gone. GNetAccept now built into GNetSync, and GNetConnect now just returns True/False.