Haha, yeah I always try to add as many comments as I can because I have a habit of forgetting what something does.
I'm not even sure what happened in my current project. It just got out of control I guess. Take a look at what I mean
It was supposed to be a multiplayer framework for an example of the use of my master server.. only it never got that far lol.
Maybe in the rerererewrite I will learn the proper use of lists. This seriously turned into a humongous freakin' mess. Like, way messier than my normal messes.
Code: Select all
; --------------------------------------------------------------------------
; last modified: June 29th, 2011 @ 10:40:53 PM
; --------------------------------------------------------------------------
Enumeration
#FLAG_RESET = 0
#NETWORK_HEARTBEAT = 1
#NETWORK_CONNECT = 2 ;
#KEYINPUT_LEFT = 4
#KEYINPUT_RIGHT = 8
#KEYINPUT_UP = 16
#KEYINPUT_DOWN = 32
#KEYINPUT_FIRE = 64
#HEARTBEAT_TIMEOUT_COUNT = 5
#HEARTBEAT_TIMER = 1000
EndEnumeration
Global *bufSend = AllocateMemory(1024)
Global *__bufSend = AllocateMemory(5)
Global *bufRecv = AllocateMemory(1024)
Structure pdata
id.l
hbcount.b
x.f
y.f
keyLeft.b
keyRight.b
keyUp.b
keyDown.b
bitFlag.b
name.s{20}
EndStructure
Structure cdata
heartbeat.s{4}
id.l
keyFlag.b
x.f
y.f
name.s{20}
EndStructure
Global Dim PlayerData.pdata(0) ; if client, element zero is always the player
Global gameType.l = 0, host.l
Declare client_ProcessData()
Declare server_ProcessData()
Declare client_GetInput()
Declare client_RecvData()
Declare client_SendData()
Declare server_RecvData()
Declare server_SendData()
Enumeration
#MAIN_WIN
EndEnumeration
InitNetwork() : InitSprite() : InitKeyboard()
gameType.l = Val(InputRequester("GAMETYPE", "Run as Client or Server? (enter 0 for client or 1 for server)", "0"))
OpenWindow(#MAIN_WIN, 0, 0, 800, 600, "Test", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
OpenWindowedScreen(WindowID(#MAIN_WIN), 0, 0, 800, 600, 1, 0, 0)
#Player = 0 ; <-- remove.. V -- remove..
CreateSprite(#Player, 50, 50, #PB_Sprite_Texture | #PB_Sprite_AlphaBlending)
StartDrawing(SpriteOutput(#Player))
Box(0, 0, 50, 50, RGB(0,50,255))
StopDrawing()
If gameType.l = 0 ;{
SetWindowTitle(#MAIN_WIN, "Client")
port.l = Val(InputRequester("Choose port...", "Input an open UDP port", "21000"))
host.l = OpenNetworkConnection(InputRequester("Choose IP...", "Input the IP address to connect to", "127.0.0.1"), port.l, #PB_Network_UDP)
SendNetworkString(host.l, "cn")
PlayerData(0)\id = 0
Else
SetWindowTitle(#MAIN_WIN, "Server")
port.l = Val(InputRequester("Choose port...", "Input an open UDP port", "21000"))
host.l = CreateNetworkServer(#PB_Any, port.l, #PB_Network_UDP)
OpenConsole()
EndIf ;}
Repeat
Delay(1)
eventID = WaitWindowEvent(1)
If gameType.l = 0 ; quick hack to do some position updating/heartbeats should probably throw this into a neatly packed procedure
client_GetInput()
client_RecvData()
If netClientHeartBeatTimer < ElapsedMilliseconds() ; send heartbeat...
SendNetworkString(host.l, "hb")
PlayerData(0)\hbcount + 1
If PlayerData(0)\hbcount >= #HEARTBEAT_TIMEOUT_COUNT
MessageRequester("Error","Lost connection to the server")
End
EndIf
netClientHeartBeatTimer = ElapsedMilliseconds() + #HEARTBEAT_TIMER
EndIf
client_SendData()
; client side position updating
For x = 0 To ArraySize(PlayerData())
If (PlayerData(x)\bitFlag & #KEYINPUT_LEFT) <> 0 : PlayerData(x)\x = PlayerData(x)\x - 1 : EndIf
If (PlayerData(x)\bitFlag & #KEYINPUT_RIGHT) <> 0 : PlayerData(x)\x = PlayerData(x)\x + 1 : EndIf
If (PlayerData(x)\bitFlag & #KEYINPUT_UP) <> 0 : PlayerData(x)\y = PlayerData(x)\y - 1 : EndIf
If (PlayerData(x)\bitFlag & #KEYINPUT_DOWN) <> 0 : PlayerData(x)\y = PlayerData(x)\y + 1 : EndIf
Next x
ElseIf gameType.l = 1
server_RecvData()
If netServerHeartBeatTimer < ElapsedMilliseconds()
;PrintN("[netHeartBeat] netHeartBeatTimer has elapsed, doing a row call to see who's here.")
For cn = 0 To ArraySize(PlayerData()) ; update heartbeat count and check for disconnections
If PlayerData(cn)\id <> 0
If PlayerData(cn)\hbcount >= #HEARTBEAT_TIMEOUT_COUNT
PrintN("[netHeartBeat] [ID: "+Str(PlayerData(cn)\id)+"] lost connection, removing id for recycling...")
PlayerData(cn)\id = 0
Else
PlayerData(cn)\hbcount + 1; add to the count...
EndIf
EndIf
Next cn
netServerHeartBeatTimer = ElapsedMilliseconds() + #HEARTBEAT_TIMER
EndIf
; Do some position x/y updating.
For cn = 0 To ArraySize(PlayerData())
If PlayerData(cn)\id <> 0
If PlayerData(cn)\keyLeft = #KEYINPUT_LEFT : PlayerData(cn)\x = PlayerData(cn)\x - 1 : EndIf
If PlayerData(cn)\keyRight = #KEYINPUT_RIGHT : PlayerData(cn)\x = PlayerData(cn)\x + 1 : EndIf
If PlayerData(cn)\keyUp = #KEYINPUT_UP : PlayerData(cn)\y = PlayerData(cn)\y - 1 : EndIf
If PlayerData(cn)\keyDown = #KEYINPUT_DOWN : PlayerData(cn)\y = PlayerData(cn)\y + 1 : EndIf
EndIf
Next cn
EndIf
ClearScreen(RGB(0,0,0))
For x = 0 To ArraySize(PlayerData())
If PlayerData(x)\id <> 0
DisplaySprite(#Player, PlayerData(x)\x, PlayerData(x)\y)
EndIf
Next x
FlipBuffers()
Until eventID = #PB_Event_CloseWindow
Procedure client_GetInput()
Static keyLeftState.b, keyRightState.b, keyUpState.b, keyDownState.b
Shared keyFlagLeft.b, keyFlagRight.b, keyFlagUp.b, keyFlagDown.b
Shared netBitFlag.b
ExamineKeyboard()
; on keypress set the keyflag state for the bit calculation later.
If KeyboardPushed(#PB_Key_A)
If keyLeftState.b = 0 : keyFlagLeft.b = #KEYINPUT_LEFT : keyLeftState.b = 1 : EndIf
Else
If keyLeftState.b = 1 : keyFlagLeft.b = #FLAG_RESET : keyLeftState.b = 0 : EndIf
EndIf
If KeyboardPushed(#PB_Key_D)
If keyRightState.b = 0 : keyFlagRight.b = #KEYINPUT_RIGHT : keyRightState.b = 1 : EndIf
Else
If keyRightState.b = 1 : keyFlagRight.b = #FLAG_RESET : keyRightState.b = 0 : EndIf
EndIf
If KeyboardPushed(#PB_Key_W)
If keyUpState.b = 0 : keyFlagUp.b = #KEYINPUT_UP : keyUpState.b = 1 : EndIf
Else
If keyUpState.b = 1 : keyFlagUp = #FLAG_RESET : keyUpState.b = 0 : EndIf
EndIf
If KeyboardPushed(#PB_Key_S)
If keyDownState.b = 0 : keyFlagDown.b = #KEYINPUT_DOWN : keyDownState.b = 1 : EndIf
Else
If keyDownState.b = 1 : keyFlagDown.b = #FLAG_RESET : keyDownState.b = 0 : EndIf
EndIf
EndProcedure
Procedure client_SendData()
Shared keyFlagLeft.b, keyFlagRight.b, keyFlagUp.b, keyFlagDown.b
Shared netBitFlag.b
Static __newBitFlag.b
; form bit calc, can store several instructions in a single byte.
netBitFlag.b = keyFlagLeft.b|keyFlagRight.b|keyFlagUp.b|keyFlagDown.b;|#NETWORK_HEARTBEAT
; should only send when a change occurs...
If netBitFlag.b <> __newBitFlag.b ; checking if a change has occured from a key press.
Debug "Before sending: " + Str(netBitFlag.b)
PokeB(*bufSend + 4, netBitFlag.b)
SendNetworkData(host.l, *bufSend, SizeOf(cdata))
__newBitFlag.b = netBitFlag.b
EndIf
EndProcedure
Procedure client_RecvData()
eventNetwork= NetworkClientEvent(host.l)
If eventNetwork
Select eventNetwork
Case #PB_NetworkEvent_Data
recv = ReceiveNetworkData(host.l, *bufRecv, SizeOf(cdata))
;Debug "client BitFlag = " + Str(PeekB(*bufRecv))
If recv > 0
client_ProcessData()
EndIf
EndSelect
EndIf
EndProcedure
Procedure client_ProcessData()
If PeekS(*bufRecv) = "hb"
;Debug "[netHeartBeat] received heartbeat reply from server.. Reseting hbcount"
PlayerData(0)\hbcount = 0
EndIf
If FindString(PeekS(*bufRecv), "c|", 0)
Debug "Resizing array: " + Mid(PeekS(*bufRecv),FindString(PeekS(*bufRecv), "c|", 0))
ReDim PlayerData(Val(Mid(PeekS(*bufRecv),FindString(PeekS(*bufRecv), "c|", 0))))
EndIf
;Else
For x = 0 To ArraySize(PlayerData()) ; try to update all clients found in the server.... fails....
; why is this pretending that there are more than 1 client in the server if only 1 client is running..?
Debug "[ID: " + Str(PeekL(*bufRecv+4)) + "] Attempting to match"
If PlayerData(x)\id = PeekL(*bufRecv + 4) ; Do an update for clients we know already exist
;Debug "Matched id's with received ID...."
PlayerData(x)\bitFlag = PeekB(*bufRecv + 8) ; bitflag
PlayerData(x)\x = PeekF(*bufRecv + 9) ; struct\y
PlayerData(x)\y = PeekF(*bufRecv + 13) ; struct\y
PlayerData(x)\name = PeekS(*bufSend + 17) ; struct\name.s{20}
Debug "[ID: "+Str(PlayerData(x)\id)+"] [X: " +Str(PlayerData(x)\x) + "] [Y:"+Str(PlayerData(x)\y) + "] Already Exists"
EndIf
If x = ArraySize(PlayerData())
;If PlayerData(x)\id = 0
PlayerData(x)\id = PeekL(*bufRecv + 4)
PlayerData(x)\bitFlag = PeekB(*bufRecv + 8) ; bitflag
PlayerData(x)\x = PeekF(*bufRecv + 9) ; struct\y
PlayerData(x)\y = PeekF(*bufRecv + 13) ; struct\y
PlayerData(x)\name = PeekS(*bufSend + 17) ; struct\name.s{20}
Debug "[ID: "+Str(PlayerData(x)\id)+"] [X: " +Str(PlayerData(x)\x) + "] [Y:"+Str(PlayerData(x)\y) + "] New Element"
;EndIf
EndIf
Next x
;EndIf
EndProcedure
Procedure server_RecvData()
Shared clientID
eventNetwork = NetworkServerEvent()
If eventNetwork
clientID = EventClient()
Select eventNetwork
Case #PB_NetworkEvent_Data
recv = ReceiveNetworkData(clientID, *bufRecv, SizeOf(cdata))
PrintN("[netReceive] [IP: " + IPString(GetClientIP(clientID)) + "] Buffer content: " + Str(PeekB(*bufRecv)))
If recv > 0
server_ProcessData()
EndIf
EndSelect
EndIf
EndProcedure
Procedure server_ProcessData()
Shared clientID
Static netRecycle
;
If PeekS(*bufRecv) = "hb" ; Heartbeat
PrintN("[netHeartBeat] received a heartbeat [clientID:"+Str(clientID)+"]")
For cn = 0 To ArraySize(PlayerData())
If PlayerData(cn)\id = clientID
;PrintN("[netHeartBeat] heartbeat matched [clientID:"+Str(clientID)+" @ "+Str(cn)+"]")
PlayerData(cn)\hbcount.b = 0
SendNetworkString(PlayerData(cn)\id, "hb")
;PrintN("[netHeartbeat] sent a heartbeat back to client. We're here!!!")
Break
EndIf
Next cn
EndIf
If PeekS(*bufRecv) = "cn";(PeekB(*bufRecv) & #NETWORK_CONNECT) <> 0 ; process any connection attempts
; TODO: check if max connections reached before continuing
PrintN("[netConnect] received connection attempt..")
For cn = 0 To ArraySize(PlayerData())
If PlayerData(cn)\id = clientID ; player already in the server!
PrintN("[netConnect] player already connected!")
Break
EndIf
If cn = ArraySize(PlayerData()) ; reached the end of the array with no matches. he's legit.
PrintN("[netConnect] id was not found.. ")
If PlayerData(cn)\id = 0 ; check if last element is empty
;PrintN("[netConnect] current array["+Str(cn)+"] was empty, no need to recycle.")
PlayerData(cn)\id = clientID
PrintN("[netConnect] player["+Str(cn)+"] joined successfully.")
For x = 0 To ArraySize(PlayerData())
SendNetworkString(PlayerData(cn)\id, "c|"+Str(ArraySize(PlayerData())))
Next x
Break
Else
netRecycle = #False
For c = 0 To ArraySize(PlayerData()) ; recycle empty elements
;PrintN("[netConnect] attempting to recycle..")
If PlayerData(c)\id = 0
;PrintN("[netConnect] found empty slot, adding id to array and continuing...")
netRecycle = #True
PlayerData(c)\id = clientID
PrintN("[netConnect] player["+Str(cn)+"] joined successfully.")
For x = 0 To ArraySize(PlayerData())
SendNetworkString(PlayerData(cn)\id, "c|"+Str(ArraySize(PlayerData())))
Next x
Break
EndIf
Next c
If netRecycle = #False
;PrintN("[netConnect] recycling failed, resizing array.")
ReDim PlayerData.pdata(ArraySize(PlayerData())+1) ; resize
PrintN("[netConnect] player["+Str(cn)+"] joined successfully.")
For x = 0 To ArraySize(PlayerData())
SendNetworkString(PlayerData(cn)\id, "c|"+Str(ArraySize(PlayerData())))
Next x
PlayerData(cn+1)\id = clientID
Break
EndIf
EndIf
EndIf
Next cn
EndIf
; TODO: bit flag check should be optimized some how..
If (PeekB(*bufRecv + 4) & #KEYINPUT_LEFT) <> 0
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyLeft = #KEYINPUT_LEFT : EndIf
Next kz
Else
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyLeft = #FLAG_RESET : EndIf
Next kz
EndIf
If (PeekB(*bufRecv + 4) & #KEYINPUT_RIGHT) <> 0
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyRight = #KEYINPUT_RIGHT : EndIf
Next kz
Else
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyRight = #FLAG_RESET : EndIf
Next kz
EndIf
If (PeekB(*bufRecv + 4) & #KEYINPUT_UP) <> 0
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyUp = #KEYINPUT_UP : EndIf
Next kz
Else
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyUp = #FLAG_RESET : EndIf
Next kz
EndIf
If (PeekB(*bufRecv + 4) & #KEYINPUT_DOWN) <> 0
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyDown = #KEYINPUT_DOWN : EndIf
Next kz
Else
For kz = 0 To ArraySize(PlayerData())
If PlayerData(kz)\id = clientID : PlayerData(kz)\keyDown = #FLAG_RESET : EndIf
Next kz
EndIf
server_SendData()
EndProcedure
Procedure server_SendData() ; should only use this function after some event occurs...
Shared clientID
Static *struct.cdata
; TODO: need to only send data when a client structure has changed. probably don't need to include x/y until after bitFlag & key <> 0
; probably need to look into a way of comparing memory of each client..
*struct.cdata = AllocateMemory(SizeOf(cdata))
For y = 0 To ArraySize(PlayerData()) ; <- client to send data to
For x = 0 To ArraySize(PlayerData())
With *struct
\heartbeat = "hb"
\keyFlag = PlayerData(x)\keyDown | PlayerData(x)\keyLeft | PlayerData(x)\keyRight | PlayerData(x)\keyUp
;If PlayerData(y)\id = PlayerData(x)\id
; \id = -1 ; -1 = local client id, or the id that belongs to playerdata(0) on the client side -- no longer true
;Else
\id = PlayerData(x)\id
;EndIf
\x = PlayerData(x)\x
\y = PlayerData(x)\y
Debug "["+Str(PlayerData(x)\id)+"] [X: " +Str(PlayerData(x)\x) + "] [Y:"+Str(PlayerData(x)\y) + "]"
EndWith
SendNetworkData(PlayerData(y)\id, *struct, SizeOf(cdata))
Next x
Next y
EndProcedure