Also. Da ich gerne mal mit TCP rumspiele und auch mal Multiplayergames anfange zu programmieren, nervt es mich (bzw. ist zeitaufwendig) immer das ganze Zeugs mit den fetten (2D) Arrays zu machen. Also habe ich eine UDF geschrieben, die Variablen mit dem Empfänger austauscht.
Und zwar nimmt die SynVar UDF euch ein wenig Arbeit ab indem sie die Paketerstellung, Versand, Empfang und Verarbeitung macht. Also so ziemlich alles. Zusätzlich bietet die UDF eine Paketstrukturierung, die sehr effizient und effektiv ist.
Ich arbeite noch ein wenig an der UDF, denn wie man schnell feststellt, unterstützt es kein UDP. Bekannte Spiele wie Minecraft benutzen soweit ich weiß UDP zum übermitteln von Positionen etc. Ich möchte UDP einbauen, da ich in dem Beispielspiel wegen dem TCPRecv nur 10 FPS habe.
Ich habe das mit UDP noch nicht versucht, aber ich vermute es wird sehr schwierig, da UDP soweit ich weiß nur in eine Richtung geht. Deswegen frage ich euch mal: Wäre es möglich? Und wenn ja, dann auch ohne NAT traversal? Und wenn es nur mit NAT traversal geht, wie wäre es dann in AutoIt umsetzbar?
Hier die UDF:
Code:
#cs ----------------------------------------------------------------------------
AutoIt Version: 3.3.12.0
Author: FacePalmMan
Credits to: owadziak
Script Function:
Easy synchronization of variables between server and client(s) (or possibly even between servers (e.g. login-®istration server))
Pros:
-Easy to use, and easy to add more variables to be synchronized
Cons:
-Can't use UDP yet.
Functions:
_SynVar_Add
_SynVar_Read
_SynVar_Set
_SynVar_Handle
#ce ----------------------------------------------------------------------------
TCPStartup()
Global Const $PACKETDELIM = Chr(2)
Global Const $PARAMDELIM = Chr(3)
;===============================================================================
;
; Description: Adds a variable to a socket
; Syntax: _SynVar_Add($hCon, $sVariableName[, $sContent[, $bProtected]])
; Parameter(s):
; $hCon - The connection socket you get from TCPAccept or TCPConnect
; $sVariableName - The name the variable should have
; $sContent - The content it should have. Leave empty if you just want to declare it without content
; $bProtected - If True, the socket you are connected to, will neither know about the variable nor be able to edit it
;
; Return Value(s): On Success - Returns 1
; On Failure - Returns 0 (error: 1 when UDP used)
;
;===============================================================================
Func _SynVar_Add($hCon, $sVariableName, $sContent = "", $bProtected = False)
If IsArray($hCon) Then Return SetError(1, 0, False)
Local $bReturn = Assign($hCon & "_" & $sVariableName, $sContent, 2)
If $bReturn Then
Assign("Protect_" & $sVariableName, $bProtected)
If Not $bProtected Then __SynVar_SendVar($hCon, $sVariableName, $sContent)
EndIf
Return $bReturn
EndFunc ;==>_SynVar_Add
;===============================================================================
;
; Description: Gets the value from the variable assigned to the given socket
; Syntax: _SynVar_Read($hCon, $sVariableName)
; Parameter(s):
; $hCon - The socket of the client the variable is assigned to
; $sVariableName - The name of the variable
;
;
; Return Value(s): On Success - Returns content
; On Failure - Returns False (error: 1 when UDP used)
;
; Note: If the variable contains an array, it will return it as an
; array too.
;
;===============================================================================
Func _SynVar_Read($hCon, $sVariableName)
If IsArray($hCon) Then Return SetError(1, 0, False)
Return Eval($hCon & "_" & $sVariableName)
EndFunc ;==>_SynVar_Read
;===============================================================================
;
; Description: Changes the content of the variable
; Syntax: _SynVar_Set($hCon, $sVariableName, $sContent)
; Parameter(s):
; $hCon - The socket of the client the variable is assigned to
; $sVariableName - The name of the variable
; $sContent - The content you want to put in there.
; You can also use (1D and 2D) arrays
;
; Return Value(s): On Success - Returns 1
; On Failure - Returns 0 (error: 1 when UDP used; or WSA errors if TCP error occurred)
;
;===============================================================================
Func _SynVar_Set($hCon, $sVariableName, $sContent)
If IsArray($hCon) Then Return SetError(1, 0, False)
Local $bChanged = $sContent <> Eval($hCon & "_" & $sVariableName)
If $bChanged Then
Assign($hCon & "_" & $sVariableName, $sContent, 2)
Local $bReturn = __SynVar_SendVar($hCon, $sVariableName, $sContent)
Return SetError(@error, @extended, $bReturn)
Else
Return True
EndIf
EndFunc ;==>_SynVar_Set
;===============================================================================
;
; Description: Receives and handles SynCon-relevant data and returns the rest
; Syntax: _SynVar_Handle($hCon)
; Parameter(s):
; $hCon - The socket of the client the variable is assigned to
;
; Return Value(s): On Success - Returns all remaining packets in an array
; [0] = index (the number of packets)
; [1] = the first remaining packet
; [2] = the second remaining packet
; ...
; On Failure - Returns emty string and an error
; (If there is a problem with the socket, a
; WSA error is returned (like for example 10054))
;
;===============================================================================
Func _SynVar_Handle($hCon)
Local $sRecv = TCPRecv($hCon, 1048576)
Local $aReturn[1] = [0]
If @error > 0 Then Return SetError(@error, @extended, "")
If $sRecv <> "" Then
$aPackets = StringSplit($sRecv, $PACKETDELIM)
For $i = 1 To $aPackets[0]
$aParams = StringSplit($aPackets[$i], $PARAMDELIM)
If $aParams[1] = "0" Then
If $aParams[0] < 3 Then
ConsoleWrite("Badly formated packet (<3 params): " & $aPackets[$i])
ContinueLoop
EndIf
If Eval("Protect_" & $hCon) = "True" Then ContinueLoop
Assign($hCon & "_" & $aParams[2], $aParams[3])
ElseIf $aParams[1] = "1" Then
If $aParams[0] < 4 Then
ConsoleWrite("Badly formated packet (<4 params): " & $aPackets[$i])
ContinueLoop
EndIf
Local $aTemp[$aParams[3]]
For $j = 0 To $aParams[3] - 1
$aTemp[$j] = $aParams[4 + $j]
Next
If Eval("Protect_" & $hCon) = "True" Then ContinueLoop
Assign($hCon & "_" & $aParams[2], $aTemp)
ElseIf $aParams[1] = "2" Then
If $aParams[0] < 5 Then
ConsoleWrite("Badly formated packet (<5 params): " & $aPackets[$i])
ContinueLoop
EndIf
Local $aTemp[$aParams[3]][$aParams[4]]
For $j = 0 To $aParams[3] - 1
For $k = 0 To $aParams[4] - 1
$aTemp[$j][$k] = $aParams[5 + $k + ($aParams[4] * $j)]
Next
Next
If Eval("Protect_" & $hCon) = "True" Then ContinueLoop
Assign($hCon & "_" & $aParams[2], $aTemp)
ElseIf $aParams[1] <> "" Then
$aReturn[0] += 1
ReDim $aReturn[$aReturn[0] + 1]
$aReturn[$aReturn[0]] = $aPackets[$i]
EndIf
Next
EndIf
Return $aReturn
EndFunc ;==>_SynVar_Handle
Func __SynVar_SendVar($hCon, $sVariableName, $sContent)
Local $i
If Not IsArray($sContent) Then
TCPSend($hCon, "0" & $PARAMDELIM & $sVariableName & $PARAMDELIM & $sContent & $PACKETDELIM)
Else
If UBound($sContent, 0) = 1 Then
Local $sString = "1" & $PARAMDELIM & $sVariableName & $PARAMDELIM & UBound($sContent, 1)
For $i = 0 To UBound($sContent, 1) - 1
$sString &= $PARAMDELIM & $sContent[$i]
Next
TCPSend($hCon, $sString & $PACKETDELIM)
ElseIf UBound($sContent, 0) = 2 Then
Local $sString = "2" & $PARAMDELIM & $sVariableName & $PARAMDELIM & UBound($sContent, 1) & $PARAMDELIM & UBound($sContent, 2)
For $i = 0 To UBound($sContent, 1) - 1
For $j = 0 To UBound($sContent, 2) - 1
$sString &= $PARAMDELIM & $sContent[$i][$j]
Next
Next
TCPSend($hCon, $sString & $PACKETDELIM)
EndIf
EndIf
Return SetError(@error, @extended, True)
EndFunc
Hier das TestSpiel:
Code:
#cs ----------------------------------------------------------------------------
AutoIt Version: 3.3.12.0
Author: FacePalmMan
Script Function:
A client
#ce ----------------------------------------------------------------------------
#include <GDIPlus.au3>
#include <SynVar.au3>
#include <Array.au3>
TCPStartup()
_GDIPlus_Startup()
Opt("MouseCoordMode", 2)
Global $ahGraphics[3]
Global $hSocket
Global $aEnemies[1][5] = [[0]] ;[name,x,y,rotation,health]
Global Const $RAD = 180 / 3.14159265358979323846264
$hGUI = GUICreate("SynVar Testclient", 800, 600)
GUISetState()
$ahGraphics[0] = _GDIPlus_GraphicsCreateFromHWND($hGUI)
$ahGraphics[1] = _GDIPlus_BitmapCreateFromGraphics(800, 600, $ahGraphics[0])
$ahGraphics[2] = _GDIPlus_ImageGetGraphicsContext($ahGraphics[1])
$sIP = InputBox("SynVar Testclient - IP", "Please enter the server ip")
$sName = InputBox("SynVar Testclient - Name", "Please choose a username")
$hSocket = TCPConnect($sIP, 25565)
If $hSocket = -1 Then
MsgBox(16, "SynVar Testclient", "Unable to connect to " & $sIP & " - error " & @error & @CRLF & "closing...")
Exit
EndIf
_SynVar_Add($hSocket, "x", 400)
_SynVar_Add($hSocket, "y", 300)
_SynVar_Add($hSocket, "rotation", 0)
_SynVar_Add($hSocket, "name", $sName)
_SynVar_Add($hSocket, "health", 100)
_SynVar_Add($hSocket, "bIsPressed_W", False)
_SynVar_Add($hSocket, "bIsPressed_A", False)
_SynVar_Add($hSocket, "bIsPressed_S", False)
_SynVar_Add($hSocket, "bIsPressed_D", False)
_SynVar_Add($hSocket, "bIsPressed_LMB", False)
_SynVar_Add($hSocket, "enemies", $aEnemies) ;This here is supposed to be a 2D array
$hTimer = TimerInit()
$hFPSTimer = TimerInit()
$iTimerCounter = 1
$iFPS = 0
While GUIGetMsg() <> -3
$iTimerdiff = TimerDiff($hTimer)
$hTimer = TimerInit() ;For the smooth moving
$iTimerCounter += 1
If TimerDiff($hFPSTimer) > 1000 Then ;Timer stuff for the FPS display
$hFPSTimer = TimerInit()
$iFPS = 1000 / $iTimerCounter
$iTimerCounter = 0
EndIf
If WinActive($hGUI) Then ;If the game window is open, update the mouse rotation and the key statuses, as well as the position.
$aTemp = MouseGetPos()
_SynVar_Set($hSocket, "rotation", - __CalcAngle(_SynVar_Read($hSocket, "x"), _SynVar_Read($hSocket, "y"), $aTemp[0], $aTemp[1]))
If _IsPressed("57") <> _SynVar_Read($hSocket, "bIsPressed_W") Then _SynVar_Set($hSocket, "bIsPressed_W", _IsPressed("57"))
If _IsPressed("41") <> _SynVar_Read($hSocket, "bIsPressed_A") Then _SynVar_Set($hSocket, "bIsPressed_A", _IsPressed("41"))
If _IsPressed("53") <> _SynVar_Read($hSocket, "bIsPressed_S") Then _SynVar_Set($hSocket, "bIsPressed_S", _IsPressed("53"))
If _IsPressed("44") <> _SynVar_Read($hSocket, "bIsPressed_D") Then _SynVar_Set($hSocket, "bIsPressed_D", _IsPressed("44"))
If _IsPressed("01") <> _SynVar_Read($hSocket, "bIsPressed_LMB") Then _SynVar_Set($hSocket, "bIsPressed_LMB", _IsPressed("01"))
If _SynVar_Read($hSocket, "bIsPressed_W") Then _SynVar_Set($hSocket, "y", _SynVar_Read($hSocket, "y") - $iTimerdiff * 0.05)
If _SynVar_Read($hSocket, "bIsPressed_S") Then _SynVar_Set($hSocket, "y", _SynVar_Read($hSocket, "y") + $iTimerdiff * 0.05)
If _SynVar_Read($hSocket, "bIsPressed_A") Then _SynVar_Set($hSocket, "x", _SynVar_Read($hSocket, "x") - $iTimerdiff * 0.05)
If _SynVar_Read($hSocket, "bIsPressed_D") Then _SynVar_Set($hSocket, "x", _SynVar_Read($hSocket, "x") + $iTimerdiff * 0.05)
EndIf
_GDIPlus_GraphicsClear($ahGraphics[2], 0xFFFFFFFF) ;Here finally comes the GDI+ stuff!
_GDIPlus_GraphicsDrawString($ahGraphics[2], "FPS: " & $iFPS, 0, 0)
$aEnemies = _SynVar_Read($hSocket, "enemies")
If IsArray($aEnemies) And UBound($aEnemies, 0) = 2 Then
For $i = 1 To $aEnemies[0][0] ;Draw the players
_GDIPlus_GraphicsDrawString($ahGraphics[2], $aEnemies[$i][3], $aEnemies[$i][0], $aEnemies[$i][1])
_GDIPlus_GraphicsDrawLine($ahGraphics[2], $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] - 45) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] - 45) / $RAD), $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 45) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 45) / $RAD))
_GDIPlus_GraphicsDrawLine($ahGraphics[2], $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 45) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 45) / $RAD), $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 135) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 135) / $RAD))
_GDIPlus_GraphicsDrawLine($ahGraphics[2], $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 135) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 135) / $RAD), $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 225) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 225) / $RAD))
_GDIPlus_GraphicsDrawLine($ahGraphics[2], $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 225) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 225) / $RAD), $aEnemies[$i][0] + 16 * Sin(($aEnemies[$i][2] + 315) / $RAD), $aEnemies[$i][1] + 16 * Cos(($aEnemies[$i][2] + 315) / $RAD))
Next
EndIf
_GDIPlus_GraphicsDrawImage($ahGraphics[0], $ahGraphics[1], 0, 0)
$aPackets = _SynVar_Handle($hSocket)
If $aPackets[0] > 0 Then
MsgBox(0, "SynVar Testclient - Uhm what?", "Looool. There is at least one unhandled packet. What went wrong here?")
_ArrayDisplay($aPackets)
EndIf
WEnd
Func __CalcAngle($x, $y, $x2, $y2)
Local $iRad = 180 / 3.14159265358979323846264
Local $iReturn = ($y2 - $y) / ($x2 - $x)
If $x = $x2 And $y = $y2 Then Return 0
If $x2 >= $x And $y2 >= $y Then
Return (ATan($iReturn) * $iRad) + 270
ElseIf $x2 <= $x Then
Return (ATan($iReturn) * $iRad) + 90
Else
Return (ATan($iReturn) * $iRad) + 270
EndIf
EndFunc
Func __Rotate($iX, $iY, $iXC, $iYC, $fAngle)
Local $aReturn[2]
Local $fCos = Cos(ATan(1) / 45 * $fAngle)
Local $fSin = Sin(ATan(1) / 45 * $fAngle)
Local $iXn, $iYn
$iXn = $iX - $iXC
$iYn = $iY - $iYC
$aReturn[0] = $iXC + Round($iXn * $fCos - $iYn * $fSin)
$aReturn[1] = $iYC + Round($iXn * $fSin + $iYn * $fCos)
Return $aReturn
EndFunc
Func _IsPressed($sHexKey, $vDLL = 'user32.dll')
Return BitAND(DllCall($vDLL, "short", "GetAsyncKeyState", "int", '0x' & $sHexKey)[0], 0x8000) <> 0
EndFunc ;==>_IsPressed
Und hier der Server zum Spiel:
Code:
#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.12.0 Author: FacePalmMan Script Function: A server #ce ---------------------------------------------------------------------------- #include <SynVar.au3> TCPStartup() Global $hSocket = TCPListen(@IPAddress1, 25565) If $hSocket = -1 Then MsgBox(16, "SynVar Testserver", "Unable to start server. Error " & @error) Exit EndIf Global $ahClients[1] = [0] Global $aTemp[1][1] = [[0]] While 1 $hAcc = TCPAccept($hSocket) If $hAcc <> -1 Then $ahClients[0] += 1 ReDim $ahClients[$ahClients[0] + 1] $ahClients[$ahClients[0]] = $hAcc _SynVar_Add($hAcc, "x", 400) _SynVar_Add($hAcc, "y", 300) _SynVar_Add($hAcc, "rotation", 0) _SynVar_Add($hAcc, "name", "") _SynVar_Add($hAcc, "health", 100) _SynVar_Add($hAcc, "enemies") $hTemp_Timer = TimerInit() While (_Synvar_Read($hAcc, "name") <> "") And TimerDiff($hTemp_Timer) < 5000 _SynVar_Handle($hAcc) WEnd ReDim $aTemp[$ahClients[0] + 1][5] $aTemp[0][0] = $ahClients[0] For $i = 1 To $aTemp[0][0] $aTemp[$i][0] = _Synvar_Read($ahClients[$i], "x") $aTemp[$i][1] = _Synvar_Read($ahClients[$i], "y") $aTemp[$i][2] = _Synvar_Read($ahClients[$i], "rotation") $aTemp[$i][3] = _Synvar_Read($ahClients[$i], "name") $aTemp[$i][4] = _Synvar_Read($ahClients[$i], "health") Next For $i = 1 To $ahClients[0] _SynVar_Set($ahClients[$i], "enemies", $aTemp) Next EndIf For $i = 1 To $ahClients[0] _SynVar_Handle($ahClients[$i]) Next For $i = 1 To $aTemp[0][0] $aTemp[$i][0] = _Synvar_Read($ahClients[$i], "x") $aTemp[$i][1] = _Synvar_Read($ahClients[$i], "y") $aTemp[$i][2] = _Synvar_Read($ahClients[$i], "rotation") $aTemp[$i][3] = _Synvar_Read($ahClients[$i], "name") $aTemp[$i][4] = _Synvar_Read($ahClients[$i], "health") Next For $i = 1 To $ahClients[0] _SynVar_Set($ahClients[$i], "enemies", $aTemp) Next WEnd







