multiplayer -- what?

BlitzMax Forums/BlitzMax Programming/multiplayer -- what?

i've read every topic on gnet that the search brought up, and every thing in the code archives.

alot of the examples and code snippets wouldn't work, probably because of version differences, and i don't know where to start to correct them.

i've written a VERY CRUDE test project (just to test things out and try and get an understanding of using gnet), thats gone under many revisions based off the code examples that did work.

my final code is loosely based on the code that WendellM wrote.

it seems to connect, but doesn't send and/or receive any information.
i've spent the last 3 days constantly going over all the gnet posts and examples and recoding, but for the life of me can't figure out what to do or how to do it correctly.

in this test program each player is player 1 on their own computer and the other connected player is player 2.
i need to:
1. know when player 2 connects and add 1 to the numplayers var on both computers.

2. be able to send p1x,p1y coords to be recieved by the other player and put them into p2x,p2y.

as always any help would be greatly appreciated.


Import BRL.GNet
AppTitle="multitest"
Graphics 640,480
SeedRnd MilliSecs()

'gfx
Global character = LoadImage("character.png")

'var
Global p1x=Rand(0,575),p1y=Rand(0,416)
Global p2x,p2y
Global oldx,oldy
Global numplayers = 1
Global multiOn = 0
Global myStatus$ = "not connected"
Global connected = 0
Global host:TGNetHost = CreateGNetHost()
Global port = 12345
Global ip$ = "127.0.0.1"
Global timeout_ms = 10000 
Global hosting

Global localObj:TGNetObject = CreateGNetObject:TGNetObject( host )
Global remoteObj:TGNetObject
Global objList:TList = New TList 

Const SLOT_CONNECT = 0
Const SLOT_X = 1
Const SLOT_Y = 2

While Not AppTerminate()
Cls
	If multiOn = 0 Or multiOn = 2
		If numplayers = 2
		    'player2
		    SetColor 255,0,0
		    DrawImage character,p2x,p2y
		EndIf
		
		'player1
		SetColor 255,255,255
		DrawImage character,p1x,p1y		
	EndIf	
		
	Keys()		
	
	If connected = 1
		SendInfo()	
		GNetSync host
		ReceiveInfo()
	EndIf	
			
	If multiOn = 1
		DrawText "[H]ost or [Join]",200,200		
	EndIf
	
SetColor 255,255,255

'debug info
DrawText "p1x: "+p1x,10,50
DrawText "p1y: "+p1y,10,60
DrawText "oldx: "+oldx,10,70
DrawText "oldy: "+oldy,10,80
DrawText "p2x: "+p2x,10,100
DrawText "p2y: "+p2y,10,110

'connection status
DrawText "Players: "+numplayers,10,10
DrawText "Status: "+myStatus$,10,450
Flip
Wend
End

Function ConnectTo()
	CloseGNetHost(host)
	host:TGNetHost=CreateGNetHost()
	If hosting = 1
		success = GNetListen( host, port )
		If Not success
			connected = 0
			myStatus$ = "GNetListen failed"			
			multiOn = 0
		Else
			connected = 1			
			myStatus$ = "connected -- HOST"
			SetGNetInt(localObj, SLOT_CONNECT,1)
			SendInfo()
			multiOn = 2			
		EndIf
	Else
		success = GNetConnect( host, ip$, port, 10000 )
		If Not success
			connected = 0
			myStatus$ = "GNetConnect failed"
			multiOn = 0
		Else
			connected = 1
			myStatus$ = "connected -- CLIENT"
			SetGNetInt(localObj, SLOT_CONNECT,1)
			SendInfo()
			multiOn = 2
		EndIf
	EndIf		
EndFunction

Function Keys()
	If multiOn = 0  Or multiOn = 2
		If KeyDown(key_Left)
			p1x:-10
			If p1x <0 Then p1x = 0
		EndIf
		If KeyDown(key_Right)
			p1x:+10
			If p1x > 575 Then p1x = 575
		EndIf		
		If KeyDown(key_Up)
			p1y:-10
			If p1y <0 Then p1y = 0
		EndIf
		If KeyDown(key_down)
			p1y:+10
			If p1y > 416 Then p1y = 416
		EndIf
		
		If multiOn = 0
			If KeyDown(key_M)
				multiOn = 1
			EndIf
		EndIf
	EndIf
	
	If multiOn = 1
		If KeyDown(key_H)
			hosting = 1
			ConnectTo()
		EndIf
		If KeyDown(key_J)
			hosting = 2
			EnterIP()
		EndIf
	EndIf
	
	If KeyDown(key_Escape) Then End
EndFunction

Function EnterIP()
Local done = 0

	Repeat
	Cls
		DrawText"Enter IP:> "+ip$+"*",200,200
		
		c=GetChar()
		If c
			If c = 8
				If Len(ip$)>0 Then ip$=Left$(ip$,Len(ip$)-1)
			EndIf
			Select Chr(c)
				Case 1
					ip$ = ip$ +Chr(c)
				Case 2
					ip$ = ip$ +Chr(c)
				Case 3
					ip$ = ip$ +Chr(c)
				Case 4
					ip$ = ip$ +Chr(c)
				Case 5
					ip$ = ip$ +Chr(c)
				Case 6
					ip$ = ip$ +Chr(c)
				Case 7
					ip$ = ip$ +Chr(c)
				Case 8
					ip$ = ip$ +Chr(c)
				Case 9
					ip$ = ip$ +Chr(c)
				Case 0
					ip$ = ip$ +Chr(c)
				Case "."
					ip$ = ip$ +Chr(c)
			EndSelect
		EndIf
		
		If KeyHit(key_Enter)			
			done = 1
			Cls
			DrawText"attempting to connect to ip: "+ip$,200,200
		EndIf
	Flip
	Until done = 1
	
	ConnectTo()
EndFunction

Function SendInfo()
	If p1x <> oldx
		oldx = p1x
		SetGNetInt localObj,SLOT_X,p1x
	EndIf
	If p1y <> oldy
		oldy = p1y
		SetGNetInt localObj,SLOT_Y,p1y
	EndIf	
EndFunction

Function ReceiveInfo()
	objList = GNetObjects( host, GNET_MODIFIED )
	For remoteObj = EachIn objList
		Local c = GetGNetInt(remoteObj,SLOT_CONNECT)
		If c = 1 Then numplayers:+1
		
		p2x = GetGNetInt(remoteObj, SLOT_X)
		p2y = GetGNetInt(remoteObj, SLOT_Y)
	Next	
EndFunction



Hi KingNothing,

Your problem is caused by the GNetObject localObj which you can only create after you have constructed the TGNetHost.

So if you change your connectTo() function and create the localObj after you create the Host it works fine!

e.g.

Function ConnectTo()
	CloseGNetHost(host)
	host:TGNetHost=CreateGNetHost()

	'localObj needs to be created from new host
	localObj = CreateGNetObject:TGNetObject( host )

	If hosting = 1


Hope this helps.

still having probs knowing how many are connected ie. 1 or 2
(but i know i'm doing something wrong), so i just changed numplayers to always read as 2 for now. other then that it works great.
the other comp connects and both players move great.
so big THANKYOU!!

now i have a place to start tinkering and figuring things out. then i can acctually attempt to add multiplayer to my real game.

It took me a while to figure it out too. Once you get your head around it though, it's easy to play with. Try this demo.

note: you'll have to change the image.

Strict


Const NET_TYPE 		= 1
Const NET_TXT		= 2
Const NET_POSITION 	= 3
Const NET_HEALTH	= 4
Const NET_NAME		= 5
Const NET_MODE		= 6
' ETC, ETC


Graphics 640,480,0



Global localPlayer:TPlayer
Global Game:TGame=New TGame
Global net:TNetwork=New TNetwork

Global txtList:TList = CreateList()




localPlayer=TPlayer.createLocalPlayer("local","happy")





While KeyHit(KEY_ESCAPE)=0
	Cls
		WaitTimer(game.timer)
		
		' are we connected at all?
		' no... then ask if you want to host or join
		If net.connectionType=0
			DrawText "1. Host",5,5
			DrawText "2. Join",5,15
			DrawText "3. Quit",5,25			
			If KeyHit(KEY_1) net.StartHost();localPlayer.changeName("local")
			If KeyHit(KEY_2) net.connectToHost("127.0.0.1");localPlayer.changeName("remote")
			If KeyHit(KEY_3) Exit
		EndIf
	
		If KeyHit(KEY_M) net.sendAMessage("chat","hello there")
	
		game.update()
	
		DrawTxt() ' output our messages to see progress
	
	
		DrawText "Players: "+game.objects.count()+" | M = send message, < and > = move, 1,2,3 = change modes",0,GraphicsHeight()-20
	Flip	
	Delay 1
Wend

End














Type TGame
	Field objects:TList=CreateList()	
	Field timer:TTimer=CreateTimer(30)
	
	Method update()
		' update all players, whether local or remote
		For Local i:TPlayer=EachIn objects
			i.update()
		Next		
		net.update()		
	End Method
	
	Method removePlayer(p:Tplayer)
		' to be honest, I'm just guessing about this one.
		' since GnetObjects can only be closed by the creator
		' we have to close it at some point.
		' check to see if it was created locally
		' then destroy it so it can be synced across the network.
		If p.g
			If GNetObjectLocal(p.g) CloseGNetObject(p.g)
		EndIf		
		
		' and remove the player so the garbage collector takes care of it
		ListRemove(objects,p)
	End Method
	
	Method getPlayerFromGNetObj:Tplayer(f:TGNetObject)
		' find a player from its gnetObject, g
		For Local i:TPlayer=EachIn objects
			If i.g=f Return i
		Next		
	End Method
	
	Method parse$(in$,de$,se)
		Local m$=""
		Local i=0
		For Local x=1 To Len(in)
			Local v$=Mid(in,x,1)
			If v=de i=i+1
			If se=i And v<>de m$=m+v
		Next
		Return m
	End Method	
	
End Type

Type TPlayer
	Field name$,mode$,health
	Field g:TGnetObject
	Field x,y
	Field image:TImage

	Function createLocalPlayer:Tplayer(name$,mode$)
		Local i:Tplayer=New TPlayer		
		i.name=name
		i.mode=mode
		i.image=LoadImage("player.png")
		i.health=Rand(1,100)
		i.x=Rand(0,GraphicsWidth())
		i.y=Rand(0,GraphicsHeight())
		i.g=CreateGNetObject(net.host) ' create a local GNetObject
		' now, the magic happens in GNetSync because that creates the object on every computer
		' connected to the host automatically. It's possible to see which objects are
		
		' we have to set them ALL here otherwise you'll receive an assert failure for some reason.	
		SetGNetString i.g,NET_TYPE, "player"				
		SetGNetString i.g,NET_NAME, i.name
		SetGNetString i.g,NET_MODE, i.mode
		SetGNetInt i.g,NET_HEALTH, i.health
		SetGNetString i.g,NET_POSITION, i.x+"|"+i.y ' x|y|angle... we'll pack this later
		
		ListAddLast(game.objects,i)			
		Return i
	End Function

	Method changeName(f$)
		name=f
		SetGNetString g,NET_NAME, name
	End Method

	Method changeMode(f$)
		mode=f
		SetGNetString g,NET_MODE, mode		
	End Method

	Method update()
		' you can tell whether the player is yours (local) or not by 
		' checking agains the localPlayer we created at the beginning
		' although there are probably better ways of doing it
		
		If Self=localPlayer
			DrawText name+" "+mode,x,y-20
			DrawImage image,x,y
			
			' update position here
			' you'll want to limit this, depending on whether 
			' the x and y have changed recently.
						
			SetGNetString(g,NET_POSITION,x+"|"+y)
			SetGNetInt(g,NET_HEALTH,health)
			
			' once you've called these, GnetSync will automagically sync other computers
			' with the variable changes.
			
			If KeyDown(KEY_LEFT) x:-1
			If KeyDown(KEY_RIGHT) x:+1
			
			If KeyHit(KEY_1) changeMode("unhappy")
			If KeyHit(KEY_2) changeMode("happy")
			If KeyHit(KEY_3) changeMode("something else")
					
		Else
			' it's another, remote player
			' we don't  need to decode the x and y, it's done for us
			' in the network.update() bit. see below.
			
			DrawImage image,x,y
			DrawText name+" "+mode,x,y-20
		EndIf
	End Method	
	
	Method toString$()
		Return "player - "+name+" mode: "+mode+" health: "+health
	End Method
End Type



Type TNetwork	
	Field host:TGNetHost = CreateGNetHost()	' create our local TGNetHost object
	Field port = 8086, timeout = 5000
	Field LocalIP$
	Field connected, ping, lastPing, currentMs, lastMs, pingTimer, displayPing, restartHost
	
	Const CONNECTION_HOST		= 1
	Const CONNECTION_CLIENT		= -1
	Field connectionType

	' you can use this to determine whether WE are the host or a CLIENT
	Method isHost()
		If connectionType=CONNECTION_HOST Return True
		Return False
	End Method

	Method sendAMessage(t$,txt$)
		' create a GNetMessage
		' I see these as temporary GnetObjects.
		' we can use them to send chat, etc
		'
		Local i:TGNetObject=CreateGNetMessage(host)
		SetGNetString i,NET_TYPE,t
		SetGNetString i,NET_TXT,txt
		
		' send to all players
		For Local j:TPlayer=EachIn game.objects
			SendGNetMessage(i,j.g)
		Next
	End Method



	Method update()
			
			GNetSync host ' send to other instance & get updates
			
			
			' get TNetMessages sent. this can be chat, etc
			Local msgs:TList=GNetMessages(host)
			
			' now cycle throgh all the messages received
			For Local i:TGnetObject = EachIn msgs
				addtxt("new object: "+i.toString(),255,255,0)
				Select GetGNetString(i,NET_TYPE)
					Case "chat" 
						AddTxt("message received",255,0,0)
						AddTxt("message: "+GetGNetString(i,NET_TXT),255,0,0)		
				End Select	
			Next		
			
			' get all the GnetObjects created during our session using the GNET_CREATED flag
			Local joined:TList=GNetObjects(host, GNET_CREATED)
			
			' this represents objects created. so, depending on their type, it might
			' be a player or a torpedo, or anything.
			' in my example, players have a TNetObject called g, player.g
			' so, we check to see whether a player already exists with this gNetObject
			' and if not, create a remotePlayer type and assign its g as the one we received.
			
			For Local i:TGNetObject = EachIn joined
				If GetGNetString(i,NET_TYPE) = "player"				
					
					' this is my way of seeing whether a player with this GNetObject already exists.
					If game.getPlayerFromGNetObj(i)=Null			
					
						' only create a player is it doesn't exist already
						addtxt "new player joining...",0,255,0					
						Local p:TPlayer=New TPlayer
						
						p.g=i	' assign its gnetObject we got from the joined list.
						
						' now get all its variables					
						p.image=LoadImage("player.png")
						
						p.mode=GetGNetString(i,NET_MODE)
						p.name=GetGNetString(i,NET_NAME)
						p.health=GetGNetInt(i,NET_HEALTH)
						Local loc$=GetGNetString(i,NET_POSITION) ' x,y',angle
						p.x=		Int(game.parse(loc,"|",0))
						p.y=		Int(game.parse(loc,"|",1))

						' and add it to the game to get updated
						ListAddLast(game.objects,p)
						
						addtxt p.name+" joined successfully",0,255,0
					EndIf					
				EndIf	
			Next
			
			' as well as GNET_JOINED and GNET_MODIFIED, we have
			' GNET_CLOSED which gives us a list of all the objects that have been
			' destroyed. Let's say for example that we leave and destroy our localPlayer.g
			' object. GnetSync will tell every other computer that that g object
			' has been destroyed. So we should destroy players linked to that TGnetObject
			' too. 
			
			Local leftGame:TList=GNetObjects(host, GNET_CLOSED)
			
			For Local i:TGNetObject = EachIn leftGame
			
				If GetGNetString(i,NET_TYPE) = "player"
					addtxt "player leaving...",255,255,0
					
					' find the player associaed with that g, TGnetObject
					Local p:TPlayer=game.getPlayerFromGNetObj(i)
					
					If p game.removePlayer(p)
				EndIf			
			Next			
			
			' we have GNET_ALL too, but that's not particularly helpful. we make
			' a list of all the modified objects to check whether to set player's
			' variables.
			
			Local objList:TList = GNetObjects(host, GNET_MODIFIED)	
			
			' cycle through the modified Gnetobjects...
			For Local i:TGNetObject  = EachIn objList
			
				' get the player this i, TGnetObject is referring to.
				Local o:TPlayer=game.getPlayerFromGNetObj(i)
				If o
					Local p:Tplayer=Tplayer(o)
					' now unpack their info.
					
					Local loc$=GetGNetString(p.g,NET_POSITION)
					p.x=Int(game.parse(loc,"|",0))
					p.y=Int(game.parse(loc,"|",1))
					p.mode=GetGNetString(p.g,NET_MODE)
					p.health=GetGNetInt(p.g,NET_HEALTH)
					
				EndIf				
			Next		
	End Method
	
		
	Method StartHost()
		addtxt "hosting"
		connectionType = CONNECTION_HOST
		If Not GNetListen(host, port) Then
			connectionType = 0
			Return 0
		Else
			addtxt "success!"
		EndIf	
		Return True	
	End Method
	
	Method connectToHost(ip$)
		addtxt "joining"
		connectionType = CONNECTION_CLIENT
		If GNetConnect(host, ip$, port, timeout)
			Return 1
			addtxt "success!"
		Else	
			addtxt "failed to join"
			connectionType = 0
			Return 0
		EndIf
	End Method		
	
	Method New()
		Local ip_array[] = HostIps("")
		Local ip = HostIp("")		
		localIp$ = DottedIP(ip_array[0])	
	End Method
	
	Method Delete()		
		disconnect()
	End Method
	
	Method disconnect()
		GNetSync host
		clearConnection()		
		restartHost = 5
	End Method	
	
	Method clearConnection()
		connectionType = 0;connected = False;ping = 0;lastPing = 0;displayPing = 0
	End Method	
End Type



Type Txt
	Field content$
	Field red%, green%, blue%
End Type

Function AddTxt(content$, red% = 255, green% = 255, blue% = 255, limit = 10)
	Local t:Txt = New Txt
	t.content = content
	t.red = red
	t.green = green
	t.blue = blue
	txtList.AddLast t
	DebugLog content
	If txtList.Count() > limit Then txtList.RemoveFirst()
End Function

Function DrawTxt(x% = 20, y% = 20, yStep% = 20)
	For Local t:Txt = EachIn txtList
		SetColor t.red, t.green, t.blue
		DrawText t.content,x,y
		y :+ yStep
	Next 
	SetColor 255,255,255
End Function


very cool Rimmsy i like it,thanks for that.
i'm actually starting to under stand this gnet stuff a bit now thanks to you two.

its hard to believe well actually its not, that one line in the wrong place caused me three days of headache (literally) lol.

Tell me about it. I went through every possible multiplayer lib on the forums and didn't have any joy with them. I must have spent about three days developing wrappers for them only to realise they weren't right for the job.

Good luck with your project, mine's coming along nice.