git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2463 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
meep-eep
2006-10-26 22:17:03 +00:00
parent ea1657a589
commit 620c00c52c
117 changed files with 12189 additions and 425 deletions
+285 -12
View File
@@ -44,7 +44,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FD /c
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
@@ -52,8 +52,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Stripping debug info...
@@ -74,7 +74,7 @@ PostBuild_Cmds=rebase -b 0x400000 -x . "../../uqm.exe"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
@@ -82,8 +82,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# SUBTRACT LINK32 /nodefaultlib
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
@@ -102,7 +102,7 @@ LINK32=link.exe
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "ZLIB_DLL" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
# SUBTRACT BASE CPP /YX
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /GZ /c
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /GZ /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
@@ -110,8 +110,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo /o"UrQuanMasters.bsc"
# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
# SUBTRACT LINK32 /nodefaultlib
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
@@ -130,7 +130,7 @@ LINK32=link.exe
# PROP Target_Dir ""
# ADD BASE CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "ZLIB_DLL" /D "USE_PLATFORM_ACCEL" /FD /c
# SUBTRACT BASE CPP /YX
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /c
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /c
# SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
@@ -138,8 +138,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
# Begin Special Build Tool
SOURCE="$(InputPath)"
PostBuild_Desc=Stripping debug info...
@@ -1333,6 +1333,114 @@ SOURCE=..\sc2code\libs\mikmod\virtch2.c
SOURCE=..\sc2code\libs\mikmod\virtch_common.c
# End Source File
# End Group
# Begin Group "network"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\libs\network\bytesex.h
# End Source File
# Begin Group "connect"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\connect.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\connect.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\listen.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\listen.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\resolve.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\connect\resolve.h
# End Source File
# End Group
# Begin Group "netmanager"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\ndesc.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\ndesc.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\ndindex.ci
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\netmanager_common.ci
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\netmanager.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\netmanager_win.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netmanager\netmanager_win.h
# End Source File
# End Group
# Begin Source File
SOURCE=..\sc2code\libs\network\netport.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\netport.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\network_win.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\network.h
# End Source File
# Begin Group "socket"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\libs\network\socket\socket.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\socket\socket.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\socket\socket_win.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\network\socket\socket_win.h
# End Source File
# End Group
# End Group
# Begin Source File
SOURCE=..\sc2code\libs\alarm.h
@@ -1387,6 +1495,10 @@ SOURCE=..\sc2code\libs\misc.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\net.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\platform.h
# End Source File
# Begin Source File
@@ -2333,6 +2445,167 @@ SOURCE=..\sc2code\comm\zoqfot\zoqfotc.c
SOURCE=..\sc2code\comm\commall.h
# End Source File
# End Group
# Begin Group "netplay"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\netplay\checksum.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\checksum.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\checkbuf.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\checkbuf.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\crc.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\crc.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\nc_connect.ci
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netconnection.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netconnection.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netinput.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netinput.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netoptions.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netoptions.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netmelee.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netmelee.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netmisc.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netmisc.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netplay.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netrcv.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netrcv.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netsend.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netsend.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netstate.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\netstate.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\notify.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\notify.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packet.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packet.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packethandlers.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packethandlers.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packetsenders.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packetsenders.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packetq.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\packetq.h
# End Source File
# Begin Group "proto"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\sc2code\netplay\proto\npconfirm.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\proto\npconfirm.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\proto\ready.c
# End Source File
# Begin Source File
SOURCE=..\sc2code\netplay\proto\ready.h
# End Source File
# End Group
# End Group
# Begin Group "planets"
-1
View File
@@ -110,4 +110,3 @@ readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
}
#endif
+272
View File
@@ -128,6 +128,278 @@ typedef unsigned short wchar_t;
typedef unsigned int wint_t;
#endif
// errno error numbers. The values used don't matter, as long as they
// don't conflict with existing errno error numbers.
#if defined (PORT_WANT_ERRNO) && defined (_MSC_VER)
# include <errno.h>
# ifndef E2BIG
# define E2BIG 0x01000001
# endif
# ifndef EACCES
# define EACCES 0x01000002
# endif
# ifndef EADDRINUSE
# define EADDRINUSE 0x01000003
# endif
# ifndef EADDRNOTAVAIL
# define EADDRNOTAVAIL 0x01000004
# endif
# ifndef EAFNOSUPPORT
# define EAFNOSUPPORT 0x01000005
# endif
# ifndef EAGAIN
# ifdef EWOULDBLOCK
# define EAGAIN EWOULDBLOCK
# else
# define EAGAIN 0x01000006
# endif
# endif
# ifndef EALREADY
# define EALREADY 0x01000007
# endif
# ifndef EBADF
# define EBADF 0x01000008
# endif
# ifndef EBADMSG
# define EBADMSG 0x01000009
# endif
# ifndef EBUSY
# define EBUSY 0x0100000a
# endif
# ifndef ECANCELED
# define ECANCELED 0x0100000b
# endif
# ifndef ECHILD
# define ECHILD 0x0100000c
# endif
# ifndef ECONNABORTED
# define ECONNABORTED 0x0100000d
# endif
# ifndef ECONNREFUSED
# define ECONNREFUSED 0x0100000e
# endif
# ifndef ECONNRESET
# define ECONNRESET 0x0100000f
# endif
# ifndef EDEADLK
# define EDEADLK 0x01000010
# endif
# ifndef EDESTADDRREQ
# define EDESTADDRREQ 0x01000011
# endif
# ifndef EDOM
# define EDOM 0x01000012
# endif
// Reserved in POSIX
//# ifndef //EDQUOT
//# define //EDQUOT 0x01000013
//# endif
# ifndef EEXIST
# define EEXIST 0x01000014
# endif
# ifndef EFAULT
# define EFAULT 0x01000015
# endif
# ifndef EFBIG
# define EFBIG 0x01000016
# endif
# ifndef EHOSTUNREACH
# define EHOSTUNREACH 0x01000017
# endif
# ifndef EIDRM
# define EIDRM 0x01000018
# endif
# ifndef EILSEQ
# define EILSEQ 0x01000019
# endif
# ifndef EINPROGRESS
# define EINPROGRESS 0x0100001a
# endif
# ifndef EINTR
# define EINTR 0x0100001b
# endif
# ifndef EINVAL
# define EINVAL 0x0100001c
# endif
# ifndef EIO
# define EIO 0x0100001d
# endif
# ifndef EISCONN
# define EISCONN 0x0100001e
# endif
# ifndef EISDIR
# define EISDIR 0x0100001f
# endif
# ifndef ELOOP
# define ELOOP 0x01000020
# endif
# ifndef EMFILE
# define EMFILE 0x01000021
# endif
# ifndef EMLINK
# define EMLINK 0x01000022
# endif
# ifndef EMSGSIZE
# define EMSGSIZE 0x01000023
# endif
// Reserved in POSIX
//# ifndef //EMULTIHOP
//# define //EMULTIHOP 0x01000024
//# endif
# ifndef ENAMETOOLONG
# define ENAMETOOLONG 0x01000025
# endif
# ifndef ENETDOWN
# define ENETDOWN 0x01000026
# endif
# ifndef ENETRESET
# define ENETRESET 0x01000027
# endif
# ifndef ENETUNREACH
# define ENETUNREACH 0x01000028
# endif
# ifndef ENFILE
# define ENFILE 0x01000029
# endif
# ifndef ENOBUFS
# define ENOBUFS 0x0100002a
# endif
# ifndef ENODATA
# define ENODATA 0x0100002b
# endif
# ifndef ENODEV
# define ENODEV 0x0100002c
# endif
# ifndef ENOENT
# define ENOENT 0x0100002d
# endif
# ifndef ENOEXEC
# define ENOEXEC 0x0100002e
# endif
# ifndef ENOLCK
# define ENOLCK 0x0100002f
# endif
// Reserved in POSIX
//# ifndef ENOLINK
//# define ENOLINK 0x01000030
//# endif
# ifndef ENOMEM
# define ENOMEM 0x01000031
# endif
# ifndef ENOMSG
# define ENOMSG 0x01000032
# endif
# ifndef ENOPROTOOPT
# define ENOPROTOOPT 0x01000033
# endif
# ifndef ENOSPC
# define ENOSPC 0x01000034
# endif
# ifndef ENOSR
# define ENOSR 0x01000035
# endif
# ifndef ENOSTR
# define ENOSTR 0x01000036
# endif
# ifndef ENOSYS
# define ENOSYS 0x01000037
# endif
# ifndef ENOTCONN
# define ENOTCONN 0x01000038
# endif
# ifndef ENOTDIR
# define ENOTDIR 0x01000039
# endif
# ifndef ENOTEMPTY
# define ENOTEMPTY 0x0100003a
# endif
# ifndef ENOTSOCK
# define ENOTSOCK 0x0100003b
# endif
# ifndef ENOTSUP
# define ENOTSUP 0x0100003c
# endif
# ifndef ENOTTY
# define ENOTTY 0x0100003d
# endif
# ifndef ENXIO
# define ENXIO 0x0100003e
# endif
# ifndef EOPNOTSUPP
# define EOPNOTSUPP 0x0100003f
# endif
# ifndef EOVERFLOW
# define EOVERFLOW 0x01000040
# endif
# ifndef EPERM
# define EPERM 0x01000041
# endif
# ifndef EPIPE
# define EPIPE 0x01000042
# endif
# ifndef EPROTO
# define EPROTO 0x01000043
# endif
# ifndef EPROTONOSUPPORT
# define EPROTONOSUPPORT 0x01000044
# endif
# ifndef EPROTOTYPE
# define EPROTOTYPE 0x01000045
# endif
# ifndef ERANGE
# define ERANGE 0x01000046
# endif
# ifndef EROFS
# define EROFS 0x01000047
# endif
# ifndef ESPIPE
# define ESPIPE 0x01000048
# endif
# ifndef ESRCH
# define ESRCH 0x01000049
# endif
// Reserved in POSIX
//# ifndef //ESTALE
//# define //ESTALE 0x0100004a
//# endif
# ifndef ETIME
# define ETIME 0x0100004b
# endif
# ifndef ETIMEDOUT
# define ETIMEDOUT 0x0100004c
# endif
# ifndef ETXTBSY
# define ETXTBSY 0x0100004d
# endif
# ifndef EWOULDBLOCK
# ifdef EAGAIN
# define EWOULDBLOCK EAGAIN
# else
# define EWOULDBLOCK 0x0100004e
# endif
# endif
# ifndef EXDEV
# define EXDEV 0x0100004f
# endif
// Non-POSIX:
# ifndef EHOSTDOWN
# define EHOSTDOWN 0x01100001
# endif
# ifndef EPFNOSUPPORT
# define EPFNOSUPPORT 0x01100002
# endif
# ifndef EPROCLIM
# define EPROCLIM 0x01100003
# endif
# ifndef ESHUTDOWN
# define ESHUTDOWN 0x01100004
# endif
# ifndef ESOCKTNOSUPPORT
# define ESOCKTNOSUPPORT 0x01100005
# endif
#endif /* defined (PORT_WANT_ERRNO) && defined (_MSC_VER) */
// Use SDL_INCLUDE and SDL_IMAGE_INCLUDE to portably include the SDL files
// from the right location.
// TODO: Where the SDL and SDL_image headers are located could be detected
+4
View File
@@ -9,3 +9,7 @@ uqm_CFILES="battle.c border.c build.c cleanup.c clock.c collide.c comm.c
settings.c setup.c setupmenu.c ship.c shipstat.c shipyard.c
sis.c sounds.c starbase.c starcon.c starmap.c state.c status.c
tactrans.c trans.c uqmdebug.c util.c velocity.c weapon.c"
if [ -n "$uqm_NETPLAY" ]; then
uqm_SUBDIRS="$uqm_SUBDIRS netplay"
fi
+209 -12
View File
@@ -21,6 +21,12 @@
#include "controls.h"
#include "init.h"
#include "intel.h"
#ifdef NETPLAY
# include "netplay/netmelee.h"
# ifdef NETPLAY_CHECKSUM
# include "netplay/checksum.h"
# endif
#endif
#include "resinst.h"
#include "setup.h"
#include "settings.h"
@@ -32,6 +38,15 @@
QUEUE disp_q;
SIZE battle_counter;
BOOLEAN instantVictory;
size_t battleInputOrder[NUM_SIDES];
// Indices of the sides in the order their input is processed.
// Network sides are last so that the sides will never be waiting
// on eachother, and games with a 0 frame delay are theoretically
// possible.
#ifdef NETPLAY
BattleFrameCounter battleFrameCount;
// Used for synchronisation purposes during netplay.
#endif
static BOOLEAN
RunAwayAllowed (void)
@@ -67,7 +82,7 @@ DoRunAway (STARSHIPPTR StarShipPtr)
~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
SetPrimColor (&DisplayArray[ElementPtr->PrimIndex],
BUILD_COLOR (MAKE_RGB15 (0xB, 0x00, 0x00), 0x2E));
BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2E));
SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMPFILL_PRIM);
CyborgDescPtr->ship_input_state = 0;
@@ -75,17 +90,54 @@ DoRunAway (STARSHIPPTR StarShipPtr)
UnlockElement (StarShipPtr->hShip);
}
static void
setupBattleInputOrder(void)
{
size_t i;
#ifndef NETPLAY
for (i = 0; i < NUM_SIDES; i++)
battleInputOrder[i] = i;
#else
int j;
i = 0;
// First put the locally controlled players in the array.
for (j = 0; j < NUM_SIDES; j++) {
if (!(PlayerControl[j] & NETWORK_CONTROL)) {
battleInputOrder[i] = j;
i++;
}
}
// Next put the network controlled players in the array.
for (j = 0; j < NUM_SIDES; j++) {
if (PlayerControl[j] & NETWORK_CONTROL) {
battleInputOrder[i] = j;
i++;
}
}
#endif
}
static void
ProcessInput (void)
{
BOOLEAN CanRunAway;
size_t sideI;
#ifdef NETPLAY
netInput ();
#endif
CanRunAway = RunAwayAllowed ();
for (cur_player = NUM_SIDES - 1; cur_player >= 0; --cur_player)
for (sideI = 0; sideI < NUM_SIDES; sideI++)
{
HSTARSHIP hBattleShip, hNextShip;
cur_player = battleInputOrder[sideI];
for (hBattleShip = GetHeadLink (&race_q[cur_player]);
hBattleShip != 0; hBattleShip = hNextShip)
{
@@ -99,10 +151,24 @@ ProcessInput (void)
{
CyborgDescPtr = StarShipPtr;
InputState = (*(PlayerInput[cur_player]))();
InputState = (*(PlayerInput[cur_player]))(cur_player,
StarShipPtr);
#if CREATE_JOURNAL
JournalInput (InputState);
#endif /* CREATE_JOURNAL */
#ifdef NETPLAY
if (PlayerControl[cur_player] & HUMAN_CONTROL)
{
BattleInputBuffer *bib = getBattleInputBuffer(cur_player);
sendBattleInputConnections (InputState);
flushPacketQueues ();
BattleInputBuffer_push (bib, InputState);
// Add this input to the end of the buffer.
BattleInputBuffer_pop (bib, &InputState);
// Get the input from the front of the buffer.
}
#endif
CyborgDescPtr->ship_input_state = 0;
if (CyborgDescPtr->RaceDescPtr->ship_info.crew_level)
@@ -128,6 +194,10 @@ ProcessInput (void)
UnlockStarShip (&race_q[cur_player], hBattleShip);
}
}
#ifdef NETPLAY
flushPacketQueues ();
#endif
if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT))
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
@@ -180,7 +250,39 @@ DoBattle (BATTLE_STATE *bs)
bs->MenuRepeatDelay = 0;
SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
if (getNumNetConnections() > 0 &&
battleFrameCount % NETPLAY_CHECKSUM_INTERVAL == 0)
{
crc_State state;
Checksum checksum;
crc_init(&state);
crc_processDispQueue (&state);
checksum = (Checksum) crc_finish (&state);
sendChecksumConnections ((uint32) battleFrameCount,
(uint32) checksum);
flushPacketQueues ();
addLocalChecksum (battleFrameCount, checksum);
}
#endif
ProcessInput ();
// Also calls NetInput()
#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
if (getNumNetConnections() > 0)
{
size_t delay = getBattleInputDelay();
if (battleFrameCount >= delay
&& (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0)
{
if (!verifyChecksums (battleFrameCount - delay))
GLOBAL(CurrentActivity) |= CHECK_ABORT;
}
}
#endif
LockMutex (GraphicsLock);
if (bs->first_time)
{
@@ -220,7 +322,79 @@ DoBattle (BATTLE_STATE *bs)
+ BATTLE_FRAME_RATE / (battle_speed + 1));
bs->NextTime = GetTimeCounter ();
}
return (BOOLEAN) ((GLOBAL (CurrentActivity) & IN_BATTLE) != 0);
if ((GLOBAL (CurrentActivity) & IN_BATTLE) == 0)
return FALSE;
#ifdef NETPLAY
battleFrameCount++;
#endif
return TRUE;
}
// Let each player pick his ship.
static BOOLEAN
selectAllShips (SIZE num_ships)
{
#ifndef NETPLAY
while (num_ships--)
{
if (!GetNextStarShip (NULL_PTR, num_ships == 1))
return FALSE;
}
return TRUE;
#else
// On network play, what is the top player to one party may be
// the bottom player the other. To keep both sides synchronised
// the order is always the same.
SIZE order[2];
if (num_ships == 1) {
// HyperSpace in full game.
return GetNextStarShip (NULL_PTR, 0);
}
if (num_ships != 2)
{
fprintf(stderr, "More than two players is not supported.\n");
return FALSE;
}
if ((PlayerControl[0] & NETWORK_CONTROL) &&
(PlayerControl[1] & NETWORK_CONTROL))
{
fprintf(stderr, "Only one side at a time can be network "
"controlled.\n");
return FALSE;
}
// Iff 'myTurn' is set on a connection, the local party will be
// processed first.
// If neither is network controlled, the top player (1) is handled first.
if (((PlayerControl[0] & NETWORK_CONTROL) &&
!NetConnection_getDiscriminant (netConnections[0])) ||
((PlayerControl[1] & NETWORK_CONTROL) &&
NetConnection_getDiscriminant (netConnections[1])))
{
order[0] = 0;
order[1] = 1;
}
else
{
order[0] = 1;
order[1] = 0;
}
{
size_t i;
for (i = 0; i < 2; i++)
{
if (!GetNextStarShip (NULL_PTR, order[i]))
return FALSE;
}
}
return TRUE;
#endif
}
BOOLEAN
@@ -233,7 +407,10 @@ Battle (void)
SetResourceIndex (hResIndex);
#if !(DEMO_MODE || CREATE_JOURNAL)
TFB_SeedRandom (GetTimeCounter ());
if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE) {
// In Supermelee, the RNG is already initialised.
TFB_SeedRandom (GetTimeCounter ());
}
#else /* DEMO_MODE */
if (BattleSeed == 0)
BattleSeed = TFB_Random ();
@@ -262,21 +439,41 @@ Battle (void)
CountLinks (&race_q[1])
);
while (num_ships--)
{
if (!GetNextStarShip (NULL_PTR, num_ships))
goto AbortBattle;
}
if (!selectAllShips (num_ships))
goto AbortBattle;
BattleSong (TRUE);
bs.NextTime = 0;
bs.InputFunc = &DoBattle;
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE);
setupBattleInputOrder ();
#ifdef NETPLAY
initBattleInputBuffers ();
#ifdef NETPLAY_CHECKSUM
initChecksumBuffers ();
#endif
battleFrameCount = 0;
initBattleStateDataConnections ();
{
bool allOk = negotiateReadyConnections (true, NetState_inBattle);
if (!allOk)
goto AbortBattle;
}
#endif /* NETPLAY */
bs.InputFunc = DoBattle;
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) ==
IN_HYPERSPACE);
UnlockMutex (GraphicsLock);
DoInput ((PVOID)&bs, FALSE);
LockMutex (GraphicsLock);
AbortBattle:
#ifdef NETPLAY
uninitBattleInputBuffers();
#ifdef NETPLAY_CHECKSUM
uninitChecksumBuffers ();
#endif
#endif /* NETPLAY */
StopMusic ();
StopSound ();
}
+4
View File
@@ -22,6 +22,10 @@
extern QUEUE disp_q;
extern SIZE battle_counter;
extern BOOLEAN instantVictory;
#ifdef NETPLAY
typedef DWORD BattleFrameCounter;
extern BattleFrameCounter battleFrameCount;
#endif
extern BOOLEAN Battle (void);
+2 -4
View File
@@ -60,8 +60,6 @@
static int ambient_anim_task (void *data);
static BOOLEAN getLineWithinWidth(TEXT *pText,
const unsigned char **startNext, SIZE maxWidth, COUNT maxChars);
#define MAX_RESPONSES 8
#define BACKGROUND_VOL \
@@ -355,8 +353,8 @@ add_text (int status, PTEXT pTextIn)
// pText is the text to be fitted. pText->CharCount will be set to the
// number of characters that fitted.
// startNext will be filled with the start of the first word that
// doesn't fit in one line, or if an entire line fitted, to the character
// past the newline, or if the entire string fitted, to the end of the
// doesn't fit in one line, or if an entire line fits, to the character
// past the newline, or if the entire string fits, to the end of the
// string.
// maxWidth is the maximum number of pixels that a line may be wide
// maxChars is the maximum number of characters (not bytes) that are to
+2
View File
@@ -24,6 +24,8 @@ extern void init_communication (void);
extern void uninit_communication (void);
extern int do_subtitles (UNICODE *pStr);
extern void AlienTalkSegue (COUNT wait_track);
BOOLEAN getLineWithinWidth(TEXT *pText, const unsigned char **startNext,
SIZE maxWidth, COUNT maxChars);
extern RECT CommWndRect; /* comm window rect */
+8 -4
View File
@@ -19,6 +19,9 @@
#ifndef _CONTROLS_H_
#define _CONTROLS_H_
#include "races.h"
// For STARSHIPPTR
#include "libs/compiler.h"
#include "libs/strlib.h"
@@ -85,8 +88,9 @@ typedef UBYTE BATTLE_INPUT_STATE;
#define BATTLE_ESCAPE ((BATTLE_INPUT_STATE)(1 << 5))
#define BATTLE_DOWN ((BATTLE_INPUT_STATE)(1 << 6))
typedef BATTLE_INPUT_STATE (*battle_summary_func) (void);
extern battle_summary_func ComputerInput, HumanInput[];
typedef BATTLE_INPUT_STATE (*battle_summary_func) (COUNT player,
STARSHIPPTR StarShipPtr);
extern battle_summary_func ComputerInput, HumanInput[], NetworkInput;
extern battle_summary_func PlayerInput[];
extern CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState;
@@ -107,8 +111,8 @@ extern void WaitForNoInput (SIZE Duration);
extern BOOLEAN ConfirmExit (void);
extern void DoInput (PVOID pInputState, BOOLEAN resetInput);
BATTLE_INPUT_STATE p1_combat_summary (void);
BATTLE_INPUT_STATE p2_combat_summary (void);
BATTLE_INPUT_STATE p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
BATTLE_INPUT_STATE p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
extern volatile BOOLEAN GamePaused, ExitRequested;
+4 -5
View File
@@ -1010,7 +1010,7 @@ Avoid (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR EvalDescPtr)
}
BATTLE_INPUT_STATE
tactical_intelligence (void)
tactical_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
{
ELEMENTPTR ShipPtr;
ELEMENT Ship;
@@ -1019,10 +1019,9 @@ tactical_intelligence (void)
COUNT ConcernCounter;
EVALUATE_DESC ObjectsOfConcern[10];
BOOLEAN ShipMoved, UltraManeuverable;
STARSHIPPTR StarShipPtr, EnemyStarShipPtr;
STARSHIPPTR EnemyStarShipPtr;
RACE_DESCPTR RDPtr, EnemyRDPtr;
StarShipPtr = CyborgDescPtr;
RDPtr = StarShipPtr->RaceDescPtr;
if (RDPtr->cyborg_control.ManeuverabilityIndex == 0)
@@ -1037,7 +1036,7 @@ tactical_intelligence (void)
}
ShipMoved = TRUE;
if (PlayerControl[cur_player] & STANDARD_RATING)
if (PlayerControl[player] & STANDARD_RATING)
++StarShipPtr->special_counter;
#ifdef DEBUG_CYBORG
@@ -1260,7 +1259,7 @@ if (!(ShipPtr->state_flags & FINITE_LIFE)
ed.which_turn = 0;
}
}
else if (!(PlayerControl[cur_player] & AWESOME_RATING))
else if (!(PlayerControl[player] & AWESOME_RATING))
ed.which_turn = 0;
else
{
+2
View File
@@ -43,6 +43,7 @@
typedef QUEUE_HANDLE HELEMENT;
// Bits for ELEMENT_FLAGS:
#define GOOD_GUY (1 << 0)
#define BAD_GUY (1 << 1)
#define PLAYER_SHIP (1 << 2)
@@ -65,6 +66,7 @@ typedef QUEUE_HANDLE HELEMENT;
#define CREW_OBJECT (1 << 14)
#define BACKGROUND_OBJECT (1 << 15)
#define HYPERJUMP_LIFE 15
#define NUM_EXPLOSION_FRAMES 12
+1 -1
View File
@@ -260,7 +260,7 @@ InitGalaxy (void)
{
SetPrimType (&DisplayArray[p], STAMP_PRIM);
SetPrimColor (&DisplayArray[p],
BUILD_COLOR (MAKE_RGB15 (0xB, 0xB, 0x1F), 0x09));
BUILD_COLOR (MAKE_RGB15 (0x0B, 0x0B, 0x1F), 0x09));
DisplayArray[p].Object.Stamp.frame = stars_in_space;
}
else
+9 -3
View File
@@ -26,7 +26,7 @@
#include "libs/threadlib.h"
battle_summary_func ComputerInput, HumanInput[NUM_PLAYERS];
battle_summary_func ComputerInput, HumanInput[NUM_PLAYERS], NetworkInput;
battle_summary_func PlayerInput[NUM_PLAYERS];
#define ACCELERATION_INCREMENT (ONE_SECOND / 12)
@@ -379,7 +379,7 @@ GetMenuSounds (MENU_SOUND_FLAGS *s0, MENU_SOUND_FLAGS *s1)
/* These can really be refactored */
BATTLE_INPUT_STATE
p1_combat_summary (void)
p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
{
BATTLE_INPUT_STATE InputState = 0;
if (CurrentInputState.key[PlayerOne][KEY_UP])
@@ -396,11 +396,14 @@ p1_combat_summary (void)
InputState |= BATTLE_ESCAPE;
if (CurrentInputState.key[PlayerOne][KEY_DOWN])
InputState |= BATTLE_DOWN;
(void) player;
(void) StarShipPtr;
return InputState;
}
BATTLE_INPUT_STATE
p2_combat_summary (void)
p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
{
BATTLE_INPUT_STATE InputState = 0;
if (CurrentInputState.key[PlayerTwo][KEY_UP])
@@ -415,6 +418,9 @@ p2_combat_summary (void)
InputState |= BATTLE_SPECIAL;
if (CurrentInputState.key[PlayerTwo][KEY_DOWN])
InputState |= BATTLE_DOWN;
(void) player;
(void) StarShipPtr;
return InputState;
}
+3 -1
View File
@@ -46,6 +46,7 @@
#define FLAGSHIP_STRING_COUNT 13
#define ORBITSCAN_STRING_COUNT 19
#define MAINMENU_STRING_COUNT 56
#define NETMELEE_STRING_COUNT 16
enum {
STAR_STRING_BASE = 0,
@@ -69,8 +70,9 @@ enum {
FLAGSHIP_STRING_BASE = STATUS_STRING_BASE + STATUS_STRING_COUNT,
ORBITSCAN_STRING_BASE = FLAGSHIP_STRING_BASE + FLAGSHIP_STRING_COUNT,
MAINMENU_STRING_BASE = ORBITSCAN_STRING_BASE + ORBITSCAN_STRING_COUNT,
NETMELEE_STRING_BASE = MAINMENU_STRING_BASE + MAINMENU_STRING_COUNT,
GAMESTR_COUNT = MAINMENU_STRING_BASE + MAINMENU_STRING_COUNT
GAMESTR_COUNT = NETMELEE_STRING_BASE + NETMELEE_STRING_COUNT
};
+9 -8
View File
@@ -29,27 +29,28 @@
SIZE cur_player;
BATTLE_INPUT_STATE
computer_intelligence (void)
computer_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
{
BATTLE_INPUT_STATE InputState;
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
return 0;
if (CyborgDescPtr)
if (StarShipPtr)
{
if (PlayerControl[cur_player] & CYBORG_CONTROL)
if (PlayerControl[player] & CYBORG_CONTROL)
{
InputState = tactical_intelligence ();
InputState = tactical_intelligence (player, StarShipPtr);
// allow a player to warp-escape in cyborg mode
if (cur_player == 0)
InputState |= (*(HumanInput[cur_player])) () & BATTLE_ESCAPE;
if (player == 0)
InputState |= (*(HumanInput[player])) (player, StarShipPtr)
& BATTLE_ESCAPE;
}
else
InputState = (*(HumanInput[cur_player])) ();
InputState = (*(HumanInput[player])) (player, StarShipPtr);
}
else if (!(PlayerControl[cur_player] & PSYTRON_CONTROL))
else if (!(PlayerControl[player] & PSYTRON_CONTROL))
InputState = 0;
else
{
+8 -5
View File
@@ -63,8 +63,10 @@ enum
extern STARSHIPPTR CyborgDescPtr;
extern SIZE cur_player;
extern BATTLE_INPUT_STATE computer_intelligence (void);
extern BATTLE_INPUT_STATE tactical_intelligence (void);
extern BATTLE_INPUT_STATE computer_intelligence (COUNT player,
STARSHIPPTR StarShipPtr);
extern BATTLE_INPUT_STATE tactical_intelligence (COUNT player,
STARSHIPPTR StarShipPtr);
extern void ship_intelligence (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR
ObjectsOfConcern, COUNT ConcernCounter);
extern BOOLEAN ship_weapons (ELEMENTPTR ShipPtr, ELEMENTPTR OtherPtr,
@@ -80,11 +82,12 @@ extern BOOLEAN ThrustShip (ELEMENTPTR ShipPtr, COUNT angle);
#define HUMAN_CONTROL (BYTE)(1 << 0)
#define CYBORG_CONTROL (BYTE)(1 << 1)
#define PSYTRON_CONTROL (BYTE)(1 << 2)
#define NETWORK_CONTROL (BYTE)(1 << 3)
#define COMPUTER_CONTROL (CYBORG_CONTROL | PSYTRON_CONTROL)
#define STANDARD_RATING (BYTE)(1 << 3)
#define GOOD_RATING (BYTE)(1 << 4)
#define AWESOME_RATING (BYTE)(1 << 5)
#define STANDARD_RATING (BYTE)(1 << 4)
#define GOOD_RATING (BYTE)(1 << 5)
#define AWESOME_RATING (BYTE)(1 << 6)
#endif /* _INTEL_H */
+1 -1
View File
@@ -1,5 +1,5 @@
uqm_SUBDIRS="callback decomp file graphics heap input list math memory
resource sound strings task threads time uio video log"
network resource sound strings task threads time uio video log"
if [ -n "$uqm_USE_INTERNAL_MIKMOD" ]; then
uqm_SUBDIRS="$uqm_SUBDIRS mikmod"
fi
+29
View File
@@ -0,0 +1,29 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NET_H
#define _NET_H
#include "network/network.h"
#include "network/netmanager/netmanager.h"
#include "network/connect/connect.h"
#include "network/connect/listen.h"
#include "network/connect/resolve.h"
#endif /* _NET_H */
+24
View File
@@ -0,0 +1,24 @@
In libs/network/:
netport.{c,h} Portability definitions.
bytesex.h Functions for endianness conversions.
network.h Functions for initialising the network subsystem.
network_bsd.c Network subsystem functions for BSD sockets.
network_win.c Network subsystem functions for Winsock sockets.
In libs/network/netmanager/:
ndesc.{c,h} Defines network descriptors.
netmanager.h Handles callbacks for network activity.
netmanager_bsd.{c,h} NetManager for systems with BSD sockets.
netmanager_win.{c,h} NetManager for Winsock systems.
In libs/network/socket/:
socket.{c,h} Platform-independant socket layer.
socket_bsd.{c,h} Socket operations for BSD sockets.
socket_win.{c,h} Socket operations for Winsock sockets.
In libs/network/connect/:
connect.{c,h} Routines for establishing outgoing connections.
listen.{c,h} Routines for receiving incoming connections.
resolve.{c,h} Routines for hostname lookups.
+3
View File
@@ -0,0 +1,3 @@
uqm_SUBDIRS="connect netmanager socket"
uqm_CFILES="netport.c network_bsd.c"
+96
View File
@@ -0,0 +1,96 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Routines for changing the endianness of values.
// I'm not using ntohs() etc. as those would require include files that may
// have conflicting definitions. This is a problem on Windows, where these
// functions are in winsock2.h, which includes windows.h, which includes
// pretty much Microsoft's complete collection of .h files.
#ifndef _BYTESEX_H
#define _BYTESEX_H
#include "port.h"
// for inline
#include "config.h"
// for WORDS_BIGENDIAN
#include "types.h"
static inline uint16
swapBytes16(uint16 x) {
return (x << 8) | (x >> 8);
}
static inline uint32
swapBytes32(uint32 x) {
return (x << 24)
| ((x & 0x0000ff00) << 8)
| ((x & 0x00ff0000) >> 8)
| (x >> 24);
}
#ifdef WORDS_BIGENDIAN
// Already in network order.
static inline uint16
hton16(uint16 x) {
return x;
}
static inline uint32
hton32(uint32 x) {
return x;
}
static inline uint16
ntoh16(uint16 x) {
return x;
}
static inline uint32
ntoh32(uint32 x) {
return x;
}
#else /* !defined(WORDS_BIGENDIAN) */
// Need to swap bytes
static inline uint16
hton16(uint16 x) {
return swapBytes16(x);
}
static inline uint32
hton32(uint32 x) {
return swapBytes32(x);
}
static inline uint16
ntoh16(uint16 x) {
return swapBytes16(x);
}
static inline uint32
ntoh32(uint32 x) {
return swapBytes32(x);
}
#endif /* defined(WORDS_BIGENDIAN) */
#endif /* _BYTESEX_H */
@@ -0,0 +1,2 @@
uqm_CFILES="connect.c listen.c resolve.c"
@@ -0,0 +1,475 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#define CONNECT_INTERNAL
#define SOCKET_INTERNAL
#include "connect.h"
#include "resolve.h"
#include "libs/alarm.h"
#include "../socket/socket.h"
#include "libs/misc.h"
#include "libs/log.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#ifdef _MSC_VER
# include <winsock2.h>
# include <ws2tcpip.h>
# include <wspiapi.h>
#else
# include <netdb.h>
#endif
#define DEBUG_CONNECT_REF
#ifdef DEBUG_CONNECT_REF
# include "types.h"
#endif
static void connectHostNext(ConnectState *connectState);
static void doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
const struct sockaddr *addr, socklen_t addrLen);
static void doConnectErrorCallback(ConnectState *connectState,
const ConnectError *error);
static ConnectState *
ConnectState_alloc(void) {
return (ConnectState *) malloc(sizeof (ConnectState));
};
static void
ConnectState_free(ConnectState *connectState) {
free(connectState);
}
static void
ConnectState_delete(ConnectState *connectState) {
assert(connectState->nd == NULL);
assert(connectState->alarm == NULL);
assert(connectState->info == NULL);
assert(connectState->infoPtr == NULL);
ConnectState_free(connectState);
}
void
ConnectState_incRef(ConnectState *connectState) {
assert(connectState->refCount < REFCOUNT_MAX);
connectState->refCount++;
#ifdef DEBUG_CONNECT_REF
log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref++ (%d)\n",
(uintptr_t) connectState, connectState->refCount);
#endif
}
bool
ConnectState_decRef(ConnectState *connectState) {
assert(connectState->refCount > 0);
connectState->refCount--;
#ifdef DEBUG_CONNECT_REF
log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref-- (%d)\n",
(uintptr_t) connectState, connectState->refCount);
#endif
if (connectState->refCount == 0) {
ConnectState_delete(connectState);
return true;
}
return false;
}
// decrements ref count by 1
void
ConnectState_close(ConnectState *connectState) {
if (connectState->resolveState != NULL) {
Resolve_close(connectState->resolveState);
connectState->resolveState = NULL;
}
if (connectState->alarm != NULL) {
Alarm_remove(connectState->alarm);
connectState->alarm = NULL;
}
if (connectState->nd != NULL) {
NetDescriptor_close(connectState->nd);
connectState->nd = NULL;
}
if (connectState->info != NULL) {
freeaddrinfo(connectState->info);
connectState->info = NULL;
connectState->infoPtr = NULL;
}
connectState->state = Connect_closed;
ConnectState_decRef(connectState);
}
void
ConnectState_setExtra(ConnectState *connectState, void *extra) {
connectState->extra = extra;
}
void *
ConnectState_getExtra(ConnectState *connectState) {
return connectState->extra;
}
// Try connecting to the next address.
static void
connectCallback(NetDescriptor *nd) {
// Called by the NetManager when a connection has been established.
ConnectState *connectState =
(ConnectState *) NetDescriptor_getExtra(nd);
int err;
if (connectState->alarm != NULL) {
Alarm_remove(connectState->alarm);
connectState->alarm = NULL;
}
if (connectState->state == Connect_closed) {
// The connection attempt has been aborted.
ConnectState_decRef(connectState);
return;
}
if (Socket_getError(NetDescriptor_getSocket(nd), &err) == -1) {
log_add(log_Fatal, "Socket_getError() failed: %s.\n",
strerror(errno));
explode();
}
if (err != 0) {
#ifdef DEBUG
log_add(log_Debug, "connect() failed: %s.\n", strerror(err));
#endif
NetDescriptor_close(nd);
connectState->nd = NULL;
connectState->infoPtr = connectState->infoPtr->ai_next;
connectHostNext(connectState);
return;
}
// Notify the higher layer.
connectState->nd = NULL;
// The callback function takes over ownership of the
// NetDescriptor.
NetDescriptor_setWriteCallback(nd, NULL);
// Note that connectState->info and connectState->infoPtr are cleaned up
// when ConnectState_close() is called by the callback function.
ConnectState_incRef(connectState);
doConnectCallback(connectState, nd, connectState->infoPtr->ai_addr,
connectState->infoPtr->ai_addrlen);
{
// The callback called should release the last reference to
// the connectState, by calling ConnectState_close().
bool released = ConnectState_decRef(connectState);
assert(released);
(void) released; // In case assert() evaluates to nothing.
}
}
static void
connectTimeoutCallback(ConnectState *connectState) {
connectState->alarm = NULL;
NetDescriptor_close(connectState->nd);
connectState->nd = NULL;
connectState->infoPtr = connectState->infoPtr->ai_next;
connectHostNext(connectState);
}
static void
setConnectTimeout(ConnectState *connectState) {
assert(connectState->alarm == NULL);
connectState->alarm =
Alarm_addRelativeMs(connectState->flags.timeout,
(AlarmCallback) connectTimeoutCallback, connectState);
}
static Socket *
tryConnectHostNext(ConnectState *connectState) {
struct addrinfo *info;
Socket *sock;
int connectResult;
assert(connectState->nd == NULL);
info = connectState->infoPtr;
sock = Socket_openNative(info->ai_family, info->ai_socktype,
info->ai_protocol);
if (sock == Socket_noSocket) {
int savedErrno = errno;
log_add(log_Error, "socket() failed: %s.\n", strerror(errno));
errno = savedErrno;
return Socket_noSocket;
}
if (Socket_setNonBlocking(sock) == -1) {
int savedErrno = errno;
log_add(log_Error, "Could not make socket non-blocking: %s.\n",
strerror(errno));
errno = savedErrno;
return Socket_noSocket;
}
(void) Socket_setReuseAddr(sock);
// Ignore errors; it's not a big deal.
(void) Socket_setInlineOOB(sock);
// Ignore errors; it's not a big deal as the other party is not
// not supposed to send any OOB data.
(void) Socket_setKeepAlive(sock);
// Ignore errors; it's not a big deal.
connectResult = Socket_connect(sock, info->ai_addr, info->ai_addrlen);
if (connectResult == 0) {
// Connection has already succeeded.
// We just wait for the writability callback anyhow, so that
// we can use one code path.
return sock;
}
switch (errno) {
case EINPROGRESS:
// Connection in progress; wait for the write callback.
return sock;
}
// Connection failed.
{
int savedErrno = errno;
Socket_close(sock);
errno = savedErrno;
}
return Socket_noSocket;
}
static void
connectRetryCallback(ConnectState *connectState) {
connectState->alarm = NULL;
connectState->infoPtr = connectState->info;
connectHostNext(connectState);
}
static void
setConnectRetryAlarm(ConnectState *connectState) {
assert(connectState->alarm == NULL);
assert(connectState->flags.retryDelayMs != Connect_noRetry);
connectState->alarm =
Alarm_addRelativeMs(connectState->flags.retryDelayMs,
(AlarmCallback) connectRetryCallback, connectState);
}
static void
connectHostReportAllFailed(ConnectState *connectState) {
// Could not connect to any host.
ConnectError error;
freeaddrinfo(connectState->info);
connectState->info = NULL;
connectState->infoPtr = NULL;
connectState->state = Connect_closed;
error.state = Connect_connecting;
error.err = ETIMEDOUT;
// No errno code is exactly suitable. We have been unable
// to connect to any host, but the reasons may vary
// (unreachable, refused, ...).
// ETIMEDOUT is the least specific portable errno code that
// seems appropriate.
doConnectErrorCallback(connectState, &error);
}
static void
connectHostNext(ConnectState *connectState) {
Socket *sock;
while (connectState->infoPtr != NULL) {
sock = tryConnectHostNext(connectState);
if (sock != Socket_noSocket) {
// Connection succeeded or connection in progress
connectState->nd =
NetDescriptor_new(sock, (void *) connectState);
if (connectState->nd == NULL) {
ConnectError error;
int savedErrno = errno;
log_add(log_Error, "NetDescriptor_new() failed: %s.\n",
strerror(errno));
Socket_close(sock);
freeaddrinfo(connectState->info);
connectState->info = NULL;
connectState->infoPtr = NULL;
connectState->state = Connect_closed;
error.state = Connect_connecting;
error.err = savedErrno;
doConnectErrorCallback(connectState, &error);
return;
}
NetDescriptor_setWriteCallback(connectState->nd, connectCallback);
setConnectTimeout(connectState);
return;
}
connectState->infoPtr = connectState->infoPtr->ai_next;
}
// Connect failed to all addresses.
if (connectState->flags.retryDelayMs == Connect_noRetry) {
connectHostReportAllFailed(connectState);
return;
}
setConnectRetryAlarm(connectState);
}
static void
connectHostResolveCallback(ResolveState *resolveState,
struct addrinfo *info) {
ConnectState *connectState =
(ConnectState *) ResolveState_getExtra(resolveState);
connectState->state = Connect_connecting;
Resolve_close(resolveState);
connectState->resolveState = NULL;
if (connectState->flags.familyPrefer != PF_UNSPEC) {
// Reorganise the 'info' list to put the structures of the
// prefered family in front.
struct addrinfo *preferred;
struct addrinfo **preferredEnd;
struct addrinfo *rest;
struct addrinfo **restEnd;
splitAddrInfoOnFamily(info, connectState->flags.familyPrefer,
&preferred, &preferredEnd, &rest, &restEnd);
info = preferred;
*preferredEnd = rest;
}
connectState->info = info;
connectState->infoPtr = info;
connectHostNext(connectState);
}
static void
connectHostResolveErrorCallback(ResolveState *resolveState,
const ResolveError *resolveError) {
ConnectState *connectState =
(ConnectState *) ResolveState_getExtra(resolveState);
ConnectError connectError;
assert(resolveError->gaiRes != 0);
Resolve_close(resolveState);
connectState->resolveState = NULL;
connectError.state = Connect_resolving;
connectError.resolveError = resolveError;
connectError.err = resolveError->err;
doConnectErrorCallback(connectState, &connectError);
}
ConnectState *
connectHostByName(const char *host, const char *service, Protocol proto,
const ConnectFlags *flags, ConnectConnectCallback connectCallback,
ConnectErrorCallback errorCallback, void *extra) {
struct addrinfo hints;
ConnectState *connectState;
ResolveFlags resolveFlags;
// Structure is empty (for now).
assert(flags->familyDemand == PF_inet ||
flags->familyDemand == PF_inet6 ||
flags->familyDemand == PF_unspec);
assert(flags->familyPrefer == PF_inet ||
flags->familyPrefer == PF_inet6 ||
flags->familyPrefer == PF_unspec);
assert(proto == IPProto_tcp || proto == IPProto_udp);
memset(&hints, '\0', sizeof hints);
hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
hints.ai_protocol = protocolTranslation[proto];
if (proto == IPProto_tcp) {
hints.ai_socktype = SOCK_STREAM;
} else {
assert(proto == IPProto_udp);
hints.ai_socktype = SOCK_DGRAM;
}
hints.ai_flags = 0;
connectState = ConnectState_alloc();
connectState->refCount = 1;
#ifdef DEBUG_CONNECT_REF
log_add(log_Debug, "ConnectState %08" PRIxPTR ": ref=1 (%d)\n",
(uintptr_t) connectState, connectState->refCount);
#endif
connectState->state = Connect_resolving;
connectState->flags = *flags;
connectState->connectCallback = connectCallback;
connectState->errorCallback = errorCallback;
connectState->extra = extra;
connectState->info = NULL;
connectState->infoPtr = NULL;
connectState->nd = NULL;
connectState->alarm = NULL;
connectState->resolveState = getaddrinfoAsync(
host, service, &hints, &resolveFlags,
(ResolveCallback) connectHostResolveCallback,
(ResolveErrorCallback) connectHostResolveErrorCallback,
(ResolveCallbackArg) connectState);
return connectState;
}
// NB: The callback function becomes the owner of nd
static void
doConnectCallback(ConnectState *connectState, NetDescriptor *nd,
const struct sockaddr *addr, socklen_t addrLen) {
assert(connectState->connectCallback != NULL);
ConnectState_incRef(connectState);
// No need to increment nd as the callback function takes over ownership.
(*connectState->connectCallback)(connectState, nd, addr, addrLen);
ConnectState_decRef(connectState);
}
static void
doConnectErrorCallback(ConnectState *connectState,
const ConnectError *error) {
assert(connectState->errorCallback != NULL);
ConnectState_incRef(connectState);
(*connectState->errorCallback)(connectState, error);
ConnectState_decRef(connectState);
}
@@ -0,0 +1,111 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CONNECT_CONNECT_H
#define _CONNECT_CONNECT_H
typedef struct ConnectFlags ConnectFlags;
typedef struct ConnectError ConnectError;
typedef struct ConnectState ConnectState;
typedef enum {
Connect_closed,
Connect_resolving,
Connect_connecting
} ConnectStateState;
#include "../netmanager/netmanager.h"
#include "../socket/socket.h"
#include "resolve.h"
// For connectHost()
struct ConnectFlags {
ProtocolFamily familyDemand;
// Only accept a protocol family of this type.
// One of PF_inet, PF_inet6, or PF_unspec.
ProtocolFamily familyPrefer;
// Prefer a protocol family of this type.
// One of PF_inet, PF_inet6, or PF_unspec.
int timeout;
/* Number of milliseconds before timing out a connection attempt.
* Note that if a host has multiple addresses, a connect to that
* host will have this timeout *per address*. */
int retryDelayMs;
/* Retry connecting this many ms after connecting to the last
* address for the specified host fails. Set to Connect_noRetry
* to give up after one try. */
#define Connect_noRetry -1
};
struct ConnectError {
ConnectStateState state;
// State where the error occured.
int err;
// errno value. Not relevant if state == resolving unless
// gaiRes == EAI_SYSTEM.
const ResolveError *resolveError;
// Only relevant if state == resolving.
};
typedef void (*ConnectConnectCallback)(ConnectState *connectState,
NetDescriptor *nd, const struct sockaddr *addr, socklen_t addrLen);
typedef void (*ConnectErrorCallback)(ConnectState *connectState,
const ConnectError *error);
#ifdef CONNECT_INTERNAL
#include "libs/alarm.h"
struct ConnectState {
RefCount refCount;
ConnectStateState state;
ConnectFlags flags;
ConnectConnectCallback connectCallback;
ConnectErrorCallback errorCallback;
void *extra;
struct addrinfo *info;
struct addrinfo *infoPtr;
ResolveState *resolveState;
NetDescriptor *nd;
Alarm *alarm;
// Used for both the timeout for a connection attempt
// and to retry after all addresses have been tried.
};
#endif /* CONNECT_INTERNAL */
ConnectState *connectHostByName(const char *host, const char *service,
Protocol proto, const ConnectFlags *flags,
ConnectConnectCallback connectCallback,
ConnectErrorCallback errorCallback, void *extra);
void ConnectState_incRef(ConnectState *connectState);
bool ConnectState_decRef(ConnectState *connectState);
void ConnectState_close(ConnectState *connectState);
void ConnectState_setExtra(ConnectState *connectState, void *extra);
void *ConnectState_getExtra(ConnectState *connectState);
#endif /* _CONNECT_CONNECT_H */
@@ -0,0 +1,458 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#include "../netport.h"
#define LISTEN_INTERNAL
#define SOCKET_INTERNAL
#include "listen.h"
#include "resolve.h"
#include "../socket/socket.h"
#include "../netmanager/netmanager.h"
#include "libs/misc.h"
#include "libs/log.h"
#include <assert.h>
#include <errno.h>
#ifdef _MSC_VER
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <netdb.h>
#endif
#ifdef DEBUG
# ifdef _MSC_VER
# include <wspiapi.h>
# endif
#endif
#include <stdlib.h>
#include <string.h>
#define DEBUG_LISTEN_REF
#ifdef DEBUG_LISTEN_REF
# include "types.h"
#endif
static void acceptCallback(NetDescriptor *nd);
static void doListenErrorCallback(ListenState *listenState,
const ListenError *error);
static ListenState *
ListenState_alloc(void) {
return (ListenState *) malloc(sizeof (ListenState));
};
static void
ListenState_free(ListenState *listenState) {
free(listenState);
}
static void
ListenState_delete(ListenState *listenState) {
assert(listenState->nds == NULL);
ListenState_free(listenState);
}
void
ListenState_incRef(ListenState *listenState) {
assert(listenState->refCount < REFCOUNT_MAX);
listenState->refCount++;
#ifdef DEBUG_LISTEN_REF
log_add(log_Debug, "ListenState %08" PRIxPTR ": ref++ (%d)\n",
(uintptr_t) listenState, listenState->refCount);
#endif
}
bool
ListenState_decRef(ListenState *listenState) {
assert(listenState->refCount > 0);
listenState->refCount--;
#ifdef DEBUG_LISTEN_REF
log_add(log_Debug, "ListenState %08" PRIxPTR ": ref-- (%d)\n",
(uintptr_t) listenState, listenState->refCount);
#endif
if (listenState->refCount == 0) {
ListenState_delete(listenState);
return true;
}
return false;
}
// Decrements ref count byh 1
void
ListenState_close(ListenState *listenState) {
if (listenState->resolveState != NULL) {
Resolve_close(listenState->resolveState);
listenState->resolveState = NULL;
}
if (listenState->nds != NULL) {
while (listenState->numNd > 0) {
listenState->numNd--;
NetDescriptor_close(listenState->nds[listenState->numNd]);
}
free(listenState->nds);
listenState->nds = NULL;
}
listenState->state = Listen_closed;
ListenState_decRef(listenState);
}
void
ListenState_setExtra(ListenState *listenState, void *extra) {
listenState->extra = extra;
}
void *
ListenState_getExtra(ListenState *listenState) {
return listenState->extra;
}
static NetDescriptor *
listenPortSingle(struct ListenState *listenState, struct addrinfo *info) {
Socket *sock;
int bindResult;
int listenResult;
NetDescriptor *nd;
sock = Socket_openNative(info->ai_family, info->ai_socktype,
info->ai_protocol);
if (sock == Socket_noSocket) {
int savedErrno = errno;
log_add(log_Error, "socket() failed: %s.\n", strerror(errno));
errno = savedErrno;
return NULL;
}
(void) Socket_setReuseAddr(sock);
// Ignore errors; it's not a big deal.
if (Socket_setNonBlocking(sock) == -1) {
int savedErrno = errno;
// Error message is already printed.
Socket_close(sock);
errno = savedErrno;
return NULL;
}
bindResult = Socket_bind(sock, info->ai_addr, info->ai_addrlen);
if (bindResult == -1) {
int savedErrno = errno;
if (errno == EADDRINUSE) {
#ifdef DEBUG
log_add(log_Warning, "bind() failed: %s.\n", strerror(errno));
#endif
} else
log_add(log_Error, "bind() failed: %s.\n", strerror(errno));
Socket_close(sock);
errno = savedErrno;
return NULL;
}
listenResult = Socket_listen(sock, listenState->flags.backlog);
if (listenResult == -1) {
int savedErrno = errno;
log_add(log_Error, "listen() failed: %s.\n", strerror(errno));
Socket_close(sock);
errno = savedErrno;
return NULL;
}
nd = NetDescriptor_new(sock, (void *) listenState);
if (nd == NULL) {
int savedErrno = errno;
log_add(log_Error, "NetDescriptor_new() failed: %s.\n",
strerror(errno));
Socket_close(sock);
errno = savedErrno;
return NULL;
}
NetDescriptor_setReadCallback(nd, acceptCallback);
return nd;
}
static void
listenPortMulti(struct ListenState *listenState, struct addrinfo *info) {
struct addrinfo *addrPtr;
size_t addrCount;
size_t addrOkCount;
NetDescriptor **nds;
// Count how many addresses we've got.
addrCount = 0;
for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next)
addrCount++;
// This is where we intend to store the file descriptors of the
// listening sockets.
nds = malloc(addrCount * sizeof listenState->nds[0]);
// Bind to each address.
addrOkCount = 0;
for (addrPtr = info; addrPtr != NULL; addrPtr = addrPtr->ai_next) {
NetDescriptor *nd;
nd = listenPortSingle(listenState, addrPtr);
if (nd == NULL) {
// Failed. On to the next.
// An error message is already printed for serious errors.
// If the address is already in use, we here also print
// a message when we are not already listening on one of
// the other addresses.
// This is because on some IPv6 capabable systems (like Linux),
// IPv6 sockets also handle IPv4 connections, which means
// that a separate IPv4 socket won't be able to bind to the
// port.
// BUG: if the IPv4 socket is in the list before the
// IPv6 socket, it will be the IPv6 which will fail to bind,
// so only IPv4 connections will be handled, as v4 sockets can't
// accept v6 connections.
// In practice, on Linux, I haven't seen it happen, but
// it's a real possibility.
if (errno == EADDRINUSE && addrOkCount == 0) {
log_add(log_Error, "Error while preparing a network socket "
"for incoming connections: %s\n", strerror(errno));
}
continue;
}
nds[addrOkCount] = nd;
addrOkCount++;
}
freeaddrinfo(info);
listenState->nds =
realloc(nds, addrOkCount * sizeof listenState->nds[0]);
listenState->numNd = addrOkCount;
if (addrOkCount == 0) {
// Could not listen on any port.
ListenError error;
error.state = Listen_listening;
error.err = EIO;
// Nothing better to offer.
doListenErrorCallback(listenState, &error);
return;
}
}
static void
listenPortResolveCallback(ResolveState *resolveState,
struct addrinfo *result) {
ListenState *listenState =
(ListenState *) ResolveState_getExtra(resolveState);
Resolve_close(listenState->resolveState);
listenState->resolveState = NULL;
listenState->state = Listen_listening;
listenPortMulti(listenState, result);
}
static void
listenPortResolveErrorCallback(ResolveState *resolveState,
const ResolveError *resolveError) {
ListenState *listenState =
(ListenState *) ResolveState_getExtra(resolveState);
ListenError listenError;
assert(resolveError->gaiRes != 0);
listenError.state = Listen_resolving;
listenError.resolveError = resolveError;
listenError.err = resolveError->err;
doListenErrorCallback(listenState, &listenError);
}
// 'proto' is one of IPProto_tcp or IPProto_udp.
ListenState *
listenPort(const char *service, Protocol proto, const ListenFlags *flags,
ListenConnectCallback connectCallback,
ListenErrorCallback errorCallback, void *extra) {
struct addrinfo hints;
ListenState *listenState;
ResolveFlags resolveFlags;
// Structure is empty (for now).
assert(flags->familyDemand == PF_inet ||
flags->familyDemand == PF_inet6 ||
flags->familyDemand == PF_unspec);
assert(flags->familyPrefer == PF_inet ||
flags->familyPrefer == PF_inet6 ||
flags->familyPrefer == PF_unspec);
assert(proto == IPProto_tcp || proto == IPProto_udp);
// Acquire a list of addresses to bind to.
memset(&hints, '\0', sizeof hints);
hints.ai_family = protocolFamilyTranslation[flags->familyDemand];
hints.ai_protocol = protocolTranslation[proto];
if (proto == IPProto_tcp) {
hints.ai_socktype = SOCK_STREAM;
} else {
assert(proto == IPProto_udp);
hints.ai_socktype = SOCK_DGRAM;
}
hints.ai_flags = AI_PASSIVE;
listenState = ListenState_alloc();
listenState->refCount = 1;
#ifdef DEBUG_LISTEN_REF
log_add(log_Debug, "ListenState %08" PRIxPTR ": ref=1 (%d)\n",
(uintptr_t) listenState, listenState->refCount);
#endif
listenState->state = Listen_resolving;
listenState->flags = *flags;
listenState->connectCallback = connectCallback;
listenState->errorCallback = errorCallback;
listenState->extra = extra;
listenState->nds = NULL;
listenState->numNd = 0;
listenState->resolveState = getaddrinfoAsync(NULL, service, &hints,
&resolveFlags, listenPortResolveCallback,
listenPortResolveErrorCallback,
(ResolveCallbackArg) listenState);
return listenState;
}
// NB: The callback function becomes the owner of newNd.
static void
doListenConnectCallback(ListenState *listenState, NetDescriptor *listenNd,
NetDescriptor *newNd,
const struct sockaddr *addr, SOCKLEN_T addrLen) {
assert(listenState->connectCallback != NULL);
ListenState_incRef(listenState);
// No need to increment listenNd, as there's guaranteed to be one
// reference from listenState. And no need to increment newNd,
// as the callback function takes over ownership.
(*listenState->connectCallback)(listenState, listenNd, newNd,
addr, (socklen_t) addrLen);
ListenState_decRef(listenState);
}
static void
doListenErrorCallback(ListenState *listenState, const ListenError *error) {
assert(listenState->errorCallback != NULL);
ListenState_incRef(listenState);
(*listenState->errorCallback)(listenState, error);
ListenState_decRef(listenState);
}
static void
acceptSingleConnection(ListenState *listenState, NetDescriptor *nd) {
Socket *sock;
Socket *acceptResult;
struct sockaddr_storage addr;
socklen_t addrLen;
NetDescriptor *newNd;
sock = NetDescriptor_getSocket(nd);
addrLen = sizeof (addr);
acceptResult = Socket_accept(sock, (struct sockaddr *) &addr, &addrLen);
if (acceptResult == Socket_noSocket) {
switch (errno) {
case EWOULDBLOCK:
case ECONNABORTED:
// Nothing serious. Keep listening.
return;
case EMFILE:
case ENFILE:
case ENOBUFS:
case ENOMEM:
case ENOSR:
// Serious problems, but future connections may still
// be possible.
log_add(log_Warning, "accept() reported '%s'\n",
strerror(errno));
return;
default:
// Should not happen.
log_add(log_Fatal, "Internal error: accept() reported "
"'%s'\n", strerror(errno));
explode();
}
}
(void) Socket_setReuseAddr(acceptResult);
// Ignore errors; it's not a big deal.
if (Socket_setNonBlocking(acceptResult) == -1) {
int savedErrno = errno;
log_add(log_Error, "Could not make socket non-blocking: %s.\n",
strerror(errno));
Socket_close(acceptResult);
errno = savedErrno;
return;
}
(void) Socket_setInlineOOB(acceptResult);
// Ignore errors; it's not a big deal as the other
// party is not not supposed to send any OOB data.
(void) Socket_setKeepAlive(sock);
// Ignore errors; it's not a big deal.
#ifdef DEBUG
{
char hostname[NI_MAXHOST];
int gniRes;
gniRes = getnameinfo((struct sockaddr *) &addr, addrLen,
hostname, sizeof hostname, NULL, 0, 0);
if (gniRes != 0) {
log_add(log_Error, "Error while performing hostname "
"lookup for incoming connection: %s\n",
(gniRes == EAI_SYSTEM) ? strerror(errno) :
gai_strerror(gniRes));
} else {
log_add(log_Debug, "Accepted incoming connection from '%s'.\n",
hostname);
}
}
#endif
newNd = NetDescriptor_new(acceptResult, NULL);
if (newNd == NULL) {
int savedErrno = errno;
log_add(log_Error, "NetDescriptor_new() failed: %s.\n",
strerror(errno));
Socket_close(acceptResult);
errno = savedErrno;
return;
}
doListenConnectCallback(listenState, nd, newNd,
(struct sockaddr *) &addr, addrLen);
// NB: newNd is now handed over to the callback function, and should
// no longer be referenced from here.
}
// Called when select() has indicated readability on a listening socket,
// i.e. when a connection is in the queue.
static void
acceptCallback(NetDescriptor *nd) {
ListenState *listenState = (ListenState *) NetDescriptor_getExtra(nd);
acceptSingleConnection(listenState, nd);
}
@@ -0,0 +1,104 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CONNECT_LISTEN_H
#define _CONNECT_LISTEN_H
typedef struct ListenFlags ListenFlags;
typedef enum {
Listen_closed,
Listen_resolving,
Listen_listening
} ListenStateState;
typedef struct ListenError ListenError;
typedef struct ListenState ListenState;
#ifdef _MSC_VER
// I do not want to include winsock2.h, because of possible conflicts with
// code that includes this file.
// Note that listen.c itself includes winsock2.h; SOCKLEN_T is used there
// only where necessary to keep the API consistent.
# define SOCKLEN_T size_t
struct sockaddr;
#else
# include <netinet/in.h>
# define SOCKLEN_T socklen_t
#endif
#include "resolve.h"
#include "../socket/socket.h"
#include "../netmanager/netmanager.h"
// For listenPort()
struct ListenFlags {
ProtocolFamily familyDemand;
// Only accept a protocol family of this type.
// One of PF_inet, PF_inet6, or PF_unspec.
ProtocolFamily familyPrefer;
// Prefer a protocol family of this type.
// One of PF_inet, PF_inet6, or PF_unspec.
int backlog;
// As the 2rd parameter to listen();
};
struct ListenError {
ListenStateState state;
// State where the error occured.
int err;
// errno value. Not relevant if state == resolving unless
// gaiRes == EAI_SYSTEM.
const ResolveError *resolveError;
// Only relevant if state == resolving.
};
typedef void (*ListenConnectCallback)(ListenState *listenState,
NetDescriptor *listenNd, NetDescriptor *newNd,
const struct sockaddr *addr, SOCKLEN_T addrLen);
typedef void (*ListenErrorCallback)(ListenState *listenState,
const ListenError *error);
#ifdef LISTEN_INTERNAL
struct ListenState {
RefCount refCount;
ListenStateState state;
ListenFlags flags;
ListenConnectCallback connectCallback;
ListenErrorCallback errorCallback;
void *extra;
ResolveState *resolveState;
NetDescriptor **nds;
size_t numNd;
// INV: (numNd == NULL) == (nds == NULL)
};
#endif /* defined(LISTEN_INTERNAL) */
ListenState *listenPort(const char *service, Protocol proto,
const ListenFlags *flags, ListenConnectCallback connectCallback,
ListenErrorCallback errorCallback, void *extra);
void ListenState_close(ListenState *listenState);
void ListenState_incRef(ListenState *listenState);
bool ListenState_decRef(ListenState *listenState);
void ListenState_setExtra(ListenState *listenState, void *extra);
void *ListenState_getExtra(ListenState *listenState);
#endif /* _CONNECT_LISTEN_H */
@@ -0,0 +1,211 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define RESOLVE_INTERNAL
#include "resolve.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#define DEBUG_RESOLVE_REF
#ifdef DEBUG_RESOLVE_REF
# include "types.h"
# include "libs/log.h"
# include <string.h>
#endif
static ResolveState *
ResolveState_new(void) {
return (ResolveState *) malloc(sizeof (ResolveState));
};
static void
ResolveState_free(ResolveState *resolveState) {
free(resolveState);
}
static void
ResolveState_delete(ResolveState *resolveState) {
assert(resolveState->callbackID == CallbackID_invalid);
ResolveState_free(resolveState);
}
void
ResolveState_incRef(ResolveState *resolveState) {
assert(resolveState->refCount < REFCOUNT_MAX);
resolveState->refCount++;
#ifdef DEBUG_RESOLVE_REF
log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref++ (%d)\n",
(uintptr_t) resolveState, resolveState->refCount);
#endif
}
bool
ResolveState_decRef(ResolveState *resolveState) {
assert(resolveState->refCount > 0);
resolveState->refCount--;
#ifdef DEBUG_RESOLVE_REF
log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref-- (%d)\n",
(uintptr_t) resolveState, resolveState->refCount);
#endif
if (resolveState->refCount == 0) {
ResolveState_delete(resolveState);
return true;
}
return false;
}
void
ResolveState_setExtra(ResolveState *resolveState, void *extra) {
resolveState->extra = extra;
}
void *
ResolveState_getExtra(ResolveState *resolveState) {
return resolveState->extra;
}
static void
doResolveCallback(ResolveState *resolveState) {
ResolveState_incRef(resolveState);
(*resolveState->callback)(resolveState, resolveState->result);
{
bool released = ResolveState_decRef(resolveState);
assert(released);
(void) released; // In case assert() evaluates to nothing.
}
}
static void
doResolveErrorCallback(ResolveState *resolveState) {
ResolveState_incRef(resolveState);
resolveState->errorCallback(resolveState, &resolveState->error);
{
bool released = ResolveState_decRef(resolveState);
assert(released);
(void) released; // In case assert() evaluates to nothing.
}
}
static void
resolveCallback(ResolveState *resolveState) {
resolveState->callbackID = CallbackID_invalid;
if (resolveState->error.gaiRes == 0) {
// Successful lookup.
doResolveCallback(resolveState);
} else {
// Lookup failed.
doResolveErrorCallback(resolveState);
}
}
// Function that does getaddrinfo() and calls the callback function when
// the result is available.
// TODO: actually make this function asynchronous. Right now it just calls
// getaddrinfo() (which blocks) and then schedules the callback.
ResolveState *
getaddrinfoAsync(const char *node, const char *service,
const struct addrinfo *hints, ResolveFlags *flags,
ResolveCallback callback, ResolveErrorCallback errorCallback,
ResolveCallbackArg extra) {
ResolveState *resolveState;
resolveState = ResolveState_new();
resolveState->refCount = 1;
#ifdef DEBUG_RESOLVE_REF
log_add(log_Debug, "ResolveState %08" PRIxPTR ": ref=1 (%d)\n",
(uintptr_t) resolveState, resolveState->refCount);
#endif
resolveState->state = Resolve_resolving;
resolveState->flags = *flags;
resolveState->callback = callback;
resolveState->errorCallback = errorCallback;
resolveState->extra = extra;
resolveState->result = NULL;
resolveState->error.gaiRes =
getaddrinfo(node, service, hints, &resolveState->result);
resolveState->error.err = errno;
resolveState->callbackID = Callback_add(
(CallbackFunction) resolveCallback, (CallbackArg) resolveState);
return resolveState;
}
void
Resolve_close(ResolveState *resolveState) {
if (resolveState->callbackID != CallbackID_invalid) {
Callback_removeCallback(resolveState->callbackID);
resolveState->callbackID = CallbackID_invalid;
}
resolveState->state = Resolve_closed;
ResolveState_decRef(resolveState);
}
// Split an addrinfo list into two separate lists, one with structures with
// the specified value for the ai_family field, the other with the other
// structures. The order of entries in the resulting lists will remain
// the same as in the original list.
// info - the original list
// family - the family for the first list
// selected - pointer to where the list of selected structures should be
// stored
// selectedEnd - pointer to where the end of 'selected' should be stored
// rest - pointer to where the list of not-selected structures should be
// stored
// restEnd - pointer to where the end of 'rest' should be stored
//
// Yes, it is allowed to modify the ai_next field of addrinfo structures.
// Or at least, it's not disallowed by RFC 3493, and the following
// text from that RFC seems to suggest it should be possible:
// ] The freeaddrinfo() function must support the freeing of arbitrary
// ] sublists of an addrinfo list originally returned by getaddrinfo().
void
splitAddrInfoOnFamily(struct addrinfo *info, int family,
struct addrinfo **selected, struct addrinfo ***selectedEnd,
struct addrinfo **rest, struct addrinfo ***restEnd) {
struct addrinfo *selectedFirst;
struct addrinfo **selectedNext;
struct addrinfo *restFirst;
struct addrinfo **restNext;
selectedNext = &selectedFirst;
restNext = &restFirst;
while (info != NULL) {
if (info->ai_family == family) {
*selectedNext = info;
selectedNext = &(*selectedNext)->ai_next;
} else {
*restNext = info;
restNext = &(*restNext)->ai_next;
}
info = info->ai_next;
}
*selectedNext = NULL;
*restNext = NULL;
// Fill in the result parameters.
*selected = selectedFirst;
*selectedEnd = selectedNext;
*rest = restFirst;
*restEnd = restNext;
}
@@ -0,0 +1,107 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CONNECT_RESOLVE_H
#define _CONNECT_RESOLVE_H
typedef struct ResolveFlags ResolveFlags;
typedef struct ResolveError ResolveError;
typedef enum {
Resolve_closed,
Resolve_resolving
} ResolveStateState;
typedef struct ResolveState ResolveState;
// For addrinfo.
#ifdef _MSC_VER
// Not including <winsock2.h> because of possible conflicts with files
// including this file.
struct addrinfo;
#else
# include <netdb.h>
#endif
#include "libs/callback.h"
#include "../netmanager/netmanager.h"
struct ResolveFlags {
// Nothing yet.
int dummy; // empty struct declarations are not allowed by C'99.
};
struct ResolveError {
int gaiRes;
int err;
// errno value. Only relevant if gaiRes == EAI_SYSTEM.
};
typedef void *ResolveCallbackArg;
typedef void (*ResolveCallback)(ResolveState *resolveState,
struct addrinfo *result);
// The receiver of the callback is owner of 'result' and
// should call freeaddrinfo().
typedef void (*ResolveErrorCallback)(ResolveState *resolveState,
const ResolveError *error);
#ifdef RESOLVE_INTERNAL
#ifdef _MSC_VER
# include <winsock2.h>
# include <ws2tcpip.h>
# include <wspiapi.h>
#else /* !defined(_MSC_VER) */
# include <netdb.h>
#endif /* !defined(_MSC_VER) */
struct ResolveState {
RefCount refCount;
ResolveStateState state;
ResolveFlags flags;
ResolveCallback callback;
ResolveErrorCallback errorCallback;
void *extra;
CallbackID callbackID;
ResolveError error;
struct addrinfo *result;
};
#endif /* RESOLVE_INTERNAL */
void ResolveState_incRef(ResolveState *resolveState);
bool ResolveState_decRef(ResolveState *resolveState);
void ResolveState_setExtra(ResolveState *resolveState, void *extra);
void *ResolveState_getExtra(ResolveState *resolveState);
ResolveState *getaddrinfoAsync(const char *node, const char *service,
const struct addrinfo *hints, ResolveFlags *flags,
ResolveCallback callback, ResolveErrorCallback errorCallback,
ResolveCallbackArg extra);
void Resolve_close(ResolveState *resolveState);
void splitAddrInfoOnFamily(struct addrinfo *info, int family,
struct addrinfo **selected, struct addrinfo ***selectedEnd,
struct addrinfo **rest, struct addrinfo ***restEnd);
#endif /* _CONNECT_RESOLVE_H */
@@ -0,0 +1,2 @@
uqm_CFILES="ndesc.c netmanager_bsd.c"
@@ -0,0 +1,202 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define NETDESCRIPTOR_INTERNAL
#include "ndesc.h"
#include "netmanager.h"
#include "libs/callback.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#undef DEBUG_NETDESCRIPTOR_REF
#ifdef DEBUG_NETDESCRIPTOR_REF
# include "libs/log.h"
# include <inttypes.h>
#endif
static NetDescriptor *
NetDescriptor_alloc(void) {
return malloc(sizeof (NetDescriptor));
}
static void
NetDescriptor_free(NetDescriptor *nd) {
free(nd);
}
// Sets the ref count to 1.
NetDescriptor *
NetDescriptor_new(Socket *socket, void *extra) {
NetDescriptor *nd;
nd = NetDescriptor_alloc();
nd->refCount = 1;
#ifdef DEBUG_NETDESCRIPTOR_REF
log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref=1 (%d)\n",
(uintptr_t) nd, nd->refCount);
#endif
nd->flags.closed = false;
nd->readCallback = NULL;
nd->writeCallback = NULL;
nd->exceptionCallback = NULL;
nd->closeCallback = NULL;
nd->socket = socket;
nd->smd = NULL;
nd->extra = extra;
if (NetManager_addDesc(nd) == -1) {
int savedErrno = errno;
NetDescriptor_free(nd);
errno = savedErrno;
return NULL;
}
return nd;
}
static void
NetDescriptor_delete(NetDescriptor *nd) {
assert(nd->socket == Socket_noSocket);
assert(nd->smd == NULL);
NetDescriptor_free(nd);
}
// Called from the callback handler.
static void
NetDescriptor_closeCallback(NetDescriptor *nd) {
if (nd->closeCallback != NULL) {
// The check is necessary because the close callback may have
// been removed before it is triggered.
(*nd->closeCallback)(nd);
}
NetDescriptor_decRef(nd);
}
void
NetDescriptor_close(NetDescriptor *nd) {
assert(!nd->flags.closed);
assert(nd->socket != Socket_noSocket);
NetManager_removeDesc(nd);
(void) Socket_close(nd->socket);
nd->socket = Socket_noSocket;
nd->flags.closed = true;
if (nd->closeCallback != NULL) {
// Keep one reference around until the close callback has been
// called.
(void) Callback_add(
(CallbackFunction) NetDescriptor_closeCallback,
(CallbackArg) nd);
} else
NetDescriptor_decRef(nd);
}
void
NetDescriptor_incRef(NetDescriptor *nd) {
assert(nd->refCount < REFCOUNT_MAX);
nd->refCount++;
#ifdef DEBUG_NETDESCRIPTOR_REF
log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref++ (%d)\n",
(uintptr_t) nd, nd->refCount);
#endif
}
// returns true iff the ref counter has reached 0.
bool
NetDescriptor_decRef(NetDescriptor *nd) {
assert(nd->refCount > 0);
nd->refCount--;
#ifdef DEBUG_NETDESCRIPTOR_REF
log_add(log_Debug, "NetDescriptor %08" PRIxPTR ": ref-- (%d)\n",
(uintptr_t) nd, nd->refCount);
#endif
if (nd->refCount == 0) {
NetDescriptor_delete(nd);
return true;
}
return false;
}
Socket *
NetDescriptor_getSocket(NetDescriptor *nd) {
return nd->socket;
}
void
NetDescriptor_setExtra(NetDescriptor *nd, void *extra) {
nd->extra = extra;
}
void *
NetDescriptor_getExtra(const NetDescriptor *nd) {
return nd->extra;
}
void
NetDescriptor_setReadCallback(NetDescriptor *nd,
NetDescriptor_ReadCallback callback) {
nd->readCallback = callback;
if (!nd->flags.closed) {
if (nd->readCallback != NULL) {
NetManager_activateReadCallback(nd);
} else
NetManager_deactivateReadCallback(nd);
}
}
void
NetDescriptor_setWriteCallback(NetDescriptor *nd,
NetDescriptor_WriteCallback callback) {
nd->writeCallback = callback;
if (!nd->flags.closed) {
if (nd->writeCallback != NULL) {
NetManager_activateWriteCallback(nd);
} else
NetManager_deactivateWriteCallback(nd);
}
}
void
NetDescriptor_setExceptionCallback(NetDescriptor *nd,
NetDescriptor_ExceptionCallback callback) {
nd->exceptionCallback = callback;
if (!nd->flags.closed) {
if (nd->exceptionCallback != NULL) {
NetManager_activateExceptionCallback(nd);
} else
NetManager_deactivateExceptionCallback(nd);
}
}
// The close callback is called as a result of a socket being closed, either
// because of a local command or a remote disconnect.
// The close callback will only be scheduled when this happens. The
// callback will not be called until the Callback_process() is called.
void
NetDescriptor_setCloseCallback(NetDescriptor *nd,
NetDescriptor_CloseCallback callback) {
nd->closeCallback = callback;
}
@@ -0,0 +1,81 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NDESC_H
#define _NDESC_H
#include "types.h"
typedef struct NetDescriptor NetDescriptor;
typedef void (*NetDescriptor_ReadCallback)(NetDescriptor *nd);
typedef void (*NetDescriptor_WriteCallback)(NetDescriptor *nd);
typedef void (*NetDescriptor_ExceptionCallback)(NetDescriptor *nd);
typedef void (*NetDescriptor_CloseCallback)(NetDescriptor *nd);
typedef uint32 RefCount;
#define REFCOUNT_MAX UINT32_MAX
#include "../socket/socket.h"
#include "netmanager.h"
#ifdef NETDESCRIPTOR_INTERNAL
// All created NetDescriptors are registered to the NetManager.
// They are unregisted when the NetDescriptor is closed.
// On creation the ref count is set to 1. On close it is decremented by 1.
struct NetDescriptor {
struct {
bool closed: 1;
} flags;
RefCount refCount;
NetDescriptor_ReadCallback readCallback;
NetDescriptor_WriteCallback writeCallback;
NetDescriptor_ExceptionCallback exceptionCallback;
NetDescriptor_CloseCallback closeCallback;
Socket *socket;
SocketManagementData *smd;
// Extra state-dependant information for the user.
void *extra;
};
#endif
NetDescriptor *NetDescriptor_new(Socket *socket, void *extra);
void NetDescriptor_close(NetDescriptor *nd);
void NetDescriptor_incRef(NetDescriptor *nd);
bool NetDescriptor_decRef(NetDescriptor *nd);
Socket *NetDescriptor_getSocket(NetDescriptor *nd);
void NetDescriptor_setExtra(NetDescriptor *nd, void *extra);
void *NetDescriptor_getExtra(const NetDescriptor *nd);
void NetDescriptor_setReadCallback(NetDescriptor *nd,
NetDescriptor_ReadCallback callback);
void NetDescriptor_setWriteCallback(NetDescriptor *nd,
NetDescriptor_WriteCallback callback);
void NetDescriptor_setExceptionCallback(NetDescriptor *nd,
NetDescriptor_ExceptionCallback callback);
void NetDescriptor_setCloseCallback(NetDescriptor *nd,
NetDescriptor_CloseCallback callback);
#endif /* _NDESC_H */
@@ -0,0 +1,103 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// This file is part of netmanager_bsd.c, from where it is #included.
// Only used for BSD sockets.
// This file provides a mapping of Sockets to NetDescriptors.
static NetDescriptor *netDescriptors[FD_SETSIZE];
// INV: flags.closed is not set for entries in netDescriptors.
static size_t maxND;
// One past the largest used ND in netDescriptors, as used in
// the first argument of select();
static inline void
NDIndex_init(void) {
size_t i;
size_t numND = sizeof (netDescriptors) / sizeof (netDescriptors[0]);
for (i = 0; i < numND; i++)
netDescriptors[i] = NULL;
}
static inline void
NDIndex_uninit(void) {
// Nothing to do.
}
static inline int
NDIndex_registerNDWithSocket(Socket *sock, NetDescriptor *nd) {
if (sock->fd >= FD_SETSIZE) {
errno = EMFILE;
return -1;
}
netDescriptors[sock->fd] = nd;
if ((size_t) sock->fd >= maxND)
maxND = (size_t) sock->fd + 1;
return 0;
}
static inline void
NDIndex_unregisterNDForSocket(Socket *sock) {
NetDescriptor **last;
netDescriptors[sock->fd] = NULL;
last = &netDescriptors[sock->fd];
if ((size_t) sock->fd + 1 == maxND) {
do {
maxND--;
if (last == &netDescriptors[0])
break;
last--;
} while (*last == NULL);
}
}
static inline NetDescriptor *
NDIndex_getNDForSocket(Socket *sock) {
assert((size_t) sock->fd < maxND);
return netDescriptors[sock->fd];
}
static inline NetDescriptor *
NDIndex_getNDForSocketFd(int fd) {
assert((size_t) fd < maxND);
return netDescriptors[fd];
}
static inline bool
NDIndex_socketRegistered(Socket *sock) {
return ((size_t) sock->fd < maxND)
&& (NDIndex_getNDForSocket(sock) != NULL);
}
// Get the first argument to be used in select().
static inline size_t
NDIndex_getSelectNumND(void) {
return maxND;
}
@@ -0,0 +1,47 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETMANAGER_H
#define _NETMANAGER_H
#include "types.h"
#ifdef WIN32
# include "netmanager_win.h"
#else
# include "netmanager_bsd.h"
#endif
#include "ndesc.h"
void NetManager_init(void);
void NetManager_uninit(void);
int NetManager_process(uint32 *timeoutMs);
// Only for internal use by the NetManager:
int NetManager_addDesc(NetDescriptor *nd);
void NetManager_removeDesc(NetDescriptor *nd);
void NetManager_activateReadCallback(NetDescriptor *nd);
void NetManager_deactivateReadCallback(NetDescriptor *nd);
void NetManager_activateWriteCallback(NetDescriptor *nd);
void NetManager_deactivateWriteCallback(NetDescriptor *nd);
void NetManager_activateExceptionCallback(NetDescriptor *nd);
void NetManager_deactivateExceptionCallback(NetDescriptor *nd);
#endif /* _NETMANAGER_H */
@@ -0,0 +1,223 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define SOCKET_INTERNAL
#define NETDESCRIPTOR_INTERNAL
#include "netmanager_bsd.h"
#include "ndesc.h"
#include "../socket/socket.h"
#include "ndesc.h"
#include "types.h"
#include "libs/log.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "netmanager_common.ci"
#include "ndindex.ci"
// INV: The following sets only contain sockets present in the netDescriptor
// array.
static fd_set readSet;
static fd_set writeSet;
static fd_set exceptionSet;
void
NetManager_init(void) {
NDIndex_init();
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&exceptionSet);
}
void
NetManager_uninit(void) {
NDIndex_uninit();
}
// Register the NetDescriptor with the NetManager.
int
NetManager_addDesc(NetDescriptor *nd) {
int fd;
assert(nd->socket != Socket_noSocket);
assert(!NDIndex_socketRegistered(nd->socket));
if (NDIndex_registerNDWithSocket(nd->socket, nd) == -1) {
// errno is set
return -1;
}
fd = nd->socket->fd;
if (nd->readCallback != NULL)
FD_SET(fd, &readSet);
if (nd->writeCallback != NULL)
FD_SET(fd, &writeSet);
if (nd->exceptionCallback != NULL)
FD_SET(fd, &exceptionSet);
return 0;
}
void
NetManager_removeDesc(NetDescriptor *nd) {
int fd;
assert(nd->socket != Socket_noSocket);
assert(NDIndex_getNDForSocket(nd->socket) == nd);
fd = nd->socket->fd;
FD_CLR(fd, &readSet);
FD_CLR(fd, &writeSet);
FD_CLR(fd, &exceptionSet);
NDIndex_unregisterNDForSocket(nd->socket);
}
void
NetManager_activateReadCallback(NetDescriptor *nd) {
FD_SET(nd->socket->fd, &readSet);
}
void
NetManager_deactivateReadCallback(NetDescriptor *nd) {
FD_CLR(nd->socket->fd, &readSet);
}
void
NetManager_activateWriteCallback(NetDescriptor *nd) {
FD_SET(nd->socket->fd, &writeSet);
}
void
NetManager_deactivateWriteCallback(NetDescriptor *nd) {
FD_CLR(nd->socket->fd, &writeSet);
}
void
NetManager_activateExceptionCallback(NetDescriptor *nd) {
FD_SET(nd->socket->fd, &exceptionSet);
}
void
NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
FD_CLR(nd->socket->fd, &exceptionSet);
}
// This function may be called again from inside a callback function
// triggered by this function. BUG: This may result in callbacks being
// called multiple times.
// This function should however not be called from multiple threads at once.
int
NetManager_process(uint32 *timeoutMs) {
struct timeval timeout;
size_t i;
int selectResult;
fd_set newReadSet;
fd_set newWriteSet;
fd_set newExceptionSet;
bool bitSet;
timeout.tv_sec = *timeoutMs / 1000;
timeout.tv_usec = (*timeoutMs % 1000) * 1000;
// Structure assignment:
newReadSet = readSet;
newWriteSet = writeSet;
newExceptionSet = exceptionSet;
do {
selectResult = select(NDIndex_getSelectNumND(),
&newReadSet, &newWriteSet, &newExceptionSet, &timeout);
// BUG: If select() is restarted because of EINTR, the timeout
// may start over. (Linux changes 'timeout' to the time left,
// but most other platforms don't.)
} while (selectResult == -1 && errno == EINTR);
if (selectResult == -1) {
int savedErrno = errno;
log_add(log_Error, "select() failed: %s.\n", strerror(errno));
errno = savedErrno;
*timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
// XXX: rounding microseconds down. Is that the correct
// thing to do?
return -1;
}
for (i = 0; i < maxND; i++) {
NetDescriptor *nd;
if (selectResult == 0) {
// No more bits set in the fd_sets
break;
}
nd = NDIndex_getNDForSocketFd(i);
if (nd == NULL)
continue;
bitSet = false;
// Is one of the bits in the fd_sets set?
// A callback may cause a NetDescriptor to be closed. The deletion
// of the structure will be scheduled, but will still be
// available at least until this function returns.
if (FD_ISSET(i, &newExceptionSet))
{
bool closed;
bitSet = true;
closed = NetManager_doExceptionCallback(nd);
if (closed)
goto next;
}
if (FD_ISSET(i, &newWriteSet))
{
bool closed;
bitSet = true;
closed = NetManager_doWriteCallback(nd);
if (closed)
goto next;
}
if (FD_ISSET(i, &newReadSet))
{
bool closed;
bitSet = true;
closed = NetManager_doReadCallback(nd);
if (closed)
goto next;
}
next:
if (bitSet)
selectResult--;
}
*timeoutMs = (timeout.tv_sec * 1000) + (timeout.tv_usec / 1000);
// XXX: rounding microseconds down. Is that the correct
// thing to do?
return 0;
}
@@ -0,0 +1,26 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETMANAGER_BSD_H
#define _NETMANAGER_BSD_H
typedef struct SocketManagementDataBsd SocketManagementDataBsd;
typedef SocketManagementDataBsd SocketManagementData;
#endif /* _NETMANAGER_BSD_H */
@@ -0,0 +1,58 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// This file is part of netmanager_bsd.ci and netmanager_win.ci,
// from where it is #included.
static bool
NetManager_doReadCallback(NetDescriptor *nd) {
bool closed;
assert(nd->readCallback != NULL);
NetDescriptor_incRef(nd);
(*nd->readCallback)(nd);
closed = nd->flags.closed;
NetDescriptor_decRef(nd);
return closed;
}
static bool
NetManager_doWriteCallback(NetDescriptor *nd) {
bool closed;
assert(nd->writeCallback != NULL);
NetDescriptor_incRef(nd);
(*nd->writeCallback)(nd);
closed = nd->flags.closed;
NetDescriptor_decRef(nd);
return closed;
}
static bool
NetManager_doExceptionCallback(NetDescriptor *nd) {
bool closed;
assert(nd->exceptionCallback != NULL);
NetDescriptor_incRef(nd);
(*nd->exceptionCallback)(nd);
closed = nd->flags.closed;
NetDescriptor_decRef(nd);
return closed;
}
@@ -0,0 +1,460 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#include "../netport.h"
#define NETMANAGER_INTERNAL
#define NETDESCRIPTOR_INTERNAL
#define SOCKET_INTERNAL
#include "netmanager_win.h"
#include "../socket/socket.h"
#include "ndesc.h"
#include "types.h"
#include "libs/misc.h"
#include "libs/log.h"
#include <assert.h>
#include <winsock2.h>
#include "netmanager_common.ci"
int closeWSAEvent(WSAEVENT event);
// The elements of the following arrays with the same index belong to
// eachother.
#define MAX_SOCKETS WSA_MAXIMUM_WAIT_EVENTS
// We cannot have more sockets than we can have events, as
// all may need an event at some point.
static NetDescriptor *netDescriptors[MAX_SOCKETS];
static WSAEVENT events[WSA_MAXIMUM_WAIT_EVENTS];
static size_t numSockets;
static size_t numActiveEvents;
// For each NetDescriptor registered with the NetManager, an event
// is created. Only the first numActiveEvents will be processed though.
// Inv: numActiveEvents <= numSockets
// Inv: for all i: 0 <= i < numActiveEvents: events[i] is active
// (events[i] being active also means netDescriptor[i]->smd->eventMask
// != 0)
// Inv: for all i: 0 <= i < numSockets: netDescriptor[i]->smd->index == i
void
NetManager_init(void) {
numActiveEvents = 0;
}
void
NetManager_uninit(void) {
assert(numActiveEvents == 0);
}
static inline SocketManagementDataWin *
SocketManagementData_alloc(void) {
return malloc(sizeof (SocketManagementDataWin));
}
static inline void
SocketManagementData_free(SocketManagementDataWin *smd) {
free(smd);
}
// XXX: This function should be moved to some file with generic network
// functions.
int
closeWSAEvent(WSAEVENT event) {
for (;;) {
int error;
if (WSACloseEvent(event))
break;
error = WSAGetLastError();
if (error != WSAEINPROGRESS) {
log_add(log_Error,
"WSACloseEvent() failed with error code %d.\n", error);
errno = winsockErrorToErrno(error);
return -1;
}
}
return 0;
}
// Register the NetDescriptor with the NetManager.
int
NetManager_addDesc(NetDescriptor *nd) {
long eventMask = 0;
WSAEVENT event;
if (numSockets >= WSA_MAXIMUM_WAIT_EVENTS) {
errno = EMFILE;
return -1;
}
if (nd->readCallback != NULL)
eventMask |= FD_READ | FD_ACCEPT;
if (nd->writeCallback != NULL)
eventMask |= FD_WRITE | FD_CONNECT;
if (nd->exceptionCallback != NULL)
eventMask |= FD_OOB;
eventMask |= FD_CLOSE;
event = WSACreateEvent();
if (event == WSA_INVALID_EVENT) {
errno = getWinsockErrno();
return -1;
}
nd->smd = SocketManagementData_alloc();
if (eventMask != 0) {
// XXX: This guard is now always true, because of FD_CLOSE.
// This means that numActiveEvents will always be equal to
// numEvents.
// Once I'm convinced this is the right way to go,
// I can remove some unnecessary code.
if (WSAEventSelect(nd->socket->sock, event, eventMask) ==
SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
int closeStatus = closeWSAEvent(event);
if (closeStatus == -1) {
log_add(log_Fatal, "closeWSAEvent() failed: %s.\n",
strerror(errno));
explode();
}
SocketManagementData_free(nd->smd);
errno = savedErrno;
return -1;
}
// Move existing socket for which there exists no event, so
// so that all sockets for which there exists an event are at
// the front of the array of netdescriptors.
if (numActiveEvents < numSockets) {
netDescriptors[numSockets] = netDescriptors[numActiveEvents];
netDescriptors[numSockets]->smd->index = numSockets;
}
nd->smd->index = numActiveEvents;
numActiveEvents++;
} else {
nd->smd->index = numSockets;
}
nd->smd->eventMask = eventMask;
netDescriptors[nd->smd->index] = nd;
events[nd->smd->index] = event;
numSockets++;
return 0;
}
void
NetManager_removeDesc(NetDescriptor *nd) {
assert(nd->smd != NULL);
assert(nd->smd->index < numSockets);
assert(nd == netDescriptors[nd->smd->index]);
{
int closeStatus = closeWSAEvent(events[nd->smd->index]);
if (closeStatus == -1)
explode();
}
if (nd->smd->index < numActiveEvents) {
size_t index = nd->smd->index;
if (index + 1 != numActiveEvents) {
// Keep the list of active events consecutive by filling
// the new hole with the last active event.
events[index] = events[numActiveEvents - 1];
netDescriptors[index] = netDescriptors[numActiveEvents - 1];
netDescriptors[index]->smd->index = index;
}
numActiveEvents--;
}
SocketManagementData_free(nd->smd);
nd->smd = NULL;
numSockets--;
}
static void
swapSockets(int index1, int index2) {
NetDescriptor *tempNd;
WSAEVENT tempEvent;
tempNd = netDescriptors[index2];
tempEvent = events[index2];
netDescriptors[index2] = netDescriptors[index1];
events[index2] = events[index1];
netDescriptors[index2]->smd->index = index2;
netDescriptors[index1] = tempNd;
events[index1] = tempEvent;
netDescriptors[index1]->smd->index = index1;
}
static int
NetManager_updateEvent(NetDescriptor *nd) {
assert(nd == netDescriptors[nd->smd->index]);
if (WSAEventSelect(nd->socket->sock,
events[nd->smd->index], nd->smd->eventMask) == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
int closeStatus = closeWSAEvent(events[nd->smd->index]);
if (closeStatus == -1) {
log_add(log_Fatal, "closeWSAEvent() failed: %s.\n",
strerror(errno));
explode();
}
errno = savedErrno;
return -1;
}
if (nd->smd->eventMask != 0) {
// There are some events that we are interested in.
if (nd->smd->index >= numActiveEvents) {
// Event was not yet active.
if (nd->smd->index != numActiveEvents) {
// Need to keep the active nds and events in the front of
// their arrays.
swapSockets(nd->smd->index, numActiveEvents);
}
numActiveEvents++;
}
} else {
// There are no events that we are interested in.
if (nd->smd->index < numActiveEvents) {
// Event was active.
if (nd->smd->index != numActiveEvents - 1) {
// Need to keep the active nds and events in the front of
// their arrays.
swapSockets(nd->smd->index, numActiveEvents - 1);
}
}
numActiveEvents--;
}
return 0;
}
static void
activateSomeCallback(NetDescriptor *nd, long eventMask) {
nd->smd->eventMask |= eventMask;
{
int status = NetManager_updateEvent(nd);
if (status == -1) {
log_add(log_Fatal, "NetManager_updateEvent() failed: %s.\n",
strerror(errno));
explode();
// TODO: better error handling.
}
}
}
static void
deactivateSomeCallback(NetDescriptor *nd, long eventMask) {
nd->smd->eventMask &= ~eventMask;
{
int status = NetManager_updateEvent(nd);
if (status == -1) {
log_add(log_Fatal, "NetManager_updateEvent() failed: %s.\n",
strerror(errno));
explode();
// TODO: better error handling
}
}
}
void
NetManager_activateReadCallback(NetDescriptor *nd) {
activateSomeCallback(nd, FD_READ | FD_ACCEPT);
}
void
NetManager_deactivateReadCallback(NetDescriptor *nd) {
deactivateSomeCallback(nd, FD_READ | FD_ACCEPT);
}
void
NetManager_activateWriteCallback(NetDescriptor *nd) {
activateSomeCallback(nd, FD_WRITE | FD_CONNECT);
}
void
NetManager_deactivateWriteCallback(NetDescriptor *nd) {
deactivateSomeCallback(nd, FD_WRITE | FD_CONNECT);
}
void
NetManager_activateExceptionCallback(NetDescriptor *nd) {
activateSomeCallback(nd, FD_OOB);
}
void
NetManager_deactivateExceptionCallback(NetDescriptor *nd) {
deactivateSomeCallback(nd, FD_OOB);
}
static inline int
NetManager_processEvent(size_t index) {
WSANETWORKEVENTS networkEvents;
int enumRes;
enumRes = WSAEnumNetworkEvents(netDescriptors[index]->socket->sock,
events[index], &networkEvents);
if (enumRes == SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
if (networkEvents.lNetworkEvents & FD_READ) {
bool closed;
if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
// No special handling is required; the callback function
// will try to do a recv() and will get the error then.
}
closed = NetManager_doReadCallback(netDescriptors[index]);
if (closed)
goto closed;
}
if (networkEvents.lNetworkEvents & FD_WRITE) {
bool closed;
if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
// No special handling is required; the callback function
// will try to do a send() and will get the error then.
}
closed = NetManager_doWriteCallback(netDescriptors[index]);
if (closed)
goto closed;
}
if (networkEvents.lNetworkEvents & FD_OOB) {
bool closed;
if (networkEvents.iErrorCode[FD_OOB_BIT] != 0) {
// No special handling is required; the callback function
// will get the error then when it tries to do a recv().
}
closed = NetManager_doExceptionCallback(netDescriptors[index]);
if (closed)
goto closed;
}
if (networkEvents.lNetworkEvents & FD_ACCEPT) {
// There is no specific accept callback (because the BSD sockets
// don't work with specific notification for accept); we use
// the read callback instead.
bool closed;
if (networkEvents.iErrorCode[FD_READ_BIT] != 0) {
// No special handling is required; the callback function
// will try to do an accept() and will get the error then.
}
closed = NetManager_doReadCallback(netDescriptors[index]);
if (closed)
goto closed;
}
if (networkEvents.lNetworkEvents & FD_CONNECT) {
// There is no specific connect callback (because the BSD sockets
// don't work with specific notification for connect); we use
// the write callback instead.
bool closed;
if (networkEvents.iErrorCode[FD_WRITE_BIT] != 0) {
// No special handling is required; the callback function
// should do getsockopt() with SO_ERROR to get the error.
}
closed = NetManager_doWriteCallback(netDescriptors[index]);
if (closed)
goto closed;
}
if (networkEvents.lNetworkEvents & FD_CLOSE) {
// The close event is handled last, in case there was still
// data in the buffers which could be processed.
NetDescriptor_close(netDescriptors[index]);
goto closed;
}
closed: /* No special actions required for now. */
return 0;
}
// This function may be called again from inside a callback function
// triggered by this function. BUG: This may result in callbacks being
// called multiple times.
// This function should however not be called from multiple threads at once.
int
NetManager_process(uint32 *timeoutMs) {
DWORD timeoutTemp;
DWORD waitResult;
DWORD startEvent;
timeoutTemp = (DWORD) *timeoutMs;
// WSAWaitForMultipleEvents only reports events for one socket at a
// time. In order to have each socket checked once, we call it
// again after it has reported an event, but passing only the
// events not yet processed. The second time, the timeout will be set
// to 0, so it won't wait.
startEvent = 0;
while (startEvent < numActiveEvents) {
waitResult = WSAWaitForMultipleEvents(numActiveEvents - startEvent,
&events[startEvent], FALSE, timeoutTemp, FALSE);
if (waitResult == WSA_WAIT_IO_COMPLETION)
continue;
if (waitResult == WSA_WAIT_TIMEOUT) {
// No events waiting.
*timeoutMs = 0;
return 0;
}
if (waitResult == WSA_WAIT_FAILED) {
errno = getWinsockErrno();
*timeoutMs = timeoutTemp;
return -1;
}
{
DWORD eventIndex = waitResult - WSA_WAIT_EVENT_0;
if (NetManager_processEvent((size_t) eventIndex) == -1) {
// errno is set
*timeoutMs = timeoutTemp;
return -1;
}
// Check the rest of the sockets, but don't wait anymore.
startEvent += eventIndex + 1;
timeoutTemp = 0;
}
}
*timeoutMs = timeoutTemp;
return 0;
}
@@ -0,0 +1,35 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETMANAGER_WIN_H
#define _NETMANAGER_WIN_H
typedef struct SocketManagementDataWin SocketManagementDataWin;
typedef SocketManagementDataWin SocketManagementData;
#ifdef NETMANAGER_INTERNAL
struct SocketManagementDataWin {
size_t index;
long eventMask;
};
#endif /* NETMANAGER_INTERNAL */
#endif /* _NETMANAGER_WIN_H */
+91
View File
@@ -0,0 +1,91 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#include "netport.h"
#ifdef _MSC_VER
# include <winsock2.h>
int
winsockErrorToErrno(int winsockError) {
switch (winsockError) {
case WSAEINTR: return EINTR;
case WSAEACCES: return EACCES;
case WSAEFAULT: return EFAULT;
case WSAEINVAL: return EINVAL;
case WSAEMFILE: return EMFILE;
case WSAEWOULDBLOCK: return EWOULDBLOCK;
case WSAEINPROGRESS: return EINPROGRESS;
case WSAEALREADY: return EALREADY;
case WSAENOTSOCK: return ENOTSOCK;
case WSAEDESTADDRREQ: return EDESTADDRREQ;
case WSAEMSGSIZE: return EMSGSIZE;
case WSAEPROTOTYPE: return EPROTOTYPE;
case WSAENOPROTOOPT: return ENOPROTOOPT;
case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT;
case WSAESOCKTNOSUPPORT: return ESOCKTNOSUPPORT;
case WSAEOPNOTSUPP: return EOPNOTSUPP;
case WSAEPFNOSUPPORT: return EPFNOSUPPORT;
case WSAEAFNOSUPPORT: return EAFNOSUPPORT;
case WSAEADDRINUSE: return EADDRINUSE;
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
case WSAENETDOWN: return ENETDOWN;
case WSAENETUNREACH: return ENETUNREACH;
case WSAENETRESET: return ENETRESET;
case WSAECONNABORTED: return ECONNABORTED;
case WSAECONNRESET: return ECONNRESET;
case WSAENOBUFS: return ENOBUFS;
case WSAEISCONN: return EISCONN;
case WSAENOTCONN: return ENOTCONN;
case WSAESHUTDOWN: return ESHUTDOWN;
case WSAETIMEDOUT: return ETIMEDOUT;
case WSAECONNREFUSED: return ECONNREFUSED;
case WSAEHOSTDOWN: return EHOSTDOWN;
case WSAEHOSTUNREACH: return EHOSTUNREACH;
case WSAEPROCLIM: return EPROCLIM;
case WSASYSNOTREADY: return ENOSYS;
case WSAVERNOTSUPPORTED: return ENOSYS;
case WSANOTINITIALISED: return ENOSYS;
case WSAEDISCON: return ECONNRESET;
case WSATYPE_NOT_FOUND: return ENODATA;
case WSAHOST_NOT_FOUND: return ENODATA;
case WSATRY_AGAIN: return EAGAIN;
case WSANO_RECOVERY: return EIO;
case WSANO_DATA: return ENODATA;
case WSA_INVALID_HANDLE: return EBADF;
case WSA_INVALID_PARAMETER: return EINVAL;
case WSA_IO_INCOMPLETE: return EAGAIN;
case WSA_IO_PENDING: return EINPROGRESS;
case WSA_NOT_ENOUGH_MEMORY: return ENOMEM;
case WSA_OPERATION_ABORTED: return EINTR;
case WSAEINVALIDPROCTABLE: return ENOSYS;
case WSAEINVALIDPROVIDER: return ENOSYS;
case WSAEPROVIDERFAILEDINIT: return ENOSYS;
case WSASYSCALLFAILURE: return EIO;
default: return EIO;
}
}
int
getWinsockErrno(void) {
return winsockErrorToErrno(WSAGetLastError());
}
#endif /* defined (_MSC_VER) */
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETPORT_H
#define _NETPORT_H
#if defined (_MSC_VER)
int winsockErrorToErrno(int winsockError);
int getWinsockErrno(void);
# define EAI_SYSTEM 0x02000001
// Any value will do that doesn't conflict with an existing value.
#endif
#endif /* _NETPORT_H */
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETWORK_H
#define _NETWORK_H
void Network_init(void);
void Network_uninit(void);
#endif /* _NETWORK_H */
@@ -0,0 +1,30 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "network.h"
void
Network_init(void) {
// Nothing to do.
}
void
Network_uninit(void) {
// Nothing to do.
}
@@ -0,0 +1,75 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netport.h"
#include "network.h"
#include "libs/misc.h"
#include "libs/log.h"
#include <errno.h>
#include <winsock2.h>
void
Network_init(void) {
WSADATA data;
int startupResult;
WORD requestVersion = MAKEWORD(2, 2);
startupResult = WSAStartup(requestVersion, &data);
if (startupResult != 0) {
int savedErrno = winsockErrorToErrno(startupResult);
log_add(log_Fatal, "WSAStartup failed.\n");
errno = savedErrno;
explode();
}
#ifdef DEBUG
log_add(log_Debug, "Winsock version %d.%d found: \"%s\".\n",
LOBYTE(data.wHighVersion), HIBYTE(data.wHighVersion),
data.szDescription);
log_add(log_Debug, "Requesting to use Winsock version %d.%d, got "
"version %d.%d.\n",
LOBYTE(requestVersion), HIBYTE(requestVersion),
LOBYTE(data.wVersion), HIBYTE(data.wVersion));
#endif
if (data.wVersion != requestVersion) {
log_add(log_Fatal, "Winsock version %d.%d presented, requested "
"%d.%d.\n", LOBYTE(data.wVersion), HIBYTE(data.wVersion),
LOBYTE(requestVersion), HIBYTE(requestVersion));
(void) WSACleanup();
// Ignoring errors; we're going to abort anyhow.
explode();
}
}
void
Network_uninit(void) {
int cleanupResult;
cleanupResult = WSACleanup();
if (cleanupResult == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
log_add(log_Fatal, "WSACleanup failed.\n");
errno = savedErrno;
explode();
}
}
@@ -0,0 +1,2 @@
uqm_CFILES="socket_bsd.c socket.c"
@@ -0,0 +1,59 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define SOCKET_INTERNAL
#include "socket.h"
#ifdef _MSC_VER
# include <winsock2.h>
#else
# include <sys/socket.h>
# include <netinet/in.h>
#endif
////////////////////////////////////////////////////////////////////////////
const int protocolFamilyTranslation[] = {
/* .[PF_unspec] = */ PF_UNSPEC,
/* .[PF_inet] = */ PF_INET,
/* .[PF_inet6] = */ PF_INET6,
};
const int protocolTranslation[] = {
/* .[IPProto_tcp] = */ IPPROTO_TCP,
/* .[IPProto_udp] = */ IPPROTO_UDP,
};
const int socketTypeTranslation[] = {
/* .[Sock_stream] = */ SOCK_STREAM,
/* .[Sock_dgram] = */ SOCK_DGRAM,
};
Socket *
Socket_open(ProtocolFamily domain, SocketType type, Protocol protocol) {
return Socket_openNative(protocolFamilyTranslation[domain],
socketTypeTranslation[type], protocolTranslation[protocol]);
}
////////////////////////////////////////////////////////////////////////////
@@ -0,0 +1,93 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _UQM_SOCKET_H
#define _UQM_SOCKET_H
typedef struct Socket Socket;
#define Socket_noSocket ((Socket *) NULL)
#include "port.h"
#ifdef WIN32
# include "socket_win.h"
#else
# include "socket_bsd.h"
#endif
////////////////////////////////////////////////////////////////////////////
// Defining our own types for protocol families and protocols instead of
// using the system defines, so that the layer using this API does not have
// to have anything to do with the system layer.
typedef enum {
PF_unspec,
PF_inet,
PF_inet6,
} ProtocolFamily;
typedef ProtocolFamily AddressFamily;
typedef enum {
IPProto_tcp,
IPProto_udp,
} Protocol;
typedef enum {
Sock_stream,
Sock_dgram,
} SocketType;
#ifdef SOCKET_INTERNAL
extern const int protocolFamilyTranslation[];
#define addressFamilyTranslation protocolFamilyTranslation;
extern const int protocolTranslation[];
extern const int socketTypeTranslation[];
#endif
////////////////////////////////////////////////////////////////////////////
Socket *Socket_open(ProtocolFamily domain, SocketType type,
Protocol protocol);
#ifdef SOCKET_INTERNAL
Socket *Socket_openNative(int domain, int type, int protocol);
#endif
int Socket_close(Socket *sock);
int Socket_connect(Socket *sock, const struct sockaddr *addr,
socklen_t addrLen);
int Socket_bind(Socket *sock, const struct sockaddr *addr,
socklen_t addrLen);
int Socket_listen(Socket *sock, int backlog);
Socket *Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen);
ssize_t Socket_send(Socket *sock, const void *buf, size_t len, int flags);
ssize_t Socket_recv(Socket *sock, void *buf, size_t len, int flags);
int Socket_setNonBlocking(Socket *sock);
int Socket_setReuseAddr(Socket *sock);
int Socket_setInteractive(Socket *sock);
int Socket_setInlineOOB(Socket *sock);
int Socket_setKeepAlive(Socket *sock);
int Socket_getError(Socket *sock, int *err);
#endif /* _UQM_SOCKET_H */
@@ -0,0 +1,257 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Socket functions for BSD sockets.
#define SOCKET_INTERNAL
#include "socket.h"
#include "libs/log.h"
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netdb.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <unistd.h>
Socket *
Socket_alloc(void) {
return malloc(sizeof (Socket));
}
void
Socket_free(Socket *sock) {
free(sock);
}
Socket *
Socket_openNative(int domain, int type, int protocol) {
Socket *result;
int fd;
fd = socket(domain, type, protocol);
if (fd == -1) {
// errno is set
return Socket_noSocket;
}
result = Socket_alloc();
result->fd = fd;
return result;
}
int
Socket_close(Socket *sock) {
int closeResult;
do {
closeResult = close(sock->fd);
if (closeResult == 0) {
Socket_free(sock);
return 0;
}
} while (errno == EINTR);
return -1;
}
int
Socket_connect(Socket *sock, const struct sockaddr *addr,
socklen_t addrLen) {
int connectResult;
do {
connectResult = connect(sock->fd, addr, addrLen);
} while (connectResult == -1 && errno == EINTR);
return connectResult;
}
int
Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
return bind(sock->fd, addr, addrLen);
}
int
Socket_listen(Socket *sock, int backlog) {
return listen(sock->fd, backlog);
}
Socket *
Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
int acceptResult;
socklen_t tempAddrLen;
do {
tempAddrLen = *addrLen;
acceptResult = accept(sock->fd, addr, &tempAddrLen);
if (acceptResult != -1) {
Socket *result = Socket_alloc();
result->fd = acceptResult;
*addrLen = tempAddrLen;
return result;
}
} while (errno == EINTR);
// errno is set
return Socket_noSocket;
}
ssize_t
Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
return send(sock->fd, buf, len, flags);
}
ssize_t
Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
return recv(sock->fd, buf, len, flags);
}
int
Socket_setNonBlocking(Socket *sock) {
int flag = 1;
if (ioctl(sock->fd, FIONBIO, &flag) == -1) {
int savedErrno = errno;
log_add(log_Error, "Setting non-block mode on socket failed: %s.\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_setReuseAddr(Socket *sock) {
int flag = 1;
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof flag)
== -1) {
int savedErrno = errno;
log_add(log_Error, "Setting socket reuse failed: %s.\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
// Send data as soon as it is available. Do not collect data to send at
// once.
int
Socket_setNodelay(Socket *sock) {
int flag = 1;
if (setsockopt(sock->fd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof flag)
== -1) {
#ifdef DEBUG
int savedErrno = errno;
log_add(log_Warning, "Disabling Nagle algorithm failed: %s.\n",
strerror(errno));
errno = savedErrno;
#endif
return -1;
}
return 0;
}
// 'tos' should be IPTOS_LOWDELAY, IPTOS_THROUGHPUT, IPTOS_THROUGHPUT,
// IPTOS_RELIABILITY, or IPTOS_MINCOST.
int
Socket_setTOS(Socket *sock, int tos) {
if (setsockopt(sock->fd, SOL_IP, IP_TOS, &tos, sizeof tos) == -1) {
#ifdef DEBUG
int savedErrno = errno;
log_add(log_Warning, "Setting socket type-of-service failed: %s.\n",
strerror(errno));
errno = savedErrno;
#endif
return -1;
}
return 0;
}
// This function setups the socket for optimal configuration for an
// interactive connection.
int
Socket_setInteractive(Socket *sock) {
if (Socket_setNodelay(sock) == -1) {
// errno is set
return -1;
}
if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
// errno is set
return -1;
}
return 0;
}
int
Socket_setInlineOOB(Socket *sock) {
int flag = 1;
if (setsockopt(sock->fd, SOL_SOCKET, SO_OOBINLINE, &flag, sizeof flag)
== -1) {
int savedErrno = errno;
log_add(log_Error, "Setting inline OOB on socket failed: %s\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_setKeepAlive(Socket *sock) {
int flag = 1;
if (setsockopt(sock->fd, SOL_TCP, SO_KEEPALIVE, &flag, sizeof flag)
== -1) {
int savedErrno = errno;
log_add(log_Error, "Setting keep-alive on socket failed: %s\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_getError(Socket *sock, int *err) {
socklen_t errLen = sizeof(*err);
if (getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, err, &errLen) == -1) {
// errno is set
return -1;
}
assert(errLen == sizeof(*err));
// err is set
return 0;
}
@@ -0,0 +1,33 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SOCKET_BSD_H
#define _SOCKET_BSD_H
#include "types.h"
#include <sys/socket.h>
#ifdef SOCKET_INTERNAL
struct Socket {
int fd;
};
#endif /* SOCKET_INTERNAL */
#endif /* _SOCKET_BSD_H */
@@ -0,0 +1,278 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// Socket functions for Winsock sockets.
#define PORT_WANT_ERRNO
#include "port.h"
#define SOCKET_INTERNAL
#include "socket.h"
#include "libs/log.h"
#include <assert.h>
#include <errno.h>
#include <winsock2.h>
Socket *
Socket_alloc(void) {
return malloc(sizeof (Socket));
}
void
Socket_free(Socket *sock) {
free(sock);
}
Socket *
Socket_openNative(int domain, int type, int protocol) {
Socket *result;
SOCKET sock;
sock = socket(domain, type, protocol);
if (sock == INVALID_SOCKET) {
errno = getWinsockErrno();
return Socket_noSocket;
}
result = Socket_alloc();
result->sock = sock;
return result;
}
int
Socket_close(Socket *sock) {
int closeResult;
do {
closeResult = closesocket(sock->sock);
if (closeResult != SOCKET_ERROR) {
Socket_free(sock);
return 0;
}
errno = getWinsockErrno();
} while (errno == EINTR);
return -1;
}
int
Socket_connect(Socket *sock, const struct sockaddr *addr,
socklen_t addrLen) {
int connectResult;
do {
connectResult = connect(sock->sock, addr, addrLen);
if (connectResult == 0)
return 0;
errno = getWinsockErrno();
} while (errno == EINTR);
return -1;
}
int
Socket_bind(Socket *sock, const struct sockaddr *addr, socklen_t addrLen) {
int bindResult;
bindResult = bind(sock->sock, addr, addrLen);
if (bindResult == SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
return 0;
}
int
Socket_listen(Socket *sock, int backlog) {
int listenResult;
listenResult = listen(sock->sock, backlog);
if (listenResult == SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
return 0;
}
Socket *
Socket_accept(Socket *sock, struct sockaddr *addr, socklen_t *addrLen) {
SOCKET acceptResult;
socklen_t tempAddrLen;
do {
tempAddrLen = *addrLen;
acceptResult = accept(sock->sock, addr, &tempAddrLen);
if (acceptResult != INVALID_SOCKET) {
Socket *result = Socket_alloc();
result->sock = acceptResult;
*addrLen = tempAddrLen;
return result;
}
errno = getWinsockErrno();
} while (errno == EINTR);
// errno is set
return Socket_noSocket;
}
ssize_t
Socket_send(Socket *sock, const void *buf, size_t len, int flags) {
int sendResult;
sendResult = send(sock->sock, buf, len, flags);
if (sendResult == SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
return sendResult;
}
ssize_t
Socket_recv(Socket *sock, void *buf, size_t len, int flags) {
int recvResult;
recvResult = recv(sock->sock, buf, len, flags);
if (recvResult == SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
return recvResult;
}
int
Socket_setNonBlocking(Socket *sock) {
unsigned long flag = 1;
if (ioctlsocket(sock->sock, FIONBIO, &flag) == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
log_add(log_Error, "Setting non-block mode on socket failed: %s.\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_setReuseAddr(Socket *sock) {
BOOL flag = 1;
if (setsockopt(sock->sock, SOL_SOCKET, SO_REUSEADDR,
(const char *) &flag, sizeof flag) == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
log_add(log_Error, "Setting socket reuse failed: %s.\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
// Send data as soon as it is available. Do not collect data to send at
// once.
int
Socket_setNodelay(Socket *sock) {
BOOL flag = 1;
if (setsockopt(sock->sock, IPPROTO_TCP, TCP_NODELAY,
(const char *) &flag, sizeof flag) == SOCKET_ERROR) {
#ifdef DEBUG
int savedErrno = getWinsockErrno();
log_add(log_Warning, "Disabling Nagle algorithm failed: %s.\n",
strerror(errno));
errno = savedErrno;
#endif
return -1;
}
return 0;
}
// This function setups the socket for optimal configuration for an
// interactive connection.
int
Socket_setInteractive(Socket *sock) {
if (Socket_setNodelay(sock) == -1) {
// errno is set
return -1;
}
#if 0
if (Socket_setTOS(sock, IPTOS_LOWDELAY) == -1) {
// errno is set
return -1;
}
#endif
return 0;
}
int
Socket_setInlineOOB(Socket *sock) {
BOOL flag = 1;
if (setsockopt(sock->sock, SOL_SOCKET, SO_OOBINLINE, (const char *) &flag,
sizeof flag) == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
log_add(log_Error, "Setting inline OOB on socket failed: %s\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_setKeepAlive(Socket *sock) {
BOOL flag = 1;
if (setsockopt(sock->sock, IPPROTO_TCP, SO_KEEPALIVE,
(const char *) &flag, sizeof flag) == SOCKET_ERROR) {
int savedErrno = getWinsockErrno();
log_add(log_Error, "Setting keep-alive on socket failed: %s\n",
strerror(errno));
errno = savedErrno;
return -1;
}
return 0;
}
int
Socket_getError(Socket *sock, int *err) {
int errLen = sizeof(*err);
if (getsockopt(sock->sock, SOL_SOCKET, SO_ERROR, (char *) err, &errLen)
== SOCKET_ERROR) {
errno = getWinsockErrno();
return -1;
}
assert(errLen == sizeof(*err));
// err is set
return 0;
}
@@ -0,0 +1,33 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _SOCKET_WIN_H
#define _SOCKET_WIN_H
#ifdef SOCKET_INTERNAL
#include <winsock2.h>
struct Socket {
SOCKET sock;
};
#endif /* SOCKET_INTERNAL */
typedef int socklen_t;
#endif /* _SOCKET_WIN_H */
+982 -190
View File
File diff suppressed because it is too large Load Diff
+18 -5
View File
@@ -25,6 +25,7 @@
#include "libs/sndlib.h"
#include "libs/reslib.h"
typedef struct melee_state MELEE_STATE;
enum
{
@@ -68,7 +69,7 @@ enum
extern FRAME PickMeleeFrame;
#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x1)
#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x01)
#define MAX_TEAM_CHARS 30
#define MAX_VIS_TEAMS 5
@@ -82,11 +83,12 @@ typedef COUNT FleetShipIndex;
typedef struct
{
BYTE ShipList[MELEE_FLEET_SIZE];
UNICODE TeamName[MAX_TEAM_CHARS + 25]; /* in case default names in starcon.txt
are unknowingly mangled */
UNICODE TeamName[MAX_TEAM_CHARS + 1 + 24];
/* The +1 is for the terminating \0; the +24 is in case some
* default name in starcon.txt is unknowingly mangled. */
} TEAM_IMAGE;
typedef struct melee_state
struct melee_state
{
BOOLEAN (*InputFunc) (struct melee_state *pInputState);
COUNT MenuRepeatDelay;
@@ -104,13 +106,24 @@ typedef struct melee_state
TEAM_IMAGE PreBuiltList[NUM_PREBUILT];
MUSIC_REF hMusic;
} MELEE_STATE;
};
typedef MELEE_STATE *PMELEE_STATE;
extern PMELEE_STATE volatile pMeleeState;
extern void Melee (void);
void teamStringChanged (PMELEE_STATE pMS, int player);
void entireFleetChanged (PMELEE_STATE pMS, int player);
void fleetShipChanged (PMELEE_STATE pMS, int player, size_t index);
void updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
size_t len);
bool updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship);
void updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed);
void connectedFeedback (PMELEE_STATE pMS, COUNT side);
void errorFeedback (PMELEE_STATE pMS, COUNT side);
void closeFeedback (PMELEE_STATE pMS, COUNT side);
#endif /* _MELEE_H */
+48
View File
@@ -0,0 +1,48 @@
In netplay/:
crc.{c,h} Generic CRC routines.
checkbuf.{c,h} Buffer of checksums.
checksum.{c,h} Routines for checksumming the game state.
netconnection.{c,h} Definition of NetConnection, being the state of a
network connection, and operations on it.
nc_connect.ci Part of netconnection.c that handles establishing
connections.
netinput.{c,h} Definitions and operations for melee input commands
over a network connection.
netmelee.{c,h} Keeps track of network connections used in the game,
with methods to control them all at once.
Functions that directly access the game data are kept
in the relevant files (melee.c, pickmele.c, battle.c).
netmisc.{c,h} Miscelaneous functions that didn't fit in elsewhere.
netoptions.{c,h} Description of a network connection to be established.
netplay.h Some global netplay definitions.
netrcv.{c,h} Processes incoming packets. Does know about the protocol
and will do consistency checking.
Does not directly manipulate the game state.
netsend.{c,h} Enqueues all sorts of packets for sending.
Does not know about the protocol and hence will do
no checks for protocol sanity.
netstate.{c,h} Definitions of the states of a network connection.
notify.{c,h} Routines for notifying a remote side of local changes.
Knows about the protocal and has assert()s to
check for local consistency.
packet.{c,h} Definition and creation of packets. Create functions
should only be called from netsend.c.
packethandlers.{c,h} Routines for processing each type of incoming packet.
packetq.{c,h} Manages the packet queue.
packetsenders.{c,h} Creates and sends/queues packets.
In netplay/proto/:
confirm.{c,h} Functions for handing the 'confirmation' protocol.
ready.{c,h} Functions for handling the 'ready' protocol.
TODO:
Division:
- files that interface with sockets and knows nothing of the game
- files that know of sockets and the game but don't directly interface
with either
- files that interface with the game and know nothing of sockets
+3
View File
@@ -0,0 +1,3 @@
uqm_SUBDIRS="proto"
uqm_CFILES="checkbuf.c checksum.c crc.c netconnection.c netinput.c netmelee.c netmisc.c netoptions.c netrcv.c netsend.c netstate.c notify.c packet.c packethandlers.c packetsenders.c packetq.c"
+146
View File
@@ -0,0 +1,146 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#include "netplay.h"
#include "checkbuf.h"
#include "libs/log.h"
#include "libs/misc.h"
#include "../battle.h"
// for battleFrameCount
#include <errno.h>
#include <stdlib.h>
static inline BattleFrameCounter
ChecksumBuffer_getCurrentFrameNr(void) {
return battleFrameCount;
}
void
ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval) {
// The input buffer lags BattleInput_inputDelay frames behind,
// but only every interval frames will there be a checksum to be
// checked.
// Checksums will be checked when 'frameNr % interval == 0'.
// (and frameNr is zero-based).
// The checksum of frame n will be processed in frame 'n + delay'.
// In the worst case, side 1 processes frames 'n' through 'n + delay - 1',
// then blocks in frame 'n + delay' (after sending a checksum, if that's
// pertinent for that frame).
// Then side 2 receives all this input and these checksums, and
// progresses to 'delay' frames after the last frame the received input
// originated from, and blocks in the frame after it (after sending a
// checksum, if that's pertinent for the frame).
// So it sent input and checksums for frames 'n' through
// 'n + delay + delay + 1'.
// The input and checksums for these '2*delay + 2' frames are still
// unhandled by side 1, so it needs buffer space for this.
// With checksums only sent every interval frames, the buffer space
// needed will be 'roundUp(2*delay + 2)' spaces.
size_t bufSize = ((2 * delay + 2) + (interval - 1)) / interval;
{
#ifdef NETPLAY_DEBUG
size_t i;
#endif
cb->checksums = malloc(bufSize * sizeof (ChecksumEntry));
cb->maxSize = bufSize;
cb->interval = interval;
#ifdef NETPLAY_DEBUG
for (i = 0; i < bufSize; i++) {
cb->checksums[i].checksum = 0;
cb->checksums[i].frameNr = (BattleFrameCounter) -1;
}
#endif
}
}
void
ChecksumBuffer_uninit(ChecksumBuffer *cb) {
if (cb->checksums != NULL) {
free(cb->checksums);
cb->checksums = NULL;
}
}
// Returns the entry that would be used for the checksum for the specified
// frame. Whether the entry is actually valid is not checked.
static ChecksumEntry *
ChecksumBuffer_getChecksumEntry(ChecksumBuffer *cb,
BattleFrameCounter frameNr) {
size_t index;
ChecksumEntry *entry;
assert(frameNr % cb->interval == 0);
// We only record checksums exactly every 'interval' frames.
index = (frameNr / cb->interval) % cb->maxSize;
entry = &cb->checksums[index];
return entry;
}
bool
ChecksumBuffer_addChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
Checksum checksum) {
ChecksumEntry *entry;
assert(frameNr % cb->interval == 0);
entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);
#ifdef NETPLAY_DEBUG
entry->frameNr = frameNr;
#endif
entry->checksum = checksum;
return true;
}
// Pre: frameNr is within the range of the checksums stored in cb.
bool
ChecksumBuffer_getChecksum(ChecksumBuffer *cb, BattleFrameCounter frameNr,
Checksum *result) {
ChecksumEntry *entry;
entry = ChecksumBuffer_getChecksumEntry(cb, frameNr);
#ifdef NETPLAY_DEBUG
if (frameNr != entry->frameNr) {
log_add(log_Error, "Checksum buffer entry for requested frame %u "
"(still?) contains a checksum for frame %u.\n",
frameNr, entry->frameNr);
return false;
}
#endif
*result = entry->checksum;
return true;
}
+62
View File
@@ -0,0 +1,62 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CHECKBUF_H
#define _CHECKBUF_H
typedef struct ChecksumEntry ChecksumEntry;
typedef struct ChecksumBuffer ChecksumBuffer;
#include "../battle.h"
// for BattleFrameCounter
#include "checksum.h"
struct ChecksumEntry {
#ifdef NETPLAY_DEBUG
BattleFrameCounter frameNr;
// The number of the frame this checksum originated from.
// If the checksumming code is working correctly, the checksum
// can only come from one frame, so this value is not needed
// for normal operation.
// Its only use is to detect some cases where checksumming code
// is *not* working correctly.
#endif
Checksum checksum;
};
struct ChecksumBuffer {
ChecksumEntry *checksums;
// Cyclic buffer. if 'size' > 0, then 'first' is an index to
// the first used entry, 'size' is the number of used
// entries in the buffer, and (first + size) % maxSize is the
// index to just past the end of the buffer.
size_t maxSize;
size_t interval;
};
void ChecksumBuffer_init(ChecksumBuffer *cb, size_t delay, size_t interval);
void ChecksumBuffer_uninit(ChecksumBuffer *cb);
bool ChecksumBuffer_addChecksum(ChecksumBuffer *cb,
BattleFrameCounter frameNr, Checksum checksum);
bool ChecksumBuffer_getChecksum(ChecksumBuffer *cb,
BattleFrameCounter frameNr, Checksum *result);
#endif /* _CHECKBUF_H */
+268
View File
@@ -0,0 +1,268 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef NETPLAY
#include "checksum.h"
#include "netoptions.h"
#ifdef NETPLAY_CHECKSUM
#include "checkbuf.h"
#include "crc.h"
// for DUMP_CRC_OPS
#include "netconnection.h"
#include "netmelee.h"
#include "libs/log.h"
#include "libs/misc.h"
ChecksumBuffer localChecksumBuffer;
void
crc_processEXTENT(crc_State *state, const EXTENT *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processEXTENT().");
#endif
crc_processCOORD(state, val->width);
crc_processCOORD(state, val->height);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processEXTENT().");
#endif
}
void
crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processVELOCITY_DESC().");
#endif
crc_processCOUNT(state, val->TravelAngle);
crc_processEXTENT(state, &val->vector);
crc_processEXTENT(state, &val->fract);
crc_processEXTENT(state, &val->error);
crc_processEXTENT(state, &val->incr);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processVELOCITY_DESC().");
#endif
}
#if 0
void
crc_processPOINT(crc_State *state, const POINT *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processPOINT().");
#endif
crc_processCOORD(state, val->x);
crc_processCOORD(state, val->y);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processPOINT().");
#endif
}
void
crc_processSTAMP(crc_State *state, const STAMP *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processSTAMP().");
#endif
crc_processPOINT(state, val->origin);
crc_processFRAME(state, val->frame);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processSTAMP().");
#endif
}
void
crc_processINTERSECT_CONTROL(crc_State *state, const INTERSECT_CONTROL *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processINTERSECT_CONTROL().");
#endif
crc_processTIME_VALUE(state, val->last_time_val);
crc_processPOINT(state, &val->EndPoint);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processINTERSECT_CONTROL().");
#endif
}
#endif
void
crc_processELEMENT(crc_State *state, const ELEMENT *val) {
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "START crc_processELEMENT().");
#endif
crc_processELEMENT_FLAGS(state, val->state_flags);
crc_processCOUNT(state, val->life_span);
crc_processCOUNT(state, val->crew_level);
crc_processBYTE(state, val->mass_points);
// HACK: when a ship is being destroyed, turn_wait is abused to store
// the side this ship is on. This must be excluded from the checksum
// as this does not have to be the same for both sides.
{
extern void new_ship(PELEMENT ElementPtr);
BYTE turn_wait = val->turn_wait;
if (val->preprocess_func == new_ship)
turn_wait = 0;
crc_processBYTE(state, turn_wait);
}
crc_processBYTE(state, val->thrust_wait);
crc_processVELOCITY_DESC(state, &val->velocity);
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processELEMENT().");
#endif
}
void
crc_processDispQueue(crc_State *state) {
HELEMENT element;
HELEMENT nextElement;
#ifdef DUMP_CRC_OPS
size_t i = 0;
log_add(log_Debug, "START crc_processDispQueue() (frame %u).",
battleFrameCount);
#endif
for (element = GetHeadElement(); element != 0; element = nextElement) {
ELEMENTPTR elementPtr;
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "===== disp_q[%d]:", i);
#endif
LockElement(element, &elementPtr);
crc_processELEMENT(state, element);
nextElement = GetSuccElement(elementPtr);
UnlockElement(element);
#ifdef DUMP_CRC_OPS
i++;
#endif
}
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "END crc_processDispQueue() (frame %u).",
battleFrameCount);
#endif
}
void
initChecksumBuffers(void) {
size_t player;
for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
{
NetConnection *conn;
ChecksumBuffer *cb;
conn = netConnections[player];
if (conn == NULL)
continue;
cb = NetConnection_getChecksumBuffer(conn);
ChecksumBuffer_init(cb, getBattleInputDelay(),
NETPLAY_CHECKSUM_INTERVAL);
}
ChecksumBuffer_init(&localChecksumBuffer, getBattleInputDelay(),
NETPLAY_CHECKSUM_INTERVAL);
}
void
uninitChecksumBuffers(void)
{
size_t player;
for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
{
NetConnection *conn;
ChecksumBuffer *cb;
conn = netConnections[player];
if (conn == NULL)
continue;
cb = NetConnection_getChecksumBuffer(conn);
ChecksumBuffer_uninit(cb);
}
ChecksumBuffer_uninit(&localChecksumBuffer);
}
void
addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum) {
assert(frameNr == battleFrameCount);
ChecksumBuffer_addChecksum(&localChecksumBuffer, frameNr, checksum);
}
void
addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Checksum checksum) {
ChecksumBuffer *cb;
assert(frameNr <= battleFrameCount + getBattleInputDelay() + 1);
assert(frameNr + getBattleInputDelay() >= battleFrameCount);
cb = NetConnection_getChecksumBuffer(conn);
ChecksumBuffer_addChecksum(cb, frameNr, checksum);
}
bool
verifyChecksums(BattleFrameCounter frameNr) {
Checksum localChecksum;
size_t player;
if (!ChecksumBuffer_getChecksum(&localChecksumBuffer, frameNr,
&localChecksum)) {
// Right now, we require that a checksum is present.
// If/when we move to UDP, and packets may get lost, we may prefer
// not to do any checks in this case.
return false;
}
for (player = 0; player < NETPLAY_NUM_PLAYERS; player++)
{
NetConnection *conn;
ChecksumBuffer *cb;
Checksum remoteChecksum;
conn = netConnections[player];
if (conn == NULL)
continue;
cb = NetConnection_getChecksumBuffer(conn);
if (!ChecksumBuffer_getChecksum(cb, frameNr, &remoteChecksum))
return false;
if (localChecksum != remoteChecksum) {
log_add(log_Error, "Network connections have gone out of "
"sync.\n");
return false;
}
}
return true;
}
#endif /* NETPLAY_CHECKSUM */
#endif /* NETPLAY */
+86
View File
@@ -0,0 +1,86 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CHECKSUM_H
#define _CHECKSUM_H
#include "types.h"
typedef uint32 Checksum;
#include "crc.h"
#include "element.h"
#include "libs/gfxlib.h"
#include "netconnection.h"
static inline void
crc_processELEMENT_FLAGS(crc_State *state, ELEMENT_FLAGS val) {
crc_processUint16(state, (uint16) (val & ~(GOOD_GUY | BAD_GUY)));
// Excluding the element side, as this does not have to
// be the same for both players over a network.
}
static inline void
crc_processCOUNT(crc_State *state, COUNT val) {
crc_processUint16(state, (uint16) val);
}
static inline void
crc_processBYTE(crc_State *state, BYTE val) {
crc_processUint8(state, (uint8) val);
}
static inline void
crc_processCOORD(crc_State *state, COORD val) {
crc_processUint16(state, (uint16) val);
}
#if 0
static inline void
crc_processTIME_VALUE(crc_State *state, const TIME_VALUE val) {
crc_processUint16(state, (uint16) val);
}
#endif
void crc_processEXTENT(crc_State *state, const EXTENT *val);
void crc_processVELOCITY_DESC(crc_State *state, const VELOCITY_DESC *val);
#if 0
void crc_processPOINT(crc_State *state, const POINT *val);
void crc_processSTAMP(crc_State *state, const STAMP *val);
void crc_processINTERSECT_CONTROL(crc_State *state,
const INTERSECT_CONTROL *val);
#endif
void crc_processELEMENT(crc_State *state, const ELEMENT *val);
void crc_processDispQueue(crc_State *state);
void initChecksumBuffers(void);
void uninitChecksumBuffers(void);
void addLocalChecksum(BattleFrameCounter frameNr, Checksum checksum);
void addRemoteChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Checksum checksum);
bool verifyChecksums(BattleFrameCounter frameNr);
#endif /* _CHECKSUM_H */
+139
View File
@@ -0,0 +1,139 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "crc.h"
#ifdef DUMP_CRC_OPS
# include "libs/log.h"
#endif
// CRC table for Polynomial 0x04c11db7 (0xedb88320 reversed)
uint32 crcTable[256] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
void
crc_init(crc_State *state) {
state->crc = 0xffffffff;
}
void
crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen) {
uint8 *end = buf + bufLen;
uint32 newCrc = state->crc;
while (buf < end)
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ *buf) & 0xff];
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "crc_processBytes(%08x, [%z bytes]) --> %08x.",
state->crc, bufLen, newCrc);
#endif
state->crc = newCrc;
}
void
crc_processUint8(crc_State *state, uint8 val) {
uint32 newCrc = state->crc;
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ val) & 0xff];
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "crc_processUint8(%08x, %02x) --> %08x.",
state->crc, (int) val, newCrc);
#endif
state->crc = newCrc;
}
void
crc_processUint16(crc_State *state, uint16 val) {
uint32 newCrc = state->crc;
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val >> 8) ) & 0xff];
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "crc_processUint16(%08x, %04x) --> %08x.",
state->crc, (int) val, newCrc);
#endif
state->crc = newCrc;
}
void
crc_processUint32(crc_State *state, uint32 val) {
uint32 newCrc = state->crc;
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ (val & 0xff)) & 0xff];
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 8) & 0xff)) & 0xff];
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 16) & 0xff)) & 0xff];
newCrc = (newCrc >> 8) ^ crcTable[(newCrc ^ ((val >> 24) )) & 0xff];
#ifdef DUMP_CRC_OPS
log_add(log_Debug, "crc_processUint32(%08x, %08x) --> %08x.",
state->crc, (int) val, newCrc);
#endif
state->crc = newCrc;
}
uint32
crc_finish(const crc_State *state) {
return ~state->crc;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CRC_H
#define _CRC_H
#include "types.h"
#include <stddef.h>
// If set, every CRC operation is logged. Very spammy.
#undef DUMP_CRC_OPS
typedef struct crc_State crc_State;
struct crc_State {
uint32 crc;
};
void crc_init(crc_State *state);
void crc_processBytes(crc_State *state, uint8 *buf, size_t bufLen);
void crc_processUint8(crc_State *state, uint8 val);
void crc_processUint16(crc_State *state, uint16 val);
void crc_processUint32(crc_State *state, uint32 val);
uint32 crc_finish(const crc_State *state);
#endif /* _CRC_H */
+303
View File
@@ -0,0 +1,303 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// This file is part of netconnection.c, from where it is #included.
static inline ConnectStateData *ConnectStateData_alloc(void);
static inline ConnectStateData *ConnectStateData_new(void);
static inline void ConnectStateData_free(ConnectStateData *connectStateData);
static void ConnectStateData_delete(ConnectStateData *connectStateData);
static int NetConnection_go(NetConnection *conn);
static ListenState *NetConnection_serverGo(NetConnection *conn);
static ConnectState *NetConnection_clientGo(NetConnection *conn);
static void NetConnection_connected(NetConnection *conn);
static void NetConnection_connectedServerCallback(ListenState *listenState,
NetDescriptor *listenNd, NetDescriptor *newNd,
const struct sockaddr *addr, socklen_t addrLen);
static void NetConnection_connectedClientCallback(ConnectState *connectState,
NetDescriptor *newNd, const struct sockaddr *addr, socklen_t addrLen);
static void NetConnection_connected(NetConnection *conn);
static void NetConnection_connectedServerErrorCallback(
ListenState *listenState, const ListenError *listenError);
static void NetConnection_connectedClientErrorCallback(
ConnectState *connectState, const ConnectError *connectError);
////////////////////////////////////////////////////////////////////////////
static inline ConnectStateData *
ConnectStateData_alloc(void) {
return (ConnectStateData *) malloc(sizeof (ConnectStateData));
}
static inline ConnectStateData *
ConnectStateData_new(void) {
ConnectStateData *connectStateData = ConnectStateData_alloc();
connectStateData->releaseFunction =
(NetConnectionStateData_ReleaseFunction) ConnectStateData_delete;
return connectStateData;
}
static inline void
ConnectStateData_free(ConnectStateData *connectStateData) {
free(connectStateData);
}
static void
ConnectStateData_delete(ConnectStateData *connectStateData) {
if (connectStateData->isServer) {
ListenState *listenState = connectStateData->state.listenState;
ListenState_close(listenState);
} else {
ConnectState *connectState = connectStateData->state.connectState;
ConnectState_close(connectState);
}
ConnectStateData_free(connectStateData);
}
////////////////////////////////////////////////////////////////////////////
static int
NetConnection_go(NetConnection *conn) {
ConnectStateData *connectStateData;
if (NetConnection_isConnected(conn))
return 0;
if (conn->options->isServer) {
ListenState *listenState;
listenState = NetConnection_serverGo(conn);
if (listenState == NULL) {
// errno is set
return -1;
}
connectStateData = ConnectStateData_new();
connectStateData->state.listenState = listenState;
} else {
ConnectState *connectState;
connectState = NetConnection_clientGo(conn);
if (connectState == NULL) {
// errno is set
return -1;
}
connectStateData = ConnectStateData_new();
connectStateData->state.connectState = connectState;
}
connectStateData->isServer = conn->options->isServer;
NetConnection_setStateData(conn, (void *) connectStateData);
return 0;
}
static ListenState *
NetConnection_serverGo(NetConnection *conn) {
ListenFlags listenFlags;
ListenState *result;
assert(conn->state == NetState_unconnected);
assert(conn->options->isServer);
NetConnection_setState(conn, NetState_connecting);
listenFlags.familyDemand =
#if NETPLAY == NETPLAY_IPV4
PF_inet;
#else
PF_unspec;
#endif
listenFlags.familyPrefer = PF_unspec;
result = listenPort(conn->options->port, IPProto_tcp, &listenFlags,
NetConnection_connectedServerCallback,
NetConnection_connectedServerErrorCallback, (void *) conn);
return result;
}
static ConnectState *
NetConnection_clientGo(NetConnection *conn) {
ConnectFlags connectFlags;
ConnectState *result;
assert(conn->state == NetState_unconnected);
assert(!conn->options->isServer);
NetConnection_setState(conn, NetState_connecting);
connectFlags.familyDemand =
#if NETPLAY == NETPLAY_IPV4
PF_inet;
#else
PF_unspec;
#endif
connectFlags.familyPrefer = PF_unspec;
connectFlags.timeout = NETPLAY_CONNECTTIMEOUT;
connectFlags.retryDelayMs = NETPLAY_RETRYDELAY;
result = connectHostByName(conn->options->host, conn->options->port,
IPProto_tcp, &connectFlags, NetConnection_connectedClientCallback,
NetConnection_connectedClientErrorCallback, (void *) conn);
return result;
}
// Called when an incoming connection has been established.
// The caller gives up ownership of newNd
static void
NetConnection_connectedServerCallback(ListenState *listenState,
NetDescriptor *listenNd, NetDescriptor *newNd,
const struct sockaddr *addr, socklen_t addrLen) {
NetConnection *conn =
(NetConnection *) ListenState_getExtra(listenState);
assert(conn->nd == NULL);
conn->nd = newNd;
// No incRef(); the caller gives up ownership.
NetDescriptor_setExtra(conn->nd, (void *) conn);
(void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
// Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself.
conn->stateFlags.myTurn = true;
// The "server" may speak first.
conn->stateFlags.discriminant = true;
NetConnection_connected(conn);
(void) listenNd;
(void) addr;
(void) addrLen;
}
// Called when an outgoing connection has been established.
// The caller gives up ownership of newNd
static void
NetConnection_connectedClientCallback(ConnectState *connectState,
NetDescriptor *newNd,
const struct sockaddr *addr, socklen_t addrLen) {
NetConnection *conn =
(NetConnection *) ConnectState_getExtra(connectState);
assert(conn->nd == NULL);
conn->nd = newNd;
// No incRef(); the caller gives up ownership.
NetDescriptor_setExtra(conn->nd, (void *) conn);
(void) Socket_setInteractive(NetDescriptor_getSocket(conn->nd));
// Ignore errors; it's not a big deal. In debug mode, a message
// will already have been printed from the function itself.
conn->stateFlags.myTurn = false;
// The "server" may speak first.
conn->stateFlags.discriminant = false;
NetConnection_connected(conn);
(void) addr;
(void) addrLen;
}
// Called when a connection has been established.
static void
NetConnection_connected(NetConnection *conn) {
ConnectStateData *connectStateData;
conn->stateFlags.connected = true;
NetConnection_setState(conn, NetState_init);
connectStateData = (ConnectStateData *) NetConnection_getStateData(conn);
ConnectStateData_delete(connectStateData);
NetConnection_setStateData(conn, NULL);
NetDescriptor_setReadCallback(conn->nd, dataReadyCallback);
NetDescriptor_setCloseCallback(conn->nd, closeCallback);
(*conn->connectCallback)(conn);
}
static void
NetConnection_connectedServerErrorCallback(ListenState *listenState,
const ListenError *listenError) {
NetConnection *conn =
(NetConnection *) ListenState_getExtra(listenState);
NetConnectionError error;
conn->stateFlags.connected = false;
conn->stateFlags.disconnected = true;
ListenState_close(listenState);
NetConnection_setState(conn, NetState_unconnected);
{
ConnectStateData *connectStateData;
connectStateData =
(ConnectStateData *) NetConnection_getStateData(conn);
ConnectStateData_free(connectStateData);
NetConnection_setStateData(conn, NULL);
}
if (conn->errorCallback != NULL) {
error.state = NetState_connecting;
error.err = listenError->err;
error.extra.listenError = listenError;
//NetConnection_incRef(conn);
(*conn->errorCallback)(conn, &error);
//NetConnection_decRef(conn);
}
NetConnection_close(conn);
}
static void
NetConnection_connectedClientErrorCallback(ConnectState *connectState,
const ConnectError *connectError) {
NetConnection *conn =
(NetConnection *) ConnectState_getExtra(connectState);
NetConnectionError error;
conn->stateFlags.connected = false;
conn->stateFlags.disconnected = true;
ConnectState_close(connectState);
NetConnection_setState(conn, NetState_unconnected);
{
ConnectStateData *connectStateData;
connectStateData =
(ConnectStateData *) NetConnection_getStateData(conn);
ConnectStateData_free(connectStateData);
NetConnection_setStateData(conn, NULL);
}
if (conn->errorCallback != NULL) {
error.state = NetState_connecting;
error.err = connectError->err;
error.extra.connectError = connectError;
//NetConnection_incRef(conn);
(*conn->errorCallback)(conn, &error);
//NetConnection_decRef(conn);
}
NetConnection_close(conn);
}
+286
View File
@@ -0,0 +1,286 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "netconnection.h"
#include "netrcv.h"
#if defined(DEBUG) || defined(NETPLAY_DEBUG)
# include "libs/log.h"
#endif
#include <assert.h>
#include <stdlib.h>
static void closeCallback(NetDescriptor *nd);
static void NetConnection_doClose(NetConnection *conn);
#include "nc_connect.ci"
// Used as initial value for Agreement structures, by structure assignment.
const Agreement Agreement_nothingAgreed;
// The NetConnection keeps a pointer to the passed NetplayPeerOptions;
// do not free it as long as the NetConnection exists.
NetConnection *
NetConnection_open(int player, const NetplayPeerOptions *options,
NetConnection_ConnectCallback connectCallback,
NetConnection_CloseCallback closeCallback,
NetConnection_ErrorCallback errorCallback, void *extra) {
NetConnection *conn;
conn = malloc(sizeof (NetConnection));
conn->nd = NULL;
conn->player = player;
conn->state = NetState_unconnected;
conn->options = options;
conn->extra = extra;
PacketQueue_init(&conn->queue);
conn->connectCallback = connectCallback;
conn->closeCallback = closeCallback;
conn->errorCallback = errorCallback;
conn->readyCallback = NULL;
conn->readyCallbackArg = NULL;
conn->readBuf = malloc(NETPLAY_READBUFSIZE);
conn->readEnd = conn->readBuf;
conn->stateData = NULL;
conn->stateFlags.connected = false;
conn->stateFlags.disconnected = false;
conn->stateFlags.myTurn = false;
conn->stateFlags.endingTurn = false;
conn->stateFlags.pendingTurnChange = false;
conn->stateFlags.discriminant = false;
conn->stateFlags.handshake.localOk = false;
conn->stateFlags.handshake.remoteOk = false;
conn->stateFlags.handshake.canceling = false;
conn->stateFlags.ready.localReady = false;
conn->stateFlags.ready.remoteReady = false;
conn->stateFlags.agreement = Agreement_nothingAgreed;
conn->stateFlags.inputDelay = 0;
#ifdef NETPLAY_CHECKSUM
conn->stateFlags.checksumInterval = NETPLAY_CHECKSUM_INTERVAL;
#endif
#ifdef NETPLAY_STATISTICS
{
size_t i;
conn->statistics.packetsReceived = 0;
conn->statistics.packetsSent = 0;
for (i = 0; i < PACKET_NUM; i++)
{
conn->statistics.packetTypeReceived[i] = 0;
conn->statistics.packetTypeSent[i] = 0;
}
}
#endif
NetConnection_go(conn);
return conn;
}
static void
NetConnection_delete(NetConnection *conn) {
free(conn->readBuf);
PacketQueue_uninit(&conn->queue);
free(conn);
}
static void
Netplay_doCloseCallback(NetConnection *conn) {
if (conn->closeCallback != NULL) {
//NetConnection_incRef(conn);
conn->closeCallback(conn);
//NetConnection_decRef(conn);
}
}
// Auxiliary function for closing, used by both closeCallback() and
// NetConnection_close()
static void
NetConnection_doClose(NetConnection *conn) {
conn->stateFlags.connected = false;
conn->stateFlags.disconnected = true;
// First the callback, so that it can still use the information
// of what is the current state, and the stateData:
Netplay_doCloseCallback(conn);
NetConnection_setState(conn, NetState_unconnected);
if (conn->stateData != NULL) {
NetConnectionStateData_release(conn->stateData);
conn->stateData = NULL;
}
}
// Called when the NetDescriptor is shut down.
static void
closeCallback(NetDescriptor *nd) {
NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
if (conn == NULL)
return;
conn->nd = NULL;
NetConnection_doClose(conn);
}
// Close and release a NetDescriptor.
void
NetConnection_close(NetConnection *conn) {
if (conn->nd != NULL) {
NetDescriptor_setCloseCallback(conn->nd, NULL);
// We're not interested in the close callback of the
// NetDescriptor anymore.
NetDescriptor_close(conn->nd);
// This would queue the close callback.
conn->nd = NULL;
}
NetConnection_doClose(conn);
NetConnection_delete(conn);
}
void
NetConnection_doErrorCallback(NetConnection *nd, int err) {
NetConnectionError error;
if (nd->errorCallback != NULL) {
error.state = nd->state;
error.err = err;
}
(*nd->errorCallback)(nd, &error);
}
void
NetConnection_setStateData(NetConnection *conn,
NetConnectionStateData *stateData) {
conn->stateData = stateData;
}
NetConnectionStateData *
NetConnection_getStateData(const NetConnection *conn) {
return conn->stateData;
}
void
NetConnection_setExtra(NetConnection *conn, void *extra) {
conn->extra = extra;
}
void *
NetConnection_getExtra(const NetConnection *conn) {
return conn->extra;
}
void
NetConnection_setReadyCallback(NetConnection *conn,
NetConnection_ReadyCallback callback, void *arg) {
conn->readyCallback = callback;
conn->readyCallbackArg = arg;
}
NetConnection_ReadyCallback
NetConnection_getReadyCallback(const NetConnection *conn) {
return conn->readyCallback;
}
void *
NetConnection_getReadyCallbackArg(const NetConnection *conn) {
return conn->readyCallbackArg;
}
void
NetConnection_setState(NetConnection *conn, NetState state) {
#ifdef NETPLAY_DEBUG
log_add(log_Debug, "NETPLAY: [%d] +/- Connection state changed to: "
"%s.\n", conn->player, netStateData[state].name);
#endif
#ifdef DEBUG
if (state == conn->state) {
log_add(log_Warning, "NETPLAY: [%d] Connection state set to %s "
"while already in that state.\n",
conn->player, netStateData[state].name);
}
#endif
conn->state = state;
}
NetState
NetConnection_getState(const NetConnection *conn) {
return conn->state;
}
bool
NetConnection_getDiscriminant(const NetConnection *conn) {
return conn->stateFlags.discriminant;
}
const NetplayPeerOptions *
NetConnection_getPeerOptions(const NetConnection *conn) {
return conn->options;
}
bool
NetConnection_isConnected(const NetConnection *conn) {
return conn->stateFlags.connected;
}
bool
NetConnection_isMyTurn(const NetConnection *conn) {
return conn->stateFlags.myTurn;
}
int
NetConnection_getPlayerNr(const NetConnection *conn) {
return conn->player;
}
size_t
NetConnection_getInputDelay(const NetConnection *conn) {
return conn->stateFlags.inputDelay;
}
#ifdef NETPLAY_CHECKSUM
ChecksumBuffer *
NetConnection_getChecksumBuffer(NetConnection *conn) {
return &conn->checksumBuffer;
}
size_t
NetConnection_getChecksumInterval(const NetConnection *conn) {
return conn->stateFlags.checksumInterval;
}
#endif /* NETPLAY_CHECKSUM */
#ifdef NETPLAY_STATISTICS
NetStatistics *
NetConnection_getStatistics(NetConnection *conn) {
return &conn->statistics;
}
#endif
+209
View File
@@ -0,0 +1,209 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETCONNECTION_H
#define _NETCONNECTION_H
#include "netplay.h"
// for NETPLAY_STATISTICS
typedef struct NetConnection NetConnection;
typedef struct NetConnectionError NetConnectionError;
typedef struct ConnectStateData ConnectStateData;
#ifdef NETPLAY_STATISTICS
typedef struct NetStatistics NetStatistics;
#endif
typedef void (*NetConnection_ConnectCallback)(NetConnection *nd);
typedef void (*NetConnection_CloseCallback)(NetConnection *nd);
typedef void (*NetConnection_ErrorCallback)(NetConnection *nd,
const NetConnectionError *error);
typedef void (*NetConnection_ReadyCallback)(NetConnection *conn, void *arg);
#include "netstate.h"
#include "netoptions.h"
#ifdef NETPLAY_CHECKSUM
# include "checkbuf.h"
#endif
struct NetConnectionError {
NetState state;
int err;
union {
const struct ListenError *listenError;
const struct ConnectError *connectError;
} extra;
};
#ifdef NETPLAY_STATISTICS
#include "packet.h"
struct NetStatistics {
size_t packetsReceived;
size_t packetTypeReceived[PACKET_NUM];
size_t packetsSent;
size_t packetTypeSent[PACKET_NUM];
};
#endif
#ifdef NETCONNECTION_INTERNAL
#include "libs/net.h"
#include "packetq.h"
typedef struct {
// For actions that require agreement by both parties.
bool localOk : 1; /* Action confirmed by us */
bool remoteOk : 1; /* Action confirmed by the remote party */
bool canceling : 1; /* Awaiting cancel confirmation */
} HandShakeFlags;
typedef struct {
// For actions that do not require agreement, but for which it
// only is relevant that both sides are ready.
bool localReady : 1;
bool remoteReady : 1;
} ReadyFlags;
// Which parameters have we both sides of a connection reached agreement on?
typedef struct {
bool randomSeed : 1;
} Agreement;
typedef struct {
bool connected;
/* This NetConnection is connected. */
bool disconnected;
/* This NetConnection has been disconnected. This implies
* !connected. It is only set if the NetConnection was once
* connected, but is no longer. */
bool myTurn;
/* This party is the only one who may send specific packets
* at the moment. */
bool endingTurn;
/* Request to change the turn from/to the other party. */
bool pendingTurnChange;
/* It is our turn and the other party wants our turn,
* but we're not ready to give it up just yet; we will first
* send our pending packets. */
bool discriminant;
/* If it is true here, it is false on the remote side
* of the same connection. It may be used to break ties.
* Unlike myTurn, this one is guaranteed not to change
* during a connection. Undefined while not connected. */
HandShakeFlags handshake;
ReadyFlags ready;
Agreement agreement;
size_t inputDelay;
/* Used during negotiation of the actual inputDelay. This
* field does NOT necessarilly contain the actual input delay,
* which is a property of the game, not of any specific
* connection. Use getBattleInputDelay() to get at it. */
#ifdef NETPLAY_CHECKSUM
size_t checksumInterval;
#endif
} NetStateFlags;
struct NetConnection {
NetDescriptor *nd;
int player;
// Number of the player for this connection, as it is
// known locally. For the other player, it may be
// differently.
NetState state;
NetStateFlags stateFlags;
NetConnection_ReadyCallback readyCallback;
// Called when both sides have indicated that they are ready.
// Set by Netplay_localReady().
void *readyCallbackArg;
// Extra argument for readyCallback().
// XXX: when is this cleaned up if a connection is broken?
const NetplayPeerOptions *options;
PacketQueue queue;
#ifdef NETPLAY_STATISTICS
NetStatistics statistics;
#endif
#ifdef NETPLAY_CHECKSUM
ChecksumBuffer checksumBuffer;
#endif
NetConnection_ConnectCallback connectCallback;
NetConnection_CloseCallback closeCallback;
NetConnection_ErrorCallback errorCallback;
uint8 *readBuf;
uint8 *readEnd;
NetConnectionStateData *stateData;
// State dependant information.
void *extra;
};
struct ConnectStateData {
NETCONNECTION_STATE_DATA_COMMON
bool isServer;
union {
struct ConnectState *connectState;
struct ListenState *listenState;
} state;
};
#endif /* NETCONNECTION_INTERNAL */
NetConnection *NetConnection_open(int player,
const NetplayPeerOptions *options,
NetConnection_ConnectCallback connectCallback,
NetConnection_CloseCallback closeCallback,
NetConnection_ErrorCallback errorCallback, void *extra);
void NetConnection_close(NetConnection *conn);
bool NetConnection_isConnected(const NetConnection *conn);
void NetConnection_doErrorCallback(NetConnection *nd, int err);
bool NetConnection_isMyTurn(const NetConnection *conn);
void NetConnection_setStateData(NetConnection *conn,
NetConnectionStateData *stateData);
NetConnectionStateData *NetConnection_getStateData(const NetConnection *conn);
void NetConnection_setExtra(NetConnection *conn, void *extra);
void *NetConnection_getExtra(const NetConnection *conn);
void NetConnection_setState(NetConnection *conn, NetState state);
NetState NetConnection_getState(const NetConnection *conn);
bool NetConnection_getDiscriminant(const NetConnection *conn);
const NetplayPeerOptions *NetConnection_getPeerOptions(
const NetConnection *conn);
int NetConnection_getPlayerNr(const NetConnection *conn);
size_t NetConnection_getInputDelay(const NetConnection *conn);
#ifdef NETPLAY_CHECKSUM
ChecksumBuffer *NetConnection_getChecksumBuffer(NetConnection *conn);
size_t NetConnection_getChecksumInterval(const NetConnection *conn);
#endif
#ifdef NETPLAY_STATISTICS
NetStatistics *NetConnection_getStatistics(NetConnection *conn);
#endif
void NetConnection_setReadyCallback(NetConnection *conn,
NetConnection_ReadyCallback callback, void *arg);
NetConnection_ReadyCallback NetConnection_getReadyCallback(
const NetConnection *conn);
void *NetConnection_getReadyCallbackArg(const NetConnection *conn);
#endif /* _NETCONNECTION_H */
+160
View File
@@ -0,0 +1,160 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#include "netplay.h"
#include "netinput.h"
#include "../intel.h"
// for NETWORK_CONTROL
#include "../setup.h"
// For PlayerControl
#include "libs/log.h"
#include <errno.h>
#include <stdlib.h>
static BattleInputBuffer battleInputBuffers[NUM_PLAYERS];
static size_t BattleInput_inputDelay;
// Call before initBattleInputBuffers()
void
setBattleInputDelay(size_t delay) {
BattleInput_inputDelay = delay;
}
size_t
getBattleInputDelay(void) {
return BattleInput_inputDelay;
}
static void
BattleInputBuffer_init(BattleInputBuffer *bib, size_t bufSize) {
bib->buf = malloc(bufSize * sizeof (BATTLE_INPUT_STATE));
bib->maxSize = bufSize;
bib->first = 0;
bib->size = 0;
}
static void
BattleInputBuffer_uninit(BattleInputBuffer *bib) {
if (bib->buf != NULL) {
free(bib->buf);
bib->buf = NULL;
}
bib->maxSize = 0;
bib->first = 0;
bib->size = 0;
}
void
initBattleInputBuffers(void) {
size_t player;
int bufSize = BattleInput_inputDelay * 2 + 2;
// The input of frame n will be processed in frame 'n + delay'.
//
// In the worst case, side 1 processes frames 'n' through 'n + delay - 1',
// then blocks in frame 'n + delay'.
// Then side 2 receives all this input, and progresses to 'delay' frames
// after the last frame the received input originated from, and blocks
// in the frame after it.
// So it sent input for frames 'n' through 'n + delay + delay + 1'.
// The input for these '2*delay + 2' frames are still
// unhandled by side 1, so it needs buffer space for this.
//
// Initially the buffer is filled with inputDelay zeroes,
// so that a party can process at least that much frames.
for (player = 0; player < NUM_PLAYERS; player++)
{
BattleInputBuffer *bib = &battleInputBuffers[player];
BattleInputBuffer_init(bib, bufSize);
{
// Initially a party must be able to process at least inputDelay
// frames, so we fill the buffer with inputDelay zeros.
size_t i;
for (i = 0; i < BattleInput_inputDelay; i++)
BattleInputBuffer_push(bib, (BATTLE_INPUT_STATE) 0);
}
}
}
void
uninitBattleInputBuffers(void)
{
size_t player;
for (player = 0; player < NUM_PLAYERS; player++)
{
BattleInputBuffer *bib;
if (!(PlayerControl[player] & NETWORK_CONTROL))
continue;
bib = &battleInputBuffers[player];
BattleInputBuffer_uninit(bib);
}
}
// On error, returns false and sets errno.
bool
BattleInputBuffer_push(BattleInputBuffer *bib, BATTLE_INPUT_STATE input)
{
size_t next;
if (bib->size == bib->maxSize) {
// No more space.
log_add(log_Error, "NETPLAY: battleInputBuffer full.\n");
errno = ENOBUFS;
return false;
}
next = (bib->first + bib->size) % bib->maxSize;
bib->buf[next] = input;
bib->size++;
return true;
}
// On error, returns false and sets errno, and *input remains unchanged.
bool
BattleInputBuffer_pop(BattleInputBuffer *bib, BATTLE_INPUT_STATE *input)
{
if (bib->size == 0)
{
// Buffer is empty.
errno = EAGAIN;
return false;
}
*input = bib->buf[bib->first];
bib->first = (bib->first + 1) % bib->maxSize;
bib->size--;
return true;
}
BattleInputBuffer *
getBattleInputBuffer(size_t player) {
return &battleInputBuffers[player];
}
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETINPUT_H
#define _NETINPUT_H
#include "../controls.h"
// for BATTLE_INPUT_STATE
#include "../init.h"
// for NUM_PLAYERS
typedef struct BattleInputBuffer {
BATTLE_INPUT_STATE *buf;
// Cyclic buffer. if 'size' > 0, then 'first' is an index to
// the first used entry, 'size' is the number of used
// entries in the buffer, and (first + size) % maxSize is the
// index to just past the end of the buffer.
size_t maxSize;
size_t first;
size_t size;
} BattleInputBuffer;
void setBattleInputDelay(size_t delay);
size_t getBattleInputDelay(void);
void initBattleInputBuffers(void);
void uninitBattleInputBuffers(void);
bool BattleInputBuffer_push(BattleInputBuffer *bib,
BATTLE_INPUT_STATE input);
bool BattleInputBuffer_pop(BattleInputBuffer *bib,
BATTLE_INPUT_STATE *input);
BattleInputBuffer *getBattleInputBuffer(size_t player);
#endif /* _NETINPUT_H */
+557
View File
@@ -0,0 +1,557 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netmelee.h"
#include "libs/alarm.h"
#include "libs/callback.h"
#include "libs/log.h"
#include "libs/net.h"
#include "netplay/netinput.h"
#include "netplay/netmisc.h"
#include "netplay/netsend.h"
#include "netplay/notify.h"
#include "netplay/packetq.h"
#include "netplay/proto/npconfirm.h"
#include "netplay/proto/ready.h"
#include "build.h"
// for StarShipPlayer()
#include "controls.h"
// for BATTLE_INPUT_STATE
#include "init.h"
// for NUM_PLAYERS
#include "globdata.h"
// for GLOBAL
#include <errno.h>
#include <stdlib.h>
////////////////////////////////////////////////////////////////////////////
NetConnection *netConnections[NUM_PLAYERS];
size_t numNetConnections;
void
addNetConnection(NetConnection *conn, int playerNr) {
netConnections[playerNr] = conn;
numNetConnections++;
}
void
removeNetConnection(int playerNr) {
netConnections[playerNr] = NULL;
numNetConnections--;
}
void
closeAllConnections(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
if (netConnections[player] != NULL)
closePlayerNetworkConnection(player);
}
size_t
getNumNetConnections(void) {
return numNetConnections;
}
////////////////////////////////////////////////////////////////////////////
// Check the network connections for input.
void
netInput(void) {
uint32 timeoutMs = 0;
NetManager_process(&timeoutMs);
Alarm_process();
Callback_process();
}
void
netInputBlocking(uint32 timeoutMs) {
uint32 nextAlarmMs;
nextAlarmMs = Alarm_timeBeforeNextMs();
if (nextAlarmMs < timeoutMs)
timeoutMs = nextAlarmMs;
NetManager_process(&timeoutMs);
Alarm_process();
Callback_process();
}
////////////////////////////////////////////////////////////////////////////
// Send along all pending network packets.
void
flushPacketQueues(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn;
int flushStatus;
conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
flushStatus = flushPacketQueue(conn);
if (flushStatus == -1 && errno != EAGAIN)
closePlayerNetworkConnection(player);
}
}
void
confirmConnections(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
Netplay_confirm(conn);
}
}
void
cancelConfirmations(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
Netplay_cancelConfirmation(conn);
}
}
void
connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
Netplay_localReady(conn, callback, arg, true);
}
}
void
sendBattleInputConnections(BATTLE_INPUT_STATE input) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
Netplay_battleInput(conn, input);
}
}
#ifdef NETPLAY_CHECKSUM
void
sendChecksumConnections(uint32 frameNr, uint32 checksum) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
Netplay_sendChecksum(conn, frameNr, checksum);
}
}
#endif /* NETPLAY_CHECKSUM */
void
initBattleStateDataConnections(void) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
BattleStateData *battleStateData;
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
battleStateData =
(BattleStateData *) NetConnection_getStateData(conn);
battleStateData->endFrameCount = 0;
}
}
BATTLE_INPUT_STATE
networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr) {
BattleInputBuffer *bib = getBattleInputBuffer(player);
BATTLE_INPUT_STATE result;
for (;;) {
bool ok = BattleInputBuffer_pop(bib, &result);
// Get the input from the front of the
// buffer.
if (ok)
break;
{
NetConnection *conn = netConnections[player];
// First try whether there is incoming data, without blocking.
// If there isn't any, only then give a warning, and then
// block after all.
netInput();
if (!NetConnection_isConnected(conn))
{
// Connection aborted.
GLOBAL(CurrentActivity) |= CHECK_ABORT;
return(BATTLE_INPUT_STATE) 0;
}
#if 0
log_add(log_Warning, "NETPLAY: [%d] stalling for "
"network input. Increase the input delay if this "
"happens a lot.\n", player);
#endif
#define MAX_BLOCK_TIME 500
netInputBlocking(MAX_BLOCK_TIME);
if (!NetConnection_isConnected(conn))
{
// Connection aborted.
GLOBAL(CurrentActivity) |= CHECK_ABORT;
return(BATTLE_INPUT_STATE) 0;
}
}
}
(void) StarShipPtr;
return result;
}
NetConnection *
openPlayerNetworkConnection(COUNT player, void *extra) {
NetConnection *conn;
assert(netConnections[player] == NULL);
conn = NetConnection_open(player,
&netplayOptions.peer[player], NetMelee_connectCallback,
NetMelee_closeCallback, NetMelee_errorCallback, extra);
addNetConnection(conn, player);
return conn;
}
void
shutdownPlayerNetworkConnection(COUNT player) {
NetConnection_close(netConnections[player]);
}
void
closePlayerNetworkConnection(COUNT player) {
assert(netConnections[player] != NULL);
NetConnection_close(netConnections[player]);
removeNetConnection(player);
}
// If the callback function returns 'false', the function will immediately
// return with 'false'. Otherwise it will return 'true' after calling
// the callback function for each connected player.
bool
forAllConnectedPlayers(ForAllCallback callback, void *arg) {
COUNT player;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
if (!(*callback)(conn, arg))
return false;
}
return true;
}
bool
setupInputDelay(size_t localInputDelay) {
COUNT player;
size_t inputDelay = localInputDelay;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn))
continue;
if (NetConnection_getInputDelay(conn) > inputDelay)
inputDelay = NetConnection_getInputDelay(conn);
}
setBattleInputDelay(inputDelay);
return true;
}
static bool
sendInputDelayConnection(NetConnection *conn, const size_t *delay) {
Netplay_sendInputDelay(conn, *delay);
return true;
}
bool
sendInputDelayConnections(size_t delay) {
return forAllConnectedPlayers(
(ForAllCallback) sendInputDelayConnection, &delay);
}
static bool
setStateConnection(NetConnection *conn, const NetState *state) {
NetConnection_setState(conn, *state);
return true;
}
bool
setStateConnections(NetState state) {
return forAllConnectedPlayers(
(bool(*)(NetConnection *, void *)) setStateConnection, &state);
}
/////////////////////////////////////////////////////////////////////////////
typedef struct {
NetConnection_ReadyCallback readyCallback;
void *readyCallbackArg;
bool notifyRemote;
} LocalReadyConnectionArg;
static bool
localReadyConnection(NetConnection *conn, LocalReadyConnectionArg *arg) {
Netplay_localReady(conn, arg->readyCallback, arg->readyCallbackArg,
arg->notifyRemote);
return true;
}
bool
localReadyConnections(NetConnection_ReadyCallback readyCallback,
void *readyArg, bool notifyRemote) {
LocalReadyConnectionArg arg;
arg.readyCallback = readyCallback;
arg.readyCallbackArg = readyArg;
arg.notifyRemote = notifyRemote;
return forAllConnectedPlayers(
(bool(*)(NetConnection *, void *)) localReadyConnection, &arg);
}
/////////////////////////////////////////////////////////////////////////////
typedef struct NegotiateReadyState NegotiateReadyState;
struct NegotiateReadyState {
// Common fields of INPUT_STATE_DESC, from which this structure
// "inherits".
BOOLEAN(*InputFunc)(PVOID pInputState);
COUNT MenuRepeatDelay;
NetConnection *conn;
NetState nextState;
bool done;
};
static BOOLEAN
negotiateReadyInputFunc(NegotiateReadyState *state) {
#define NETWORK_POLL_DELAY (ONE_SECOND / 24)
netInputBlocking(NETWORK_POLL_DELAY);
// The timing out is necessary so that immediate key presses get
// handled while we wait. If we could do without the timeout,
// we wouldn't even need negotiateReadyInputFunc() and the
// DoInput() call.
// No need to call flushPacketQueues(); nothing needs to be sent
// right now.
if (!NetConnection_isConnected(state->conn))
return FALSE;
return !state->done;
}
// Called when both sides are ready
static void
negotiateReadyBothReadyCallback(NetConnection *conn, void *arg) {
NegotiateReadyState *state =(NegotiateReadyState *) arg;
NetConnection_setState(conn, state->nextState);
// This has to be done immediately, as more packets in the
// receive queue may be handled by the netInput() call that
// triggered this callback.
// This is the reason for the nextState argument to
// negotiateReady(); setting the state after the call to
// negotiateReady() would be too late.
state->done = true;
}
bool
negotiateReady(NetConnection *conn, bool notifyRemote, NetState nextState) {
NegotiateReadyState state;
state.InputFunc = (BOOLEAN(*)(void *)) negotiateReadyInputFunc;
state.MenuRepeatDelay = 0;
state.conn = conn;
state.nextState = nextState;
state.done = false;
Netplay_localReady(conn, negotiateReadyBothReadyCallback,
(void *) &state, notifyRemote);
flushPacketQueue(conn);
if (!state.done)
DoInput(&state, FALSE);
return NetConnection_isConnected(conn);
}
// Wait for all connections to get ready.
// XXX: Right now all connections are handled one by one. Handling them all
// at once would be faster but would require more work, which is
// not worth it as the time is minimal and this function is not
// time critical.
bool
negotiateReadyConnections(bool notifyRemote, NetState nextState) {
COUNT player;
size_t numDisconnected = 0;
for (player = 0; player < NUM_PLAYERS; player++)
{
NetConnection *conn = netConnections[player];
if (conn == NULL)
continue;
if (!NetConnection_isConnected(conn)) {
numDisconnected++;
continue;
}
negotiateReady(conn, notifyRemote, nextState);
}
return numDisconnected == 0;
}
typedef struct WaitReadyState WaitReadyState;
struct WaitReadyState {
// Common fields of INPUT_STATE_DESC, from which this structure
// "inherits".
BOOLEAN(*InputFunc)(PVOID pInputState);
COUNT MenuRepeatDelay;
NetConnection *conn;
NetConnection_ReadyCallback readyCallback;
void *readyCallbackArg;
bool done;
};
static void
waitReadyCallback(NetConnection *conn, void *arg) {
WaitReadyState *state =(WaitReadyState *) arg;
state->done = true;
// Call the original callback.
state->readyCallback(conn, state->readyCallbackArg);
}
static BOOLEAN
waitReadyInputFunc(WaitReadyState *state) {
netInputBlocking(NETWORK_POLL_DELAY);
// The timing out is necessary so that immediate key presses get
// handled while we wait. If we could do without the timeout,
// we wouldn't even need negotiateReadyInputFunc() and the
// DoInput() call.
// No need to call flushPacketQueues(); nothing needs to be sent
// right now.
if (!NetConnection_isConnected(state->conn))
return FALSE;
return !state->done;
}
bool
waitReady(NetConnection *conn) {
WaitReadyState state;
state.InputFunc =(BOOLEAN(*)(void *)) waitReadyInputFunc;
state.MenuRepeatDelay = 0;
state.conn = conn;
state.readyCallback = NetConnection_getReadyCallback(conn);
state.readyCallbackArg = NetConnection_getReadyCallbackArg(conn);
state.done = false;
NetConnection_setReadyCallback(conn, waitReadyCallback, (void *) &state);
DoInput(&state, FALSE);
return NetConnection_isConnected(conn);
}
////////////////////////////////////////////////////////////////////////////
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if !defined(_NETMELEE_H) && defined(NETPLAY)
#define _NETMELEE_H
#include "netplay.h"
#include "netinput.h"
#include "netconnection.h"
#include "../controls.h"
// for BATTLE_INPUT_STATE
extern struct NetConnection *netConnections[];
void addNetConnection(NetConnection *conn, int playerNr);
void removeNetConnection(int playerNr);
void closeAllConnections(void);
void closeAllConnections(void);
size_t getNumNetConnections(void);
void netInput(void);
void netInputBlocking(uint32 timeoutMs);
void flushPacketQueues(void);
void confirmConnections(void);
void cancelConfirmations(void);
void connectionsLocalReady(NetConnection_ReadyCallback callback, void *arg);
void sendBattleInputConnections(BATTLE_INPUT_STATE input);
void sendChecksumConnections(uint32 frameNr, uint32 checksum);
void initBattleStateDataConnections(void);
BATTLE_INPUT_STATE networkBattleInput(COUNT player, STARSHIPPTR StarShipPtr);
NetConnection *openPlayerNetworkConnection(COUNT player, void *extra);
void closePlayerNetworkConnection(COUNT player);
typedef bool(*ForAllCallback)(NetConnection *conn, void *arg);
bool forAllConnectedPlayers(ForAllCallback callback, void *arg);
bool setupInputDelay(size_t localInputDelay);
bool sendInputDelayConnections(size_t delay);
bool setStateConnections(NetState state);
bool localReadyConnections(NetConnection_ReadyCallback readyCallback,
void *arg, bool notifyRemote);
bool negotiateReady(NetConnection *conn, bool notifyRemote,
NetState nextState);
bool negotiateReadyConnections(bool notifyRemote, NetState nextState);
bool waitReady(NetConnection *conn);
#endif /* _NETMELEE_H */
+174
View File
@@ -0,0 +1,174 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netplay.h"
#include "netmisc.h"
#include "netmelee.h"
#include "packetsenders.h"
#include "proto/ready.h"
#include "melee.h"
// For feedback functions.
#include <stdlib.h>
static BattleStateData *BattleStateData_alloc(void);
static void BattleStateData_free(BattleStateData *battleStateData);
static inline BattleStateData *BattleStateData_new(
struct melee_state *meleeState,
struct getmelee_struct *getMeleeState);
static void BattleStateData_delete(BattleStateData *battleStateData);
static BattleStateData *
BattleStateData_alloc(void) {
return (BattleStateData *) malloc(sizeof (BattleStateData));
}
static void
BattleStateData_free(BattleStateData *battleStateData) {
free(battleStateData);
}
static inline BattleStateData *
BattleStateData_new(struct melee_state *meleeState,
struct getmelee_struct *getMeleeState) {
BattleStateData *battleStateData = BattleStateData_alloc();
battleStateData->releaseFunction =
(NetConnectionStateData_ReleaseFunction) BattleStateData_delete;
battleStateData->meleeState = meleeState;
battleStateData->getMeleeState = getMeleeState;
return battleStateData;
}
static void
BattleStateData_delete(BattleStateData *battleStateData) {
BattleStateData_free(battleStateData);
}
////////////////////////////////////////////////////////////////////////////
static void NetMelee_enterState_inSetup(NetConnection *conn, void *arg);
// Called when a connection has been established.
void
NetMelee_connectCallback(NetConnection *conn) {
BattleStateData *battleStateData;
struct melee_state *meleeState;
meleeState = (struct melee_state *) NetConnection_getExtra(conn);
battleStateData = BattleStateData_new(meleeState, NULL);
NetConnection_setStateData(conn, (void *) battleStateData);
NetConnection_setExtra(conn, NULL);
sendInit(conn);
Netplay_localReady (conn, NetMelee_enterState_inSetup, NULL, false);
}
// Called when a connection is closed.
void
NetMelee_closeCallback(NetConnection *conn) {
struct melee_state *meleeState;
if (NetConnection_getState(conn) > NetState_connecting) {
BattleStateData *battleStateData =
(BattleStateData *) NetConnection_getStateData(conn);
meleeState = battleStateData->meleeState;
} else {
meleeState = (struct melee_state *) NetConnection_getExtra(conn);
}
closeFeedback(meleeState, NetConnection_getPlayerNr(conn));
}
// Called when a network error occurs during connect.
void
NetMelee_errorCallback(NetConnection *conn,
const NetConnectionError *error) {
void *meleeState;
meleeState = NetConnection_getExtra(conn);
errorFeedback(meleeState, NetConnection_getPlayerNr(conn));
(void) error;
}
// Callback function for when both sides have finished initialisation after
// initial connect.
static void
NetMelee_enterState_inSetup(NetConnection *conn, void *arg) {
BattleStateData *battleStateData;
struct melee_state *meleeState;
int player;
NetConnection_setState(conn, NetState_inSetup);
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
meleeState = battleStateData->meleeState;
player = NetConnection_getPlayerNr(conn);
connectedFeedback(meleeState, player);
entireFleetChanged(meleeState, player);
teamStringChanged(meleeState, player);
flushPacketQueues();
(void) arg;
}
// Callback function for when both sides have confirmed that the battle
// has ended.
void
NetMelee_reenterState_inSetup(NetConnection *conn) {
BattleStateData *battleStateData;
struct melee_state *meleeState;
NetConnection_setState(conn, NetState_inSetup);
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
meleeState = battleStateData->meleeState;
// The player who entered the menu first should send his changes over
// to the other side when the other player enters setup too.
// As myTurn is set when a change is made, and will never be unset
// until the other side makes a change, it can be used to determine
// which side was first. In the case there were no changes made, myTurn
// may be the second player, but in this case it doesn't matter
// who sends what to whom.
if (NetConnection_isMyTurn(conn))
{
size_t side;
for (side = 0; side < NUM_SIDES; side++)
{
entireFleetChanged(meleeState, side);
teamStringChanged(meleeState, side);
}
flushPacketQueues();
}
}
+70
View File
@@ -0,0 +1,70 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETMISC_H
#define _NETMISC_H
typedef struct BattleStateData BattleStateData;
#include "netconnection.h"
#include "netstate.h"
#include "types.h"
#include "battle.h"
// for BattleFrameCounter
struct BattleStateData {
NETCONNECTION_STATE_DATA_COMMON
struct melee_state *meleeState;
struct getmelee_struct *getMeleeState;
BattleFrameCounter endFrameCount;
};
void NetMelee_connectCallback(NetConnection *conn);
void NetMelee_closeCallback(NetConnection *conn);
void NetMelee_errorCallback(NetConnection *conn,
const NetConnectionError *error);
void NetMelee_reenterState_inSetup(NetConnection *conn);
// Returns true iff the connection is in a state where the confirmation
// handshake is meaningful. Right now this is only when we're in the
// pre-game setup menu.
static inline bool
handshakeMeaningful(NetState state) {
return state == NetState_inSetup;
}
static inline bool
readyFlagsMeaningful(NetState state) {
return state == NetState_init ||
state == NetState_preBattle ||
state == NetState_interBattle ||
state == NetState_inBattle ||
state == NetState_endingBattle ||
state == NetState_endingBattle2 ||
state == NetState_endMelee;
}
#endif /* _NETMISC_H */
+39
View File
@@ -0,0 +1,39 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netoptions.h"
NetplayOptions netplayOptions = {
/* .metaServer = */ "uqm.stack.nl",
/* .metaPort = */ "21836",
/* .peer = */ {
/* [0] Player 1 (bottom) */ {
/* .isServer = */ true,
/* .host = */ "localhost",
/* .port = */ "21837" /* 0x554d - "UM" */,
},
/* [1] Player 2 (top) */ {
/* .isServer = */ true,
/* .host = */ "localhost",
/* .port = */ "21837" /* 0x554d - "UM" */,
},
},
/* .inputDelay = */ 0,
};
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETOPTIONS_H
#define _NETOPTIONS_H
#include "types.h"
#include <stddef.h>
#define NETPLAY_NUM_PLAYERS 2
// Not using NUM_PLAYERS because that would mean we'd have
// to include init.h, and all that comes with it.
// XXX: Don't use a hardcoded limit.
typedef struct {
bool isServer;
const char *host;
const char *port;
// May be given as a service name.
} NetplayPeerOptions;
typedef struct {
const char *metaServer;
const char *metaPort;
// May be given as a service name.
NetplayPeerOptions peer[NETPLAY_NUM_PLAYERS];
size_t inputDelay;
} NetplayOptions;
extern NetplayOptions netplayOptions;
#endif /* _NETOPTIONS_H */
+54
View File
@@ -0,0 +1,54 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if !defined(_NETPLAY_H) && defined(NETPLAY)
#define _NETPLAY_H
// NETPLAY can either be unset (in which case we will never get here)
// NETPLAY_FULL, or NETPLAY_IPV4 (disables IPv6)
#define NETPLAY_IPV4 1
#define NETPLAY_FULL 2
#define NETPLAY_PROTOCOL_VERSION_MAJOR 0
#define NETPLAY_PROTOCOL_VERSION_MINOR 1
#define NETPLAY_DEBUG
/* Extra debugging for netplay */
#define NETPLAY_STATISTICS
/* Keep some statistics */
#define NETPLAY_CHECKSUM
/* Send/process checksums to verify that both sides of a network
* connection are still in sync.
* If not enabled, incoming checksum packets will be ignored.
* TODO: make compilation of crc.c and checksum.c conditional. */
#define NETPLAY_CHECKSUM_INTERVAL 1
/* If NETPLAY_CHECKSUM is defined, this define determines
* every how many frames a checksum packet is sent. */
#define NETPLAY_READBUFSIZE 2048
#define NETPLAY_CONNECTTIMEOUT 2000
/* Time to wait for a connect() to succeed. In ms. */
//#define NETPLAY_LISTENTIMEOUT 30000
// /* Time to wait for a listen() to succeed. In ms. */
#define NETPLAY_RETRYDELAY 2000
/* Time to wait after all addresses of a host have been tried
* before starting retrying them all. In ms. */
#endif /* _NETPLAY_H */
+189
View File
@@ -0,0 +1,189 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "port.h"
#include "netconnection.h"
#include "netrcv.h"
#include "packet.h"
#include "types.h"
#include "libs/log.h"
#include <errno.h>
#include <string.h>
// Try to get a single packet from a stream of data.
// Returns 0 if the packet was successfully processed, or
// -1 on an error, in which case the state is unchanged.
static ssize_t
dataReceivedSingle(NetConnection *conn, const uint8 *data,
size_t dataLen) {
uint32 packetLen;
PacketType type;
int result;
if (dataLen < sizeof (PacketHeader)) {
// Incomplete packet. We'll have to wait for the rest.
return 0;
}
packetLen = packetLength((const Packet *) data);
type = packetType((const Packet *) data);
if (!validPacketType(type)) {
log_add(log_Warning, "Packet with invalid type %d received.\n", type);
errno = EBADMSG;
return -1;
}
if (packetLen < packetTypeData[type].len) {
// Bad len field of packet.
log_add(log_Warning, "Packet with bad length field received (type="
"%s, lenfield=%d.\n", packetTypeData[type].name,
packetLen);
errno = EBADMSG;
return -1;
}
if (dataLen < packetLen) {
// Incomplete packet. We'll have to wait for the rest.
return 0;
}
#ifdef NETPLAY_STATISTICS
NetConnection_getStatistics(conn)->packetsReceived++;
NetConnection_getStatistics(conn)->packetTypeReceived[type]++;
#endif
#ifdef NETPLAY_DEBUG
if (type != PACKET_BATTLEINPUT && type != PACKET_CHECKSUM) {
// Reporting BattleInput and Checksum would get so spammy that it
// would slow down the battle.
log_add(log_Debug, "NETPLAY: [%d] <== Received packet of type %s.\n",
NetConnection_getPlayerNr(conn), packetTypeData[type].name);
}
#endif
result = packetTypeData[type].handler(conn, data);
if (result == -1) {
// An error occured. errno is set by the handler.
return -1;
}
return packetLen;
}
// Try to get all the packets from a stream of data.
// Returns the number of bytes processed.
static ssize_t
dataReceivedMulti(NetConnection *conn, const uint8 *data, size_t len) {
size_t processed;
processed = 0;
while (len > 0) {
ssize_t packetLen = dataReceivedSingle(conn, data, len);
if (packetLen == -1) {
// Bad packet. Errno is set.
return -1;
}
if (packetLen == 0) {
// No packet was processed. This means that no complete
// packet arrived.
break;
}
processed += packetLen;
data += packetLen;
len -= packetLen;
}
return processed;
}
// Returns -1 on error (setting errno), or 0 if everything went
// ok, regardless of whether any packets were actually processed.
void
dataReadyCallback(NetDescriptor *nd) {
NetConnection *conn = (NetConnection *) NetDescriptor_getExtra(nd);
Socket *socket = NetDescriptor_getSocket(nd);
for (;;) {
ssize_t numRead;
ssize_t numProcessed;
numRead = Socket_recv(socket, conn->readEnd,
NETPLAY_READBUFSIZE - (conn->readEnd - conn->readBuf), 0);
if (numRead == 0) {
// Other side closed the connection.
NetDescriptor_close(nd);
return;
}
if (numRead == -1) {
switch (errno) {
case EAGAIN: // No more data for now.
return;
case EINTR: // System call was interrupted. Retry.
continue;
default: {
int savedErrno = errno;
log_add(log_Error, "recv() failed: %s.\n",
strerror(errno));
NetConnection_doErrorCallback(conn, savedErrno);
NetDescriptor_close(nd);
return;
}
}
}
conn->readEnd += numRead;
numProcessed = dataReceivedMulti(conn, conn->readBuf,
conn->readEnd - conn->readBuf);
if (numProcessed == -1) {
// An error occured during processing.
// errno is set.
NetConnection_doErrorCallback(conn, errno);
NetDescriptor_close(nd);
return;
}
if (numProcessed == 0) {
// No packets could be processed. This means we need to receive
// more data first.
return;
}
// Some packets have been processed.
// We more any rest to the front of the buffer, to make room
// for more data.
// A cyclic buffer would obviate the need for this move,
// but it would complicate things a lot.
memmove(conn->readBuf, conn->readBuf + numProcessed,
(conn->readEnd - conn->readBuf) - numProcessed);
conn->readEnd -= numProcessed;
}
}
+27
View File
@@ -0,0 +1,27 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETRCV_H
#define _NETRCV_H
#include "libs/net.h"
void dataReadyCallback(NetDescriptor *nd);
#endif /* _NETRCV_H */
+87
View File
@@ -0,0 +1,87 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "port.h"
#include "netconnection.h"
#include "packet.h"
#include "libs/log.h"
#include "libs/net.h"
#include <assert.h>
#include <errno.h>
#include <string.h>
int
sendPacket(NetConnection *conn, Packet *packet) {
ssize_t sendResult;
size_t len;
Socket *socket;
assert(NetConnection_isConnected(conn));
#ifdef NETPLAY_DEBUG
//if (packetType(packet) != PACKET_BATTLEINPUT &&
// packetType(packet) != PACKET_CHECKSUM) {
// // Reporting BattleInput or Checksum would get so spammy that it
// // would slow down the battle.
// log_add(log_Debug, "NETPLAY: [%d] ==> Sending packet of type %s.\n",
// conn->player, packetTypeData[packetType(packet)].name);
//}
#endif
socket = NetDescriptor_getSocket(conn->nd);
len = packetLength(packet);
while (len > 0) {
sendResult = Socket_send(socket, (void *) packet, len, 0);
if (sendResult >= 0) {
len -= sendResult;
continue;
}
switch (errno) {
case EINTR: // System call interrupted, retry;
continue;
case ECONNRESET: { // Connection reset by peer.
// keep errno
return -1;
}
default: {
// Should not happen.
int savedErrno = errno;
log_add(log_Error, "send() failed: %s.\n", strerror(errno));
errno = savedErrno;
return -1;
}
}
}
#ifdef NETPLAY_STATISTICS
NetConnection_getStatistics(conn)->packetsSent++;
NetConnection_getStatistics(conn)->packetTypeSent[packetType(packet)]++;
#endif
return 0;
}
+28
View File
@@ -0,0 +1,28 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETSEND_H
#define _NETSEND_H
#include "packet.h"
int sendPacket(NetConnection *conn, Packet *packet);
#endif /* _NETSEND_H */
+49
View File
@@ -0,0 +1,49 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netplay.h"
#include "netstate.h"
#include <assert.h>
#include <stdlib.h>
#define DEFINE_NETSTATEDATA(stateName) \
{ \
/* .name = */ #stateName, \
}
NetStateData netStateData[] = {
DEFINE_NETSTATEDATA(unconnected),
DEFINE_NETSTATEDATA(connecting),
DEFINE_NETSTATEDATA(init),
DEFINE_NETSTATEDATA(inSetup),
DEFINE_NETSTATEDATA(preBattle),
DEFINE_NETSTATEDATA(interBattle),
DEFINE_NETSTATEDATA(selectShip),
DEFINE_NETSTATEDATA(inBattle),
DEFINE_NETSTATEDATA(endingBattle),
DEFINE_NETSTATEDATA(endingBattle2),
DEFINE_NETSTATEDATA(endMelee),
};
void
NetConnectionStateData_release(NetConnectionStateData *stateData) {
assert(stateData->releaseFunction != NULL);
stateData->releaseFunction(stateData);
}
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NETSTATE_H
#define _NETSTATE_H
#include "port.h"
typedef struct NetConnectionStateData NetConnectionStateData;
typedef enum {
NetState_unconnected, /* No connection initiated */
NetState_connecting, /* Connection being setup */
NetState_init, /* Initialising the connection */
NetState_inSetup, /* In the network game setup */
NetState_preBattle, /* Pre-battle initialisations */
NetState_interBattle, /* Negotiations between battles. */
NetState_selectShip, /* Selecting a ship in battle */
NetState_inBattle, /* Battle has started */
NetState_endingBattle, /* Both sides are prepared to end */
NetState_endingBattle2, /* Waiting for the final synchronisation */
NetState_endMelee, /* Melee ended; remote is not yet in setup */
} NetState;
#include "types.h"
typedef struct {
const char *name;
} NetStateData;
extern NetStateData netStateData[];
typedef void (*NetConnectionStateData_ReleaseFunction)(
NetConnectionStateData *stateData);
#define NETCONNECTION_STATE_DATA_COMMON \
NetConnectionStateData_ReleaseFunction releaseFunction;
struct
NetConnectionStateData {
NETCONNECTION_STATE_DATA_COMMON
};
void NetConnectionStateData_release(NetConnectionStateData *stateData);
static inline bool
NetState_battleActive(NetState state) {
return state == NetState_inBattle || state == NetState_endingBattle ||
state == NetState_endingBattle2;
}
#endif /* _NETSTATE_H */
+117
View File
@@ -0,0 +1,117 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
// This files contains functions that notify the other side of local
// changes.
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "notify.h"
#include "packetsenders.h"
// Convert a local player number to a side indication relative to this
// party.
static inline NetplaySide
netSide(NetConnection *conn, int side) {
if (side == conn->player)
return NetplaySide_remote;
return NetplaySide_local;
}
void
Netplay_selectShip(NetConnection *conn, uint16 index) {
assert(NetConnection_getState(conn) == NetState_selectShip);
sendSelectShip(conn, index);
}
void
Netplay_battleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
assert(NetConnection_getState(conn) == NetState_inBattle ||
NetConnection_getState(conn) == NetState_endingBattle ||
NetConnection_getState(conn) == NetState_endingBattle2);
sendBattleInput(conn, input);
}
void
Netplay_teamStringChanged(NetConnection *conn, int player,
const char *name, size_t len) {
assert(NetConnection_getState(conn) == NetState_inSetup);
assert(!conn->stateFlags.handshake.localOk);
sendTeamString(conn, netSide(conn, player), name, len);
}
// On initialisation, or load.
void
Netplay_entireFleetChanged(NetConnection *conn, int player,
const BYTE *fleet, size_t fleetSize) {
assert(NetConnection_getState(conn) == NetState_inSetup);
assert(!conn->stateFlags.handshake.localOk);
sendFleet(conn, netSide(conn, player), fleet, fleetSize);
}
void
Netplay_fleetShipChanged(NetConnection *conn, int player,
size_t index, BYTE ship) {
assert(NetConnection_getState(conn) == NetState_inSetup);
assert(!conn->stateFlags.handshake.localOk);
sendFleetShip(conn, netSide(conn, player), index, ship);
}
void
Netplay_seedRandom(NetConnection *conn, uint32 seed) {
assert(NetConnection_getState(conn) == NetState_preBattle);
sendSeedRandom(conn, seed);
conn->stateFlags.agreement.randomSeed = true;
}
void
Netplay_sendInputDelay(NetConnection *conn, uint32 delay) {
assert(NetConnection_getState(conn) == NetState_preBattle);
sendInputDelay(conn, delay);
}
void
Netplay_sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
assert(NetConnection_getState(conn) == NetState_endingBattle);
sendFrameCount(conn, frameCount);
}
#ifdef NETPLAY_CHECKSUM
void
Netplay_sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Checksum checksum) {
assert(NetState_battleActive(NetConnection_getState(conn)));
sendChecksum(conn, frameNr, checksum);
}
#endif /* NETPLAY_CHECKSUM */
+50
View File
@@ -0,0 +1,50 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NOTIFY_H
#define _NOTIFY_H
#include "netplay.h"
// for NETPLAY_CHECKSUM
#include "netconnection.h"
#include "controls.h"
// for BATTLE_INPUT_STATE
#ifdef NETPLAY_CHECKSUM
# include "checksum.h"
#endif
void Netplay_selectShip(NetConnection *conn, uint16 index);
void Netplay_battleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
void Netplay_teamStringChanged(NetConnection *conn, int player,
const char *name, size_t len);
void Netplay_entireFleetChanged(NetConnection *conn, int player,
const BYTE *fleet, size_t fleetSize);
void Netplay_fleetShipChanged(NetConnection *conn, int player,
size_t index, BYTE ship);
void Netplay_seedRandom(NetConnection *conn, uint32 seed);
void Netplay_sendInputDelay(NetConnection *conn, uint32 delay);
void Netplay_sendFrameCount(NetConnection *conn,
BattleFrameCounter frameCount);
#ifdef NETPLAY_CHECKSUM
void Netplay_sendChecksum(NetConnection *conn,
BattleFrameCounter frameCount, Checksum checksum);
#endif
#endif /* _NOTIFY_H */
+257
View File
@@ -0,0 +1,257 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netplay.h"
#include "packet.h"
#include "uqmversion.h"
#include "netrcv.h"
#include "packethandlers.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define DEFINE_PACKETDATA(name, inTurn) \
{ \
/* .len = */ sizeof (Packet_##name), \
/* .handler = */ (PacketHandler) PacketHandler_##name, \
/* .name = */ #name, \
/* .inTurn = */ (inTurn) \
}
PacketTypeData packetTypeData[PACKET_NUM] = {
DEFINE_PACKETDATA(Init, false),
DEFINE_PACKETDATA(Ping, false),
DEFINE_PACKETDATA(Ack, false),
DEFINE_PACKETDATA(EndTurn, false),
DEFINE_PACKETDATA(Ready, false),
DEFINE_PACKETDATA(Fleet, true),
DEFINE_PACKETDATA(TeamName, true),
DEFINE_PACKETDATA(Handshake0, false),
DEFINE_PACKETDATA(Handshake1, false),
DEFINE_PACKETDATA(HandshakeCancel, false),
DEFINE_PACKETDATA(HandshakeCancelAck, false),
DEFINE_PACKETDATA(SeedRandom, false),
DEFINE_PACKETDATA(InputDelay, false),
DEFINE_PACKETDATA(SelectShip, false),
DEFINE_PACKETDATA(BattleInput, false),
DEFINE_PACKETDATA(FrameCount, false),
DEFINE_PACKETDATA(Checksum, false),
};
static inline void *
Packet_alloc(size_t size) {
return malloc(size);
}
static Packet *
Packet_create(PacketType type, size_t extraSize) {
Packet *result;
size_t len;
// Alignment requirement.
assert(extraSize % 4 == 0);
len = packetTypeData[type].len + extraSize;
result = Packet_alloc(len);
result->header.len = hton16((uint16) len);
result->header.type = hton16((uint16) type);
return result;
}
void
Packet_delete(Packet *packet) {
free(packet);
}
Packet_Init *
Packet_Init_create(void) {
Packet_Init *packet = (Packet_Init *) Packet_create(PACKET_INIT, 0);
packet->protoVersion.major = NETPLAY_PROTOCOL_VERSION_MAJOR;
packet->protoVersion.minor = NETPLAY_PROTOCOL_VERSION_MINOR;
packet->padding0 = 0;
packet->uqmVersion.major = UQM_MAJOR_VERSION;
packet->uqmVersion.minor = UQM_MINOR_VERSION;
packet->uqmVersion.patch = UQM_PATCH_VERSION;
packet->padding1 = 0;
return packet;
}
Packet_Ping *
Packet_Ping_create(uint32 id) {
Packet_Ping *packet = (Packet_Ping *) Packet_create(PACKET_PING, 0);
packet->id = hton32(id);
return packet;
}
Packet_Ack *
Packet_Ack_create(uint32 id) {
Packet_Ack *packet = (Packet_Ack *) Packet_create(PACKET_ACK, 0);
packet->id = hton32(id);
return packet;
}
Packet_EndTurn *
Packet_EndTurn_create(void) {
Packet_EndTurn *packet =
(Packet_EndTurn *) Packet_create(PACKET_ENDTURN, 0);
return packet;
}
Packet_Ready *
Packet_Ready_create(void) {
Packet_Ready *packet = (Packet_Ready *) Packet_create(PACKET_READY, 0);
return packet;
}
// The fleet itself still needs to be filled by the caller.
// This function takes care of the necessary padding; it is allocated,
// and filled with zero chars.
Packet_Fleet *
Packet_Fleet_create(NetplaySide side, size_t numShips) {
Packet_Fleet *packet;
size_t fleetSize;
size_t extraSize;
fleetSize = numShips * sizeof (FleetEntry);
extraSize = (fleetSize + 3) & ~0x03;
packet = (Packet_Fleet *) Packet_create(PACKET_FLEET, extraSize);
packet->side = (uint8) side;
packet->padding = 0;
packet->numShips = hton16((uint16) numShips);
memset((char *) packet + sizeof (Packet_Fleet) + fleetSize,
'\0', extraSize - fleetSize);
return packet;
}
// 'size' is the number of bytes (not characters) in 'name', excluding
// a possible terminating '\0'. A '\0' will be included in the packet though.
// This function takes care of the required padding.
Packet_TeamName *
Packet_TeamName_create(NetplaySide side, const char *name, size_t size) {
Packet_TeamName *packet;
size_t extraSize;
extraSize = ((size + 1) + 3) & ~0x03;
// The +1 is for the '\0'.
packet = (Packet_TeamName *) Packet_create(PACKET_TEAMNAME, extraSize);
packet->side = (uint8) side;
packet->padding = 0;
memcpy(packet->name, name, size);
memset((char *) packet + sizeof (Packet_TeamName) + size, '\0',
extraSize - size);
// This takes care of the terminating '\0', as well as the
// padding.
return packet;
}
Packet_Handshake0 *
Packet_Handshake0_create(void) {
Packet_Handshake0 *packet =
(Packet_Handshake0 *) Packet_create(PACKET_HANDSHAKE0, 0);
return packet;
}
Packet_Handshake1 *
Packet_Handshake1_create(void) {
Packet_Handshake1 *packet =
(Packet_Handshake1 *) Packet_create(PACKET_HANDSHAKE1, 0);
return packet;
}
Packet_HandshakeCancel *
Packet_HandshakeCancel_create(void) {
Packet_HandshakeCancel *packet =
(Packet_HandshakeCancel *) Packet_create(
PACKET_HANDSHAKECANCEL, 0);
return packet;
}
Packet_HandshakeCancelAck *
Packet_HandshakeCancelAck_create(void) {
Packet_HandshakeCancelAck *packet =
(Packet_HandshakeCancelAck *) Packet_create(
PACKET_HANDSHAKECANCELACK, 0);
return packet;
}
Packet_SeedRandom *
Packet_SeedRandom_create(uint32 seed) {
Packet_SeedRandom *packet =
(Packet_SeedRandom *) Packet_create(PACKET_SEEDRANDOM, 0);
packet->seed = hton32(seed);
return packet;
}
Packet_InputDelay *
Packet_InputDelay_create(uint32 delay) {
Packet_InputDelay *packet =
(Packet_InputDelay *) Packet_create(PACKET_INPUTDELAY, 0);
packet->delay = hton32(delay);
return packet;
}
Packet_SelectShip *
Packet_SelectShip_create(uint16 ship) {
Packet_SelectShip *packet =
(Packet_SelectShip *) Packet_create(PACKET_SELECTSHIP, 0);
packet->ship = hton16(ship);
packet->padding = 0;
return packet;
}
Packet_BattleInput *
Packet_BattleInput_create(uint8 state) {
Packet_BattleInput *packet =
(Packet_BattleInput *) Packet_create(PACKET_BATTLEINPUT, 0);
packet->state = (uint8) state;
packet->padding0 = 0;
packet->padding1 = 0;
return packet;
}
Packet_FrameCount *
Packet_FrameCount_create(uint32 frameCount) {
Packet_FrameCount *packet =
(Packet_FrameCount *) Packet_create(PACKET_FRAMECOUNT, 0);
packet->frameCount = hton32(frameCount);
return packet;
}
Packet_Checksum *
Packet_Checksum_create(uint32 frameNr, uint32 checksum) {
Packet_Checksum *packet =
(Packet_Checksum *) Packet_create(PACKET_CHECKSUM, 0);
packet->frameNr = hton32(frameNr);
packet->checksum = hton32(checksum);
return packet;
}
+261
View File
@@ -0,0 +1,261 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PACKET_H
#define _PACKET_H
typedef struct Packet Packet;
typedef enum PacketType {
PACKET_INIT,
PACKET_PING,
PACKET_ACK,
PACKET_ENDTURN,
PACKET_READY,
PACKET_FLEET,
PACKET_TEAMNAME,
PACKET_HANDSHAKE0,
PACKET_HANDSHAKE1,
PACKET_HANDSHAKECANCEL,
PACKET_HANDSHAKECANCELACK,
PACKET_SEEDRANDOM,
PACKET_INPUTDELAY,
PACKET_SELECTSHIP,
PACKET_BATTLEINPUT,
PACKET_FRAMECOUNT,
PACKET_CHECKSUM,
PACKET_NUM, /* Number of packet types */
} PacketType;
#include "netconnection.h"
#include "types.h"
#include "libs/network/bytesex.h"
/* NB: These handlers are expected not to modify the state if an
* error occurs.
* When a handler is called, it has already been validated that the
* a complete packet has arrived.
*/
typedef int (*PacketHandler)(NetConnection *conn, const void *packet);
typedef struct {
size_t len; /* Minimal length of a packet of this type */
PacketHandler handler;
const char *name;
bool inTurn; /* Can only be sent if it's this party's turn. */
} PacketTypeData;
extern PacketTypeData packetTypeData[];
// When adding new packets, be sure to have all the fields properly aligned,
// and that the size of a packet is a multiple of 4 bytes in length.
// Fields should be no longer than 4 bytes in themselves, as larger
// fields may require a larger alignment.
typedef struct {
uint16 len;
uint16 type; /* Actually of type PacketType */
} PacketHeader;
// "Base class" for all packets.
struct Packet {
PacketHeader header;
};
static inline size_t
packetLength(const Packet *packet) {
return (size_t) ntoh16(packet->header.len);
}
static inline PacketType
packetType(const Packet *packet) {
return (PacketType) ntoh16(packet->header.type);
}
static inline bool
validPacketType(PacketType type) {
return type < PACKET_NUM;
}
typedef struct {
PacketHeader header;
struct {
uint8 major;
uint8 minor;
} protoVersion; /* Protocol version */
uint16 padding0; /* Set to 0 */
struct {
uint8 major;
uint8 minor;
uint8 patch;
} uqmVersion; /* Protocol version */
uint8 padding1; /* Set to 0 */
} Packet_Init;
typedef struct {
PacketHeader header;
uint32 id;
} Packet_Ping;
// Acknowldement of a Ping.
typedef struct {
PacketHeader header;
uint32 id;
} Packet_Ack;
// Used to ask or confirm a turn change.
typedef struct {
PacketHeader header;
// No contents.
} Packet_EndTurn;
// Used to signal that a party is ready to continue.
typedef struct {
PacketHeader header;
// No contents.
} Packet_Ready;
typedef struct {
PacketHeader header;
uint32 seed;
} Packet_SeedRandom;
typedef struct {
PacketHeader header;
uint32 delay;
} Packet_InputDelay;
// This enum is used to indicate that a packet containing it relates to
// either the local or the remote player, from the perspective of the
// sender of the message;
typedef enum {
NetplaySide_local,
NetplaySide_remote
} NetplaySide;
typedef struct {
uint8 index; /* Position in the fleet */
uint8 ship; /* Ship type index */
} FleetEntry;
// Structure describing an update to a player's fleet.
// TODO: use strings as ship identifiers, instead of numbers,
// so that adding of new ships doesn't break this.
typedef struct {
PacketHeader header;
uint8 side;
uint8 padding;
uint16 numShips;
FleetEntry ships[];
// Be sure to add padding to this structure to make it a multiple of
// 4 bytes in length.
} Packet_Fleet;
typedef struct {
PacketHeader header;
uint8 side;
uint8 padding;
uint8 name[];
// '\0' terminated.
// Be sure to add padding to this structure to make it a multiple of
// 4 bytes in length.
} Packet_TeamName;
typedef struct {
PacketHeader header;
// No contents.
} Packet_Handshake0;
typedef struct {
PacketHeader header;
// No contents.
} Packet_Handshake1;
typedef struct {
PacketHeader header;
// No contents.
} Packet_HandshakeCancel;
typedef struct {
PacketHeader header;
// No contents.
} Packet_HandshakeCancelAck;
typedef struct {
PacketHeader header;
uint16 ship;
// The value '(uint16) ~0' indicates random selection.
uint16 padding;
} Packet_SelectShip;
typedef enum {
PK_THRUST,
PK_LEFT,
PK_RIGHT,
PK_FIRE,
PK_SPECIAL,
PK_NUM
} PacketKey;
typedef struct {
PacketHeader header;
uint8 state; /* Actually BATTLE_INPUT_STATE */
uint8 padding0;
uint16 padding1;
} Packet_BattleInput;
typedef struct {
PacketHeader header;
uint32 frameCount; /* Actually BattleFrameCounter */
} Packet_FrameCount;
typedef struct {
PacketHeader header;
uint32 frameNr; /* Actually BattleFrameCounter */
uint32 checksum; /* Actually Checksum */
} Packet_Checksum;
void Packet_delete(Packet *packet);
Packet_Init *Packet_Init_create(void);
Packet_Ping *Packet_Ping_create(uint32 id);
Packet_Ack *Packet_Ack_create(uint32 id);
Packet_EndTurn *Packet_EndTurn_create(void);
Packet_Ready *Packet_Ready_create(void);
Packet_Handshake0 *Packet_Handshake0_create(void);
Packet_Handshake1 *Packet_Handshake1_create(void);
Packet_HandshakeCancel *Packet_HandshakeCancel_create(void);
Packet_HandshakeCancelAck *Packet_HandshakeCancelAck_create(void);
Packet_SeedRandom *Packet_SeedRandom_create(uint32 seed);
Packet_InputDelay *Packet_InputDelay_create(uint32 delay);
Packet_Fleet *Packet_Fleet_create(NetplaySide side, size_t numShips);
Packet_TeamName *Packet_TeamName_create(NetplaySide side, const char *name,
size_t size);
Packet_SelectShip *Packet_SelectShip_create(uint16 ship);
Packet_BattleInput *Packet_BattleInput_create(uint8 state);
Packet_FrameCount *Packet_FrameCount_create(uint32 frameCount);
Packet_Checksum *Packet_Checksum_create(uint32 frameNr, uint32 checksum);
#endif /* _PACKET_H */
+520
View File
@@ -0,0 +1,520 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define PORT_WANT_ERRNO
#include "port.h"
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "packethandlers.h"
#include "netinput.h"
#include "netmisc.h"
#include "packetsenders.h"
#include "proto/npconfirm.h"
#include "proto/ready.h"
#include "libs/log.h"
#include "controls.h"
// for BATTLE_INPUT_STATE
#include "init.h"
// for NUM_PLAYERS
#include "melee.h"
// for various update functions.
#include "pickmele.h"
// for various update functions.
#include "libs/mathlib.h"
// for TFB_SeedRandom
#include <errno.h>
static bool
testNetState(bool condition, PacketType type) {
if (!condition) {
log_add(log_Error, "Packet of type '%s' received from wrong "
"state.\n", packetTypeData[type].name);
errno = EBADMSG;
}
return condition;
}
int
PacketHandler_Init(NetConnection *conn, const Packet_Init *packet) {
if (!testNetState(conn->state == NetState_init &&
!conn->stateFlags.ready.remoteReady, PACKET_INIT))
return -1; // errno is set
if (packet->protoVersion.major != NETPLAY_PROTOCOL_VERSION_MAJOR ||
packet->protoVersion.minor != NETPLAY_PROTOCOL_VERSION_MINOR) {
log_add(log_Error, "Protocol version %d.%d not supported.\n",
packet->protoVersion.major, packet->protoVersion.minor);
errno = ENOSYS;
return -1;
}
Netplay_remoteReady(conn);
return 0;
}
int
PacketHandler_Ping(NetConnection *conn, const Packet_Ping *packet) {
if (!testNetState(conn->state > NetState_init, PACKET_PING))
return -1; // errno is set
sendAck(conn, packet->id);
return 0;
}
int
PacketHandler_Ack(NetConnection *conn, const Packet_Ack *packet) {
if (!testNetState(conn->state > NetState_init, PACKET_ACK))
return -1; // errno is set
(void) conn;
(void) packet;
return 0;
}
int
PacketHandler_EndTurn(NetConnection *conn, const Packet_EndTurn *packet) {
if (!testNetState(conn->state > NetState_init &&
!conn->stateFlags.pendingTurnChange, PACKET_ENDTURN))
return -1; // errno is set
if (conn->stateFlags.endingTurn) {
// This was the confirmation we were waiting for.
// NB. A remote request while we had sent a request serves as
// a confirmation.
conn->stateFlags.myTurn = !conn->stateFlags.myTurn;
conn->stateFlags.endingTurn = false;
} else {
// The other party wants to change whose turn it is.
// If it would become the other party's turn, we wait until
// the queue is flushed to actually carry out the turn change,
// and send the confirmation. I we wouldn't do that, and we still
// had some data to send in our own turn, we would end up asking
// for our turn back, and the turn would keep changing without
// any progress being made.
if (conn->stateFlags.myTurn) {
// Schedule the turn change until after the next queue flush.
conn->stateFlags.pendingTurnChange = true;
} else {
conn->stateFlags.myTurn = true;
sendEndTurn(conn);
}
}
(void) packet;
// Its contents is not interesting.
return 0;
}
// Convert the side indication relative to a remote party to
// a local player number.
static inline int
localSide(NetConnection *conn, NetplaySide side) {
if (side == NetplaySide_local) {
// "local" relative to the remote party.
return conn->player;
}
return 1 - conn->player;
}
static bool
checkYourTurn(NetConnection *conn, PacketType type) {
if (conn->stateFlags.myTurn) {
log_add(log_Warning, "Packet of type '%s' received in an "
"inappropriate turn.\n", packetTypeData[type].name);
errno = EBADMSG;
return false;
}
return true;
}
int
PacketHandler_Ready(NetConnection *conn, const Packet_Ready *packet) {
if (!testNetState(readyFlagsMeaningful(conn->state) &&
!conn->stateFlags.ready.remoteReady, PACKET_READY))
return -1; // errno is set
Netplay_remoteReady(conn);
(void) packet;
// Its contents is not interesting.
return 0;
}
int
PacketHandler_Fleet(NetConnection *conn, const Packet_Fleet *packet) {
uint32 numShips = ntoh16(packet->numShips);
size_t i;
size_t len;
int player;
BattleStateData *battleStateData;
if (!testNetState(conn->state == NetState_inSetup, PACKET_FLEET))
return -1; // errno is set
if (!checkYourTurn(conn, PACKET_FLEET)) {
// errno is set
return -1;
}
player = localSide(conn, (NetplaySide) packet->side);
len = packetLength((const Packet *) packet);
if (sizeof packet + numShips * sizeof(packet->ships[0]) > len) {
// There is not enough room in the packet to contain all
// the ships it says it contains.
log_add(log_Warning, "Invalid fleet size. Specified size is %d, "
"actual size = %d\n",
numShips, (len - sizeof packet) / sizeof(packet->ships[0]));
errno = EBADMSG;
return -1;
}
if (conn->stateFlags.handshake.localOk)
Netplay_cancelConfirmation(conn);
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
for (i = 0; i < numShips; i++) {
int ship = packet->ships[i].ship;
int index = packet->ships[i].index;
bool updateResult = updateFleetShip(
battleStateData->meleeState, player, index, ship);
if (!updateResult)
{
errno = EBADMSG;
return -1;
}
}
// Padding data may follow; it is ignored.
return 0;
}
int
PacketHandler_TeamName(NetConnection *conn, const Packet_TeamName *packet) {
size_t nameLen;
int side;
BattleStateData *battleStateData;
if (!testNetState(conn->state == NetState_inSetup, PACKET_TEAMNAME))
return -1; // errno is set
if (!checkYourTurn(conn, PACKET_TEAMNAME)) {
// errno is set
return -1;
}
if (conn->stateFlags.handshake.localOk)
Netplay_cancelConfirmation(conn);
side = localSide(conn, (NetplaySide) packet->side);
nameLen = packetLength((const Packet *) packet)
- sizeof (Packet_TeamName) - 1;
// The -1 is for not counting the terminating '\0'.
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
updateTeamName(battleStateData->meleeState, side, packet->name, nameLen);
// Padding data may follow; it is ignored.
return 0;
}
static void
handshakeComplete(NetConnection *conn) {
assert(!conn->stateFlags.handshake.localOk);
assert(!conn->stateFlags.handshake.remoteOk);
assert(!conn->stateFlags.handshake.canceling);
assert(conn->state == NetState_inSetup);
NetConnection_setState(conn, NetState_preBattle);
}
int
PacketHandler_Handshake0(NetConnection *conn,
const Packet_Handshake0 *packet) {
if (!testNetState(handshakeMeaningful(conn->state)
&& !conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKE0))
return -1; // errno is set
conn->stateFlags.handshake.remoteOk = true;
if (conn->stateFlags.handshake.localOk &&
!conn->stateFlags.handshake.canceling)
sendHandshake1(conn);
(void) packet;
// Its contents is not interesting.
return 0;
}
int
PacketHandler_Handshake1(NetConnection *conn,
const Packet_Handshake1 *packet) {
if (!testNetState(handshakeMeaningful(conn->state) &&
(conn->stateFlags.handshake.localOk ||
conn->stateFlags.handshake.canceling), PACKET_HANDSHAKE1))
return -1; // errno is set
if (conn->stateFlags.handshake.canceling) {
conn->stateFlags.handshake.remoteOk = true;
} else {
bool remoteWasOk = conn->stateFlags.handshake.remoteOk;
conn->stateFlags.handshake.localOk = false;
conn->stateFlags.handshake.remoteOk = false;
if (!remoteWasOk) {
// Received Handshake1 without prior Handshake0.
// A Handshake0 is implied, but we still need to confirm
// it with a Handshake1.
sendHandshake1(conn);
}
handshakeComplete(conn);
}
(void) packet;
// Its contents is not interesting.
return 0;
}
int
PacketHandler_HandshakeCancel(NetConnection *conn,
const Packet_HandshakeCancel *packet) {
if (!testNetState(handshakeMeaningful(conn->state)
&& conn->stateFlags.handshake.remoteOk, PACKET_HANDSHAKECANCEL))
return -1; // errno is set
conn->stateFlags.handshake.remoteOk = false;
sendHandshakeCancelAck(conn);
(void) packet;
// Its contents is not interesting.
return 0;
}
int
PacketHandler_HandshakeCancelAck(NetConnection *conn,
const Packet_HandshakeCancelAck *packet) {
if (!testNetState(handshakeMeaningful(conn->state)
&& conn->stateFlags.handshake.canceling,
PACKET_HANDSHAKECANCELACK))
return -1; // errno is set
conn->stateFlags.handshake.canceling = false;
if (conn->stateFlags.handshake.localOk) {
if (conn->stateFlags.handshake.remoteOk) {
sendHandshake1(conn);
} else
sendHandshake0(conn);
}
(void) packet;
// Its contents is not interesting.
return 0;
}
int
PacketHandler_SeedRandom(NetConnection *conn,
const Packet_SeedRandom *packet) {
BattleStateData *battleStateData;
if (!testNetState(conn->state == NetState_preBattle &&
!conn->stateFlags.discriminant, PACKET_SEEDRANDOM))
return -1; // errno is set
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
updateRandomSeed (battleStateData->meleeState, conn->player,
ntoh32(packet->seed));
conn->stateFlags.agreement.randomSeed = true;
return 0;
}
int
PacketHandler_InputDelay(NetConnection *conn,
const Packet_InputDelay *packet) {
BattleStateData *battleStateData;
uint32 delay;
if (!testNetState(conn->state == NetState_preBattle,
PACKET_INPUTDELAY))
return -1; // errno is set
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
delay = ntoh32(packet->delay);
if (delay > 60 * BATTLE_FRAME_RATE) {
log_add(log_Error, "NETPLAY: [%d] Received absurdly large "
"input delay value (%d).\n", conn->player, delay);
return -1;
}
conn->stateFlags.inputDelay = ntoh32(packet->delay);
return 0;
}
int
PacketHandler_SelectShip(NetConnection *conn,
const Packet_SelectShip *packet) {
bool updateResult;
BattleStateData *battleStateData;
if (!testNetState(conn->state == NetState_selectShip, PACKET_SELECTSHIP))
return -1; // errno is set
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
updateResult = updateMeleeSelection(battleStateData->getMeleeState,
conn->player, ntoh16(packet->ship));
if (!updateResult)
{
errno = EBADMSG;
return -1;
}
return 0;
}
int
PacketHandler_BattleInput(NetConnection *conn,
const Packet_BattleInput *packet) {
BATTLE_INPUT_STATE input;
BattleInputBuffer *bib;
if (!testNetState(conn->state == NetState_inBattle ||
conn->state == NetState_endingBattle ||
conn->state == NetState_endingBattle2, PACKET_BATTLEINPUT))
return -1; // errno is set
input = (BATTLE_INPUT_STATE) packet->state;
bib = getBattleInputBuffer(conn->player);
if (!BattleInputBuffer_push(bib, input)) {
// errno is set
return -1;
}
return 0;
}
int
PacketHandler_FrameCount(NetConnection *conn,
const Packet_FrameCount *packet) {
BattleStateData *battleStateData;
BattleFrameCounter frameCount;
if (!testNetState(conn->state == NetState_endingBattle,
PACKET_FRAMECOUNT))
return -1; // errno is set
frameCount = (BattleFrameCounter) ntoh32(packet->frameCount);
#ifdef NETPLAY_DEBUG
log_add(log_Debug, "NETPLAY: [%d] <== Received battleFrameCount %u.\n",
conn->player, (unsigned int) frameCount);
#endif
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
if (frameCount > battleStateData->endFrameCount)
battleStateData->endFrameCount = frameCount;
Netplay_remoteReady(conn);
return 0;
}
int
PacketHandler_Checksum(NetConnection *conn, const Packet_Checksum *packet) {
#ifdef NETPLAY_CHECKSUM
uint32 frameNr;
uint32 checksum;
size_t delay;
size_t interval;
#endif
if (!testNetState(NetState_battleActive(conn->state), PACKET_CHECKSUM))
return -1; // errno is set
#ifdef NETPLAY_CHECKSUM
frameNr = ntoh32(packet->frameNr);
checksum = ntoh32(packet->checksum);
interval = NetConnection_getChecksumInterval(conn);
delay = getBattleInputDelay();
if (frameNr % interval != 0) {
log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
"for frame %u, while we only expect checksums on frames "
"divisable by %u -- discarding.\n", conn->player,
(unsigned int) frameNr, interval);
return 0;
// No need to close the connection; checksums are not
// essential.
}
// The checksum is sent at the beginning of a frame.
// If we're in frame n and have sent our input already,
// the remote side has got enough input to progress delay + 1 frames from
// frame n. The next frame is then n + delay + 1, for which we can
// receive a checksum.
if (frameNr > battleFrameCount + delay + 1) {
log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
"for a frame too far in the future (frame %u, current "
"is %u, input delay is %u) -- discarding.\n", conn->player,
(unsigned int) frameNr, battleFrameCount, delay);
return 0;
// No need to close the connection; checksums are not
// essential.
}
// We can progress delay more frames after the last frame for which we
// received input. If we call that frame n, we can complete frames
// n through n + delay - 1. While we are waiting for the next input,
// in frame n + delay, we will first receive the checksum that the
// remote side sent at the start of frame n + 1.
// In this situation frameNr is n + 1, and battleFrameCount is
// n + delay.
if (frameNr + delay < battleFrameCount) {
log_add(log_Warning, "NETPLAY: [%d] <== Received checksum "
"for a frame too far in the past (frame %u, current "
"is %u, input delay is %u) -- discarding.\n", conn->player,
(unsigned int) frameNr, battleFrameCount, delay);
return 0;
// No need to close the connection; checksums are not
// essential.
}
addRemoteChecksum(conn, frameNr, checksum);
#endif
#ifndef NETPLAY_CHECKSUM
(void) packet;
#endif
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PACKETHANDLERS_H
#define _PACKETHANDLERS_H
#include "packet.h"
#define DECLARE_PACKETHANDLER(type) \
int PacketHandler_##type(NetConnection *conn, \
const Packet_##type *packet);
DECLARE_PACKETHANDLER(Init);
DECLARE_PACKETHANDLER(Ping);
DECLARE_PACKETHANDLER(Ack);
DECLARE_PACKETHANDLER(EndTurn);
DECLARE_PACKETHANDLER(Ready);
DECLARE_PACKETHANDLER(Fleet);
DECLARE_PACKETHANDLER(TeamName);
DECLARE_PACKETHANDLER(Handshake0);
DECLARE_PACKETHANDLER(Handshake1);
DECLARE_PACKETHANDLER(HandshakeCancel);
DECLARE_PACKETHANDLER(HandshakeCancelAck);
DECLARE_PACKETHANDLER(SeedRandom);
DECLARE_PACKETHANDLER(InputDelay);
DECLARE_PACKETHANDLER(SelectShip);
DECLARE_PACKETHANDLER(BattleInput);
DECLARE_PACKETHANDLER(FrameCount);
DECLARE_PACKETHANDLER(Checksum);
#endif /* _PACKETHANDLERS_H */
+200
View File
@@ -0,0 +1,200 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define NETCONNECTION_INTERNAL
#include "netplay.h"
#include "netconnection.h"
#include "packetq.h"
#include "netsend.h"
#include "packetsenders.h"
#ifdef NETPLAY_DEBUG
# include "libs/log.h"
#endif
#include <errno.h>
#include <stdlib.h>
#include <string.h>
static inline PacketQueueLink *
PacketQueueLink_alloc(void) {
// XXX: perhaps keep a pool of links?
return malloc(sizeof (PacketQueueLink));
}
static inline void
PacketQueueLink_delete(PacketQueueLink *link) {
free(link);
}
// 'maxSize' should at least be 1
void
PacketQueue_init(PacketQueue *queue) {
queue->size = 0;
queue->first = NULL;
queue->end = &queue->first;
queue->firstUrgent = NULL;
queue->endUrgent = &queue->first;
}
static void
PacketQueue_deleteLinks(PacketQueueLink *link) {
while (link != NULL) {
PacketQueueLink *next = link->next;
Packet_delete(link->packet);
PacketQueueLink_delete(link);
link = next;
}
}
void
PacketQueue_uninit(PacketQueue *queue) {
PacketQueue_deleteLinks(queue->firstUrgent);
PacketQueue_deleteLinks(queue->first);
}
void
queuePacket(NetConnection *conn, Packet *packet, bool urgent) {
PacketQueue *queue;
PacketQueueLink *link;
assert(NetConnection_isConnected(conn));
assert(!urgent || !packetTypeData[packetType(packet)].inTurn);
// Urgent packets should never stall the connection.
queue = &conn->queue;
link = PacketQueueLink_alloc();
link->packet = packet;
link->next = NULL;
if (urgent) {
*queue->endUrgent = link;
queue->endUrgent = &link->next;
} else {
*queue->end = link;
queue->end = &link->next;
}
queue->size++;
// XXX: perhaps check that this queue isn't getting too large?
#ifdef NETPLAY_DEBUG
if (packetType(packet) != PACKET_BATTLEINPUT &&
packetType(packet) != PACKET_CHECKSUM) {
// Reporting BattleInput or Checksum would get so spammy that it
// would slow down the battle.
log_add(log_Debug, "NETPLAY: [%d] ==> Queueing packet of type %s.\n",
NetConnection_getPlayerNr(conn),
packetTypeData[packetType(packet)].name);
}
#endif
}
// If an error occurs during sending, we leave the unsent packets in
// the queue, and let the caller decide what to do with them.
// This function may return -1 with errno EAGAIN if we're waiting for
// the other party to act first.
static int
flushPacketQueueLinks(NetConnection *conn, PacketQueueLink **first) {
PacketQueueLink *link;
PacketQueueLink *next;
PacketQueue *queue = &conn->queue;
for (link = *first; link != NULL; link = next) {
if (packetTypeData[packetType(link->packet)].inTurn &&
(!conn->stateFlags.myTurn || conn->stateFlags.endingTurn)) {
// This packet requires it to be 'our turn', and it isn't,
// or we've already told the other party we wanted to end our
// turn.
// This should never happen in the urgent queue.
assert(first != &queue->firstUrgent);
if (!conn->stateFlags.myTurn && !conn->stateFlags.endingTurn) {
conn->stateFlags.endingTurn = true;
if (sendEndTurnDirect(conn) == -1) {
// errno is set
*first = link;
return -1;
}
}
*first = link;
errno = EAGAIN;
// We need to wait for the reply to the turn change.
return -1;
}
if (sendPacket(conn, link->packet) == -1) {
// Errno is set.
*first = link;
return -1;
}
next = link->next;
Packet_delete(link->packet);
PacketQueueLink_delete(link);
queue->size--;
}
*first = link;
return 0;
}
int
flushPacketQueue(NetConnection *conn) {
int flushResult;
PacketQueue *queue = &conn->queue;
assert(NetConnection_isConnected(conn));
flushResult = flushPacketQueueLinks(conn, &queue->firstUrgent);
if (queue->firstUrgent == NULL)
queue->endUrgent = &queue->firstUrgent;
if (flushResult == -1) {
// errno is set
return -1;
}
flushResult = flushPacketQueueLinks(conn, &queue->first);
if (queue->first == NULL)
queue->end = &queue->first;
if (flushResult == -1) {
// errno is set
return -1;
}
// If a turn change had been requested by the other side while it was
// our turn, we first sent everything we still had to send in our turn.
// Now that is done, it is the time to actually give up the turn.
if (conn->stateFlags.pendingTurnChange) {
assert(conn->stateFlags.myTurn);
conn->stateFlags.myTurn = false;
conn->stateFlags.pendingTurnChange = false;
// Send the confirmation to the other side:
if (sendEndTurnDirect(conn) == -1) {
// errno is set
return -1;
}
}
return 0;
}
+68
View File
@@ -0,0 +1,68 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PACKETQ_H
#define _PACKETQ_H
typedef struct PacketQueue PacketQueue;
#include "packet.h"
#include "types.h"
#include <sys/types.h>
typedef struct PacketQueueLink PacketQueueLink;
struct PacketQueueLink {
PacketQueueLink *next;
Packet *packet;
};
struct PacketQueue {
size_t size;
PacketQueueLink *first;
PacketQueueLink **end;
PacketQueueLink *firstUrgent;
PacketQueueLink **endUrgent;
// first points to the first entry in the queue
// end points to the location where the next non-urgent message should
// be inserted.
// 'firstUrgent' and 'endUrgent' are analogous to 'first' and 'end'.
// Urgent packets should only be used for packets that may not
// be delayed while we wait for some remote confirmation.
// As such, these messages themselves should not require a state change
// which needs to be remotely confirmed.
// For example, the endTurn message can be sent when we want to change
// which party is allowed to transmit specific packets. That message
// should not be delayed by messages which require that *we* are that
// party, as without the endTurn message, it will never become our turn.
// Also, ping and ack packets should give some indication of the round
// trip time, which they can only do if they aren't delayed by other
// packets.
};
void PacketQueue_init(PacketQueue *queue);
void PacketQueue_uninit(PacketQueue *queue);
void queuePacket(NetConnection *conn, Packet *packet, bool urgent);
int flushPacketQueue(NetConnection *conn);
#endif
+199
View File
@@ -0,0 +1,199 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "netplay.h"
#include "packetsenders.h"
#include "packet.h"
#include "packetq.h"
#include "netsend.h"
void
sendInit(NetConnection *conn) {
Packet_Init *packet;
packet = Packet_Init_create();
queuePacket(conn, (Packet *) packet, false);
}
void
sendPing(NetConnection *conn, uint32 id) {
Packet_Ping *packet;
packet = Packet_Ping_create(id);
queuePacket(conn, (Packet *) packet, true);
}
void
sendAck(NetConnection *conn, uint32 id) {
Packet_Ack *packet;
packet = Packet_Ack_create(id);
queuePacket(conn, (Packet *) packet, true);
}
void
sendEndTurn(NetConnection *conn) {
Packet_EndTurn *packet;
packet = Packet_EndTurn_create();
queuePacket(conn, (Packet *) packet, true);
}
void
sendReady(NetConnection *conn) {
Packet_Ready *packet;
packet = Packet_Ready_create();
queuePacket(conn, (Packet *) packet, false);
}
// Bypass the packet queue.
// Should only be called from the packet queue functions themselves.
int
sendEndTurnDirect(NetConnection *conn) {
Packet_EndTurn *packet;
packet = Packet_EndTurn_create();
return sendPacket(conn, (Packet *) packet);
}
void
sendHandshake0(NetConnection *conn) {
Packet_Handshake0 *packet;
packet = Packet_Handshake0_create();
queuePacket(conn, (Packet *) packet, false);
}
void
sendHandshake1(NetConnection *conn) {
Packet_Handshake1 *packet;
packet = Packet_Handshake1_create();
queuePacket(conn, (Packet *) packet, false);
}
void
sendHandshakeCancel(NetConnection *conn) {
Packet_HandshakeCancel *packet;
packet = Packet_HandshakeCancel_create();
queuePacket(conn, (Packet *) packet, false);
}
void
sendHandshakeCancelAck(NetConnection *conn) {
Packet_HandshakeCancelAck *packet;
packet = Packet_HandshakeCancelAck_create();
queuePacket(conn, (Packet *) packet, false);
}
void
sendTeamString(NetConnection *conn, NetplaySide side, const char *name,
size_t len) {
Packet_TeamName *packet;
packet = Packet_TeamName_create(side, name, len);
queuePacket(conn, (Packet *) packet, false);
}
void
sendFleet(NetConnection *conn, NetplaySide side, const BYTE *ships,
size_t numShips) {
size_t i;
Packet_Fleet *packet;
packet = Packet_Fleet_create(side, numShips);
for (i = 0; i < numShips; i++) {
packet->ships[i].index = (uint32) i;
packet->ships[i].ship = ships[i];
}
queuePacket(conn, (Packet *) packet, false);
}
void
sendFleetShip(NetConnection *conn, NetplaySide side, size_t shipIndex,
BYTE ship) {
Packet_Fleet *packet;
packet = Packet_Fleet_create(side, 1);
packet->ships[0].index = shipIndex;
packet->ships[0].ship = ship;
queuePacket(conn, (Packet *) packet, false);
}
void
sendSeedRandom(NetConnection *conn, uint32 seed) {
Packet_SeedRandom *packet;
packet = Packet_SeedRandom_create(seed);
queuePacket(conn, (Packet *) packet, false);
}
void
sendInputDelay(NetConnection *conn, uint32 delay) {
Packet_InputDelay *packet;
packet = Packet_InputDelay_create(delay);
queuePacket(conn, (Packet *) packet, false);
}
void
sendSelectShip(NetConnection *conn, COUNT ship) {
Packet_SelectShip *packet;
packet = Packet_SelectShip_create(ship);
queuePacket(conn, (Packet *) packet, false);
}
void
sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input) {
Packet_BattleInput *packet;
packet = Packet_BattleInput_create((uint8) input);
queuePacket(conn, (Packet *) packet, false);
}
void
sendFrameCount(NetConnection *conn, BattleFrameCounter frameCount) {
Packet_FrameCount *packet;
packet = Packet_FrameCount_create((uint32) frameCount);
queuePacket(conn, (Packet *) packet, false);
}
#ifdef NETPLAY_CHECKSUM
void
sendChecksum(NetConnection *conn, BattleFrameCounter frameNr,
Checksum checksum) {
Packet_Checksum *packet;
packet = Packet_Checksum_create((uint32) frameNr, (uint32) checksum);
queuePacket(conn, (Packet *) packet, false);
}
#endif
+56
View File
@@ -0,0 +1,56 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _PACKETSENDERS_H
#define _PACKETSENDERS_H
#include "types.h"
#include "netconnection.h"
#include "packet.h"
#include "controls.h"
// for BATTLE_INPUT_STATE
void sendInit(NetConnection *conn);
void sendPing(NetConnection *conn, uint32 id);
void sendAck(NetConnection *conn, uint32 id);
void sendEndTurn(NetConnection *conn);
void sendReady(NetConnection *conn);
int sendEndTurnDirect(NetConnection *conn);
void sendHandshake0(NetConnection *conn);
void sendHandshake1(NetConnection *conn);
void sendHandshakeCancel(NetConnection *conn);
void sendHandshakeCancelAck(NetConnection *conn);
void sendTeamString(NetConnection *conn, NetplaySide side,
const char *name, size_t len);
void sendFleet(NetConnection *conn, NetplaySide side, const BYTE *ships,
size_t numShips);
void sendFleetShip(NetConnection *conn, NetplaySide player,
size_t shipIndex, BYTE ship);
void sendSeedRandom(NetConnection *conn, uint32 seed);
void sendInputDelay(NetConnection *conn, uint32 delay);
void sendSelectShip(NetConnection *conn, COUNT ship);
void sendBattleInput(NetConnection *conn, BATTLE_INPUT_STATE input);
void sendFrameCount(NetConnection *conn, uint32 frameCount);
void sendChecksum(NetConnection *conn, uint32 frameNr, uint32 checksum);
#endif /* _PACKETSENDERS_H */
+2
View File
@@ -0,0 +1,2 @@
uqm_CFILES="npconfirm.c ready.c"
+81
View File
@@ -0,0 +1,81 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define NETCONNECTION_INTERNAL
#include "../netplay.h"
#include "npconfirm.h"
#include "types.h"
#include "../netmisc.h"
#include "../packetsenders.h"
#include <assert.h>
#include <errno.h>
int
Netplay_confirm(NetConnection *conn) {
assert(handshakeMeaningful(NetConnection_getState(conn)));
if (conn->stateFlags.handshake.localOk) {
// Already confirmed
errno = EINVAL;
return -1;
}
conn->stateFlags.handshake.localOk = true;
if (conn->stateFlags.handshake.canceling) {
// If a previous confirmation was cancelled, but the cancel
// is not acknowledged yet, we don't have to send anything yet.
// The handshake0 packet will be sent when the acknowledgement
// arrives.
} else if (conn->stateFlags.handshake.remoteOk) {
// A Handshake0 is implied by the following Handshake1.
sendHandshake1(conn);
} else {
sendHandshake0(conn);
}
return 0;
}
int
Netplay_cancelConfirmation(NetConnection *conn) {
assert(handshakeMeaningful(NetConnection_getState(conn)));
if (!conn->stateFlags.handshake.localOk) {
// Not confirmed, or already canceling.
errno = EINVAL;
return -1;
}
conn->stateFlags.handshake.localOk = false;
if (conn->stateFlags.handshake.canceling) {
// If previous cancellation is still waiting to be acknowledged,
// the confirmation we are cancelling here, has not actually been
// sent yet. By setting the localOk flag to false, it is
// cancelled, without the need for any packets to be sent.
} else {
conn->stateFlags.handshake.canceling = true;
sendHandshakeCancel(conn);
}
return 0;
}
+30
View File
@@ -0,0 +1,30 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _NPCONFIRM_H
#define _NPCONFIRM_H
#include "../netplay.h"
#include "../netconnection.h"
int Netplay_confirm(NetConnection *conn);
int Netplay_cancelConfirmation(NetConnection *conn);
#endif /* _NPCONFIRM_H */
+106
View File
@@ -0,0 +1,106 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define NETCONNECTION_INTERNAL
#include "../netplay.h"
#include "ready.h"
#include "types.h"
#include "../netmisc.h"
#include "../packetsenders.h"
#include <assert.h>
static void
Netplay_bothReady(NetConnection *conn) {
NetConnection_ReadyCallback callback;
void *readyArg;
assert(conn->readyCallback != NULL);
callback = conn->readyCallback;
readyArg = conn->readyCallbackArg;
conn->readyCallback = NULL;
// Clear the readyCallback field before performing the callback,
// so that it can be set again from inside the callback
// function.
conn->readyCallbackArg = NULL;
callback(conn, readyArg);
}
// If notifyRemote is set, a 'Ready' message will be sent to the other side.
// returns true iff both sides are ready.
// Inside the callback function, ready flags may be set for a possible
// next Ready communication.
bool
Netplay_localReady(NetConnection *conn, NetConnection_ReadyCallback callback,
void *readyArg, bool notifyRemote) {
assert(readyFlagsMeaningful(NetConnection_getState(conn)));
assert(!conn->stateFlags.ready.localReady);
assert(callback != NULL);
NetConnection_setReadyCallback(conn, callback, readyArg);
if (notifyRemote)
sendReady(conn);
if (!conn->stateFlags.ready.remoteReady) {
conn->stateFlags.ready.localReady = true;
return false;
}
// Reset ready flags:
conn->stateFlags.ready.remoteReady = false;
// Trigger the callback.
Netplay_bothReady(conn);
return true;
}
// returns true iff both sides are ready.
bool
Netplay_remoteReady(NetConnection *conn) {
assert(readyFlagsMeaningful(NetConnection_getState(conn)));
// This is supposed to be already verified by the calling
// function.
assert(!conn->stateFlags.ready.remoteReady);
if (!conn->stateFlags.ready.localReady) {
conn->stateFlags.ready.remoteReady = true;
return false;
}
// Reset ready flags:
conn->stateFlags.ready.localReady = false;
// Trigger the callback.
Netplay_bothReady(conn);
return true;
}
bool
Netplay_isLocalReady(NetConnection *conn) {
return conn->stateFlags.ready.localReady;
}
bool
Netplay_isRemoteReady(NetConnection *conn) {
return conn->stateFlags.ready.remoteReady;
}
+31
View File
@@ -0,0 +1,31 @@
/*
* Copyright 2006 Serge van den Boom <svdb@stack.nl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _READY_H
#define _READY_H
#include "../netconnection.h"
bool Netplay_localReady(NetConnection *conn,
NetConnection_ReadyCallback callback, void *arg, bool notifyRemote);
bool Netplay_remoteReady(NetConnection *conn);
bool Netplay_isLocalReady(NetConnection *conn);
bool Netplay_isRemoteReady(NetConnection *conn);
#endif /* _READY_H */
+305 -94
View File
@@ -22,6 +22,11 @@
#include "controls.h"
#include "intel.h"
#include "melee.h"
#ifdef NETPLAY
# include "netplay/netmelee.h"
# include "netplay/netmisc.h"
# include "netplay/notify.h"
#endif
#include "races.h"
#include "settings.h"
#include "setup.h"
@@ -35,23 +40,107 @@
#define PICK_Y_OFFS 24
#define PICK_SIDE_OFFS 100
typedef struct getmelee_struct {
struct getmelee_struct {
BOOLEAN (*InputFunc) (struct getmelee_struct *pInputState);
COUNT MenuRepeatDelay;
BOOLEAN Initialized;
HSTARSHIP hBattleShip;
COUNT row, col, ships_left, which_player;
COUNT randomIndex;
#ifdef NETPLAY
BOOLEAN remoteSelected;
#endif
RECT flash_rect;
} GETMELEE_STATE;
};
#ifdef NETPLAY
static void reportShipSelected (GETMELEE_STATE *gms, COUNT index);
#endif
// Returns the <index>th ship in the queue, or 0 if it is not available.
// For all the ships in the queue, the ShipFacing field contains the
// index in the queue.
static HSTARSHIP
MeleeShipByQueueIndex (const QUEUE *queue, COUNT index)
{
HSTARSHIP hShip;
HSTARSHIP hNextShip;
for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
{
STARSHIPPTR StarShipPtr = LockStarShip (queue, hShip);
if (StarShipPtr->ShipFacing == index)
{
hNextShip = hShip;
if (StarShipPtr->RaceResIndex == 0)
hShip = 0;
UnlockStarShip (queue, hNextShip);
break;
}
hNextShip = _GetSuccLink (StarShipPtr);
UnlockStarShip (queue, ship);
}
return hShip;
}
// Returns the <index>th available ship in the queue.
static HSTARSHIP
MeleeShipByUsedIndex (const QUEUE *queue, COUNT index)
{
HSTARSHIP hShip;
HSTARSHIP hNextShip;
for (hShip = GetHeadLink (queue); hShip != 0; hShip = hNextShip)
{
STARSHIPPTR StarShipPtr = LockStarShip (queue, hShip);
if (StarShipPtr->RaceResIndex && index-- == 0)
{
UnlockStarShip (queue, hShip);
break;
}
hNextShip = _GetSuccLink (StarShipPtr);
UnlockStarShip (queue, hShip);
}
return hShip;
}
#if 0
static COUNT
queueIndexFromShip (HSTARSHIP hShip)
{
COUNT result;
STARSHIPPTR StarShipPtr = LockStarShip (queue, hShip);
result = StarShipPtr->ShipFacing;
UnlockStarShip (queue, hShip);
}
#endif
static void
PickMelee_ChangedSelection (GETMELEE_STATE *gms)
{
LockMutex (GraphicsLock);
gms->flash_rect.corner.x = PICK_X_OFFS + ((ICON_WIDTH + 2) * gms->col);
gms->flash_rect.corner.y = PICK_Y_OFFS + ((ICON_HEIGHT + 2) * gms->row)
+ ((1 - gms->which_player) * PICK_SIDE_OFFS);
SetFlashRect (&gms->flash_rect, (FRAME)0);
UnlockMutex (GraphicsLock);
}
// Select a new ship from the fleet for battle.
// Returns 'TRUE' if no choice has been made yet; this function is to be
// called again later.
// Returns 'FALSE' if a choice has been made. gms->hStarShip is set
// to the chosen (or randomly selected) ship, or to 0 if 'exit' has
// been chosen.
/* TODO: Include player timeouts */
static BOOLEAN
DoGetMelee (GETMELEE_STATE *gms)
{
BOOLEAN left, right, up, down, select;
HSTARSHIP hNextShip;
COUNT which_player = gms->which_player;
BOOLEAN done = FALSE;
SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
@@ -60,11 +149,23 @@ DoGetMelee (GETMELEE_STATE *gms)
gms->Initialized = TRUE;
gms->row = 0;
gms->col = NUM_MELEE_COLS_ORIG;
#ifdef NETPLAY
gms->remoteSelected = FALSE;
#endif
goto ChangeSelection;
// We determine upfront which ship would be chosen if the player
// wants a random ship, to keep it simple to keep network parties
// synchronised.
gms->randomIndex = (COUNT)TFB_Random () % gms->ships_left;
PickMelee_ChangedSelection (gms);
return TRUE;
}
SleepThread (ONE_SECOND / 120);
#ifdef NETPLAY
netInput ();
#endif
if (PlayerInput[which_player] == ComputerInput)
{
@@ -74,51 +175,85 @@ DoGetMelee (GETMELEE_STATE *gms)
left = right = up = down = FALSE;
select = TRUE;
}
else if (which_player == 0)
#ifdef NETPLAY
else if (PlayerInput[which_player] == NetworkInput)
{
left = PulsedInputState.key[PlayerOne][KEY_LEFT] ||
PulsedInputState.menu[KEY_MENU_LEFT];
right = PulsedInputState.key[PlayerOne][KEY_RIGHT] ||
PulsedInputState.menu[KEY_MENU_RIGHT];
up = PulsedInputState.key[PlayerOne][KEY_UP] ||
PulsedInputState.menu[KEY_MENU_UP];
down = PulsedInputState.key[PlayerOne][KEY_DOWN] ||
PulsedInputState.menu[KEY_MENU_DOWN];
select = PulsedInputState.key[PlayerOne][KEY_WEAPON] ||
PulsedInputState.menu[KEY_MENU_SELECT];
flushPacketQueues ();
if (gms->remoteSelected)
return FALSE;
return TRUE;
}
#endif
else
{
left = PulsedInputState.key[PlayerTwo][KEY_LEFT] ||
PulsedInputState.menu[KEY_MENU_LEFT];
right = PulsedInputState.key[PlayerTwo][KEY_RIGHT] ||
PulsedInputState.menu[KEY_MENU_RIGHT];
up = PulsedInputState.key[PlayerTwo][KEY_UP] ||
PulsedInputState.menu[KEY_MENU_UP];
down = PulsedInputState.key[PlayerTwo][KEY_DOWN] ||
PulsedInputState.menu[KEY_MENU_DOWN];
select = PulsedInputState.key[PlayerTwo][KEY_WEAPON] ||
PulsedInputState.menu[KEY_MENU_SELECT];
CONTROL_TEMPLATE template;
if (which_player == 0)
template = PlayerOne;
else
template = PlayerTwo;
left = PulsedInputState.key[template][KEY_LEFT];
right = PulsedInputState.key[template][KEY_RIGHT];
up = PulsedInputState.key[template][KEY_UP];
down = PulsedInputState.key[template][KEY_DOWN];
select = PulsedInputState.key[template][KEY_WEAPON];
}
if (GLOBAL (CurrentActivity) & CHECK_ABORT)
{
gms->hBattleShip = 0;
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
return FALSE;
#ifdef NETPLAY
// TODO: send abort packet
#endif
done = true;
}
if (select)
else if (select)
{
if (gms->hBattleShip ||
(gms->col == NUM_MELEE_COLS_ORIG && ConfirmExit ()))
if (gms->col == NUM_MELEE_COLS_ORIG)
{
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
return FALSE;
if (gms->row == 0)
{
// Random ship
gms->hBattleShip = MeleeShipByUsedIndex (
&race_q[which_player], gms->randomIndex);
#ifdef NETPLAY
reportShipSelected (gms, (COUNT)~0);
#endif
done = TRUE;
}
else
{
// Exit
if (ConfirmExit ())
{
gms->hBattleShip = 0;
GLOBAL (CurrentActivity) &= ~CHECK_ABORT;
done = TRUE;
}
}
}
else
{
COUNT ship_index;
ship_index = (gms->row * NUM_MELEE_COLS_ORIG) + gms->col;
gms->hBattleShip = MeleeShipByQueueIndex (
&race_q[which_player], ship_index);
if (gms->hBattleShip != 0)
{
// Selection contains a ship.
#ifdef NETPLAY
reportShipSelected (gms, ship_index);
#endif
done = TRUE;
}
}
}
else
{
// Process motion commands.
COUNT new_row, new_col;
new_row = gms->row;
@@ -146,72 +281,30 @@ DoGetMelee (GETMELEE_STATE *gms)
if (new_row != gms->row || new_col != gms->col)
{
COUNT ship_index;
gms->row = new_row;
gms->col = new_col;
PlayMenuSound (MENU_SOUND_MOVE);
ChangeSelection:
LockMutex (GraphicsLock);
gms->flash_rect.corner.x = PICK_X_OFFS
+ ((ICON_WIDTH + 2) * gms->col);
gms->flash_rect.corner.y = PICK_Y_OFFS
+ ((ICON_HEIGHT + 2) * gms->row)
+ ((1 - which_player) * PICK_SIDE_OFFS);
SetFlashRect (&gms->flash_rect, (FRAME)0);
gms->hBattleShip = GetHeadLink (&race_q[which_player]);
if (gms->col == NUM_MELEE_COLS_ORIG)
{
if (gms->row)
gms->hBattleShip = 0;
else
{
ship_index = (COUNT)TFB_Random () % gms->ships_left;
for (gms->hBattleShip = GetHeadLink (&race_q[which_player]);
gms->hBattleShip != 0; gms->hBattleShip = hNextShip)
{
STARSHIPPTR StarShipPtr = LockStarShip (
&race_q[which_player], gms->hBattleShip);
if (StarShipPtr->RaceResIndex && ship_index-- == 0)
{
UnlockStarShip (&race_q[which_player],
gms->hBattleShip);
break;
}
hNextShip = _GetSuccLink (StarShipPtr);
UnlockStarShip (&race_q[which_player],
gms->hBattleShip);
}
}
}
else
{
ship_index = (gms->row * NUM_MELEE_COLS_ORIG) + gms->col;
for (gms->hBattleShip = GetHeadLink (&race_q[which_player]);
gms->hBattleShip != 0; gms->hBattleShip = hNextShip)
{
STARSHIPPTR StarShipPtr = LockStarShip (
&race_q[which_player], gms->hBattleShip);
if (StarShipPtr->ShipFacing == ship_index)
{
hNextShip = gms->hBattleShip;
if (StarShipPtr->RaceResIndex == 0)
gms->hBattleShip = 0;
UnlockStarShip (&race_q[which_player], hNextShip);
break;
}
hNextShip = _GetSuccLink (StarShipPtr);
UnlockStarShip (&race_q[which_player], gms->hBattleShip);
}
}
UnlockMutex (GraphicsLock);
PickMelee_ChangedSelection (gms);
}
}
return TRUE;
#ifdef NETPLAY
flushPacketQueues ();
#endif
return !done;
}
#ifdef NETPLAY
static void
endMeleeCallback (NetConnection *conn, void *arg)
{
NetMelee_reenterState_inSetup (conn);
(void) arg;
}
#endif
HSTARSHIP
GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
{
@@ -222,17 +315,30 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
CONTEXT OldContext;
GETMELEE_STATE gmstate;
STARSHIPPTR StarShipPtr;
BOOLEAN firstTime;
if (!(GLOBAL (CurrentActivity) & IN_BATTLE))
return (0);
#ifdef NETPLAY
{
NetConnection *conn = netConnections[which_player];
if (conn != NULL) {
BattleStateData *battleStateData;
battleStateData =
(BattleStateData *) NetConnection_getStateData(conn);
battleStateData->getMeleeState = &gmstate;
}
}
#endif
s.frame = SetAbsFrameIndex (PickMeleeFrame, which_player);
OldContext = SetContext (OffScreenContext);
SetContextFGFrame (s.frame);
if (LastStarShipPtr == 0 || LastStarShipPtr->special_counter == 0)
{
COUNT cur_bucks;
COUNT cur_bucks;
HSTARSHIP hBattleShip, hNextShip;
cur_bucks = 0;
@@ -264,7 +370,8 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
GetFrameRect (s.frame, &gmstate.flash_rect);
gmstate.flash_rect.extent.width -= 4;
t.baseline.x = gmstate.flash_rect.extent.width;
gmstate.flash_rect.corner.x = gmstate.flash_rect.extent.width - (6 * 3);
gmstate.flash_rect.corner.x =
gmstate.flash_rect.extent.width - (6 * 3);
gmstate.flash_rect.corner.y = 2;
gmstate.flash_rect.extent.width = (6 * 3);
gmstate.flash_rect.extent.height = 7 - 2;
@@ -291,6 +398,7 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
if (LOBYTE (battle_counter) == 0 || HIBYTE (battle_counter) == 0)
{
// One side is out of ships. Game over.
DWORD TimeOut;
BOOLEAN PressState, ButtonState;
@@ -321,12 +429,33 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
&& (!(PlayerControl[0] & PlayerControl[1] & PSYTRON_CONTROL)
|| GetTimeCounter () < TimeOut)));
#ifdef NETPLAY
setStateConnections (NetState_endMelee);
localReadyConnections (endMeleeCallback, NULL, true);
#endif
LockMutex (GraphicsLock);
return (0);
}
if (LastStarShipPtr == 0 && which_player)
// Fade in if we're not already faded in.
#ifdef NETPLAY
// Hack. If one side is network controlled, the top player does not have
// to be the first player to get a chance to select his ship,
// As what is top locally doesn't have to be the same as what it is
// remotely.
// This check is not so nice as it replicates the code used to decide
// which party is first in selectAllShips.
firstTime = (LastStarShipPtr == 0) && ((which_player == 0) ==
(((PlayerControl[0] & NETWORK_CONTROL) &&
!NetConnection_getDiscriminant(netConnections[0])) ||
((PlayerControl[1] & NETWORK_CONTROL) &&
NetConnection_getDiscriminant(netConnections[1]))));
#else
firstTime = LastStarShipPtr == 0 && which_player == 1;
#endif
if (firstTime)
{
BYTE fade_buf[] = {FadeAllToColor};
@@ -342,6 +471,16 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
gmstate.flash_rect.extent.width = (ICON_WIDTH + 2);
gmstate.flash_rect.extent.height = (ICON_HEIGHT + 2);
#ifdef NETPLAY
{
bool allOk = negotiateReadyConnections (true, NetState_selectShip);
if (!allOk)
{
// Some network connection has been reset.
gmstate.hBattleShip = 0;
}
}
#endif
gmstate.InputFunc = DoGetMelee;
SetDefaultMenuRepeatDelay ();
gmstate.Initialized = FALSE;
@@ -359,7 +498,8 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
else
{
StarShipPtr = LockStarShip (&race_q[which_player], gmstate.hBattleShip);
StarShipPtr =
LockStarShip (&race_q[which_player], gmstate.hBattleShip);
OwnStarShip (StarShipPtr,
1 << which_player, StarShipPtr->captains_name_index);
StarShipPtr->captains_name_index = 0;
@@ -370,7 +510,78 @@ GetMeleeStarShip (STARSHIPPTR LastStarShipPtr, COUNT which_player)
WaitForSoundEnd (0);
}
#ifdef NETPLAY
{
NetConnection *conn = netConnections[which_player];
if (conn != NULL)
{
BattleStateData *battleStateData;
battleStateData = (BattleStateData *)
NetConnection_getStateData(conn);
battleStateData->getMeleeState = NULL;
if (gmstate.hBattleShip == 0)
NetConnection_setState(conn, NetState_inSetup);
}
}
#endif
return (gmstate.hBattleShip);
}
#ifdef NETPLAY
// Called when a ship selection has arrived from a remote player.
bool
updateMeleeSelection (GETMELEE_STATE *gms, COUNT player, COUNT ship)
{
if (gms == NULL)
{
// This happens when we get an update message from a connection
// for who we are not selecting a ship (yet?).
fprintf (stderr, "Unexpected ship selection packet received.\n");
return false;
}
if (ship == (COUNT) ~0)
{
// Random selection.
gms->hBattleShip =
MeleeShipByUsedIndex (&race_q[player], gms->randomIndex);
gms->remoteSelected = TRUE;
NetConnection_setState (netConnections[player], NetState_interBattle);
return true;
}
gms->hBattleShip = MeleeShipByQueueIndex (&race_q[player], ship);
if (gms->hBattleShip == 0)
{
fprintf (stderr, "Invalid ship selection received from remote "
"party.\n");
return false;
}
gms->remoteSelected = TRUE;
NetConnection_setState (netConnections[player], NetState_interBattle);
return true;
}
static void
reportShipSelected (GETMELEE_STATE *gms, COUNT index)
{
size_t playerI;
for (playerI = 0; playerI < NUM_PLAYERS; playerI++)
{
NetConnection *conn = netConnections[playerI];
if (conn == NULL)
continue;
if (!NetConnection_isConnected (conn))
continue;
Netplay_selectShip (conn, index);
NetConnection_setState (conn, NetState_interBattle);
}
(void) gms;
}
#endif
+3
View File
@@ -23,6 +23,9 @@
HSTARSHIP GetMeleeStarShip (STARSHIPPTR LastStarShipPtr,
COUNT which_player);
typedef struct getmelee_struct GETMELEE_STATE;
bool updateMeleeSelection (GETMELEE_STATE *gms, COUNT player, COUNT ship);
#endif /* _PICKMELE_H */
+16 -12
View File
@@ -141,26 +141,26 @@ DamageColorCycle (COLOR c, COUNT i)
{
WHITE_COLOR,
BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x00), 0x2A),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x7, 0x00), 0x7E),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0xE, 0x00), 0x7C),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x07, 0x00), 0x7E),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0E, 0x00), 0x7C),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0xA), 0x0E),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E),
};
if (i)
c = damage_tab[i];
else if (c == WHITE_COLOR)
c = damage_tab[6];
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0xA), 0x0E))
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x0A), 0x0E))
c = damage_tab[5];
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1C, 0x00), 0x78))
c = damage_tab[4];
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7A))
c = damage_tab[3];
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0xE, 0x00), 0x7C))
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0E, 0x00), 0x7C))
c = damage_tab[2];
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x7, 0x00), 0x7E))
else if (c == BUILD_COLOR (MAKE_RGB15 (0x1F, 0x07, 0x00), 0x7E))
c = damage_tab[1];
else
c = damage_tab[0];
@@ -1944,16 +1944,20 @@ PlanetSide (PMENU_STATE pMS)
PSD.MineralText[1] = PSD.MineralText[0];
PSD.MineralText[2] = PSD.MineralText[1];
PSD.ColorCycle[0] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x3, 0x00), 0x7F);
PSD.ColorCycle[1] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0xA, 0x00), 0x7D);
PSD.ColorCycle[0] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
PSD.ColorCycle[1] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
PSD.ColorCycle[2] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
PSD.ColorCycle[3] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
for (index = 4; index < (NUM_TEXT_FRAMES >> 1) - 4; ++index)
PSD.ColorCycle[index] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x1F), 0x0F);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 4] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 3] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 2] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0xA, 0x000), 0x7D);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 1] = BUILD_COLOR (MAKE_RGB15 (0x1F, 0x3, 0x00), 0x7F);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 4] =
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x1F, 0x00), 0x71);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 3] =
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7B);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 2] =
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7D);
PSD.ColorCycle[(NUM_TEXT_FRAMES >> 1) - 1] =
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7F);
pMS->ModuleFrame = (FRAME)&PSD;
index = NORMALIZE_FACING ((COUNT)TFB_Random ());
+13 -2
View File
@@ -22,6 +22,9 @@
#include "controls.h"
#include "options.h"
#include "nameref.h"
#ifdef NETPLAY
# include "netplay/netmelee.h"
#endif
#include "init.h"
#include "intel.h"
#include "resinst.h"
@@ -72,7 +75,10 @@ InitPlayerInput (void)
{
HumanInput[0] = p1_combat_summary;
HumanInput[1] = p2_combat_summary;
ComputerInput = computer_intelligence;
ComputerInput = computer_intelligence;
#ifdef NETPLAY
NetworkInput = networkBattleInput;
#endif
}
void
@@ -215,10 +221,11 @@ SetPlayerInput (void)
for (which_player = 0; which_player < NUM_PLAYERS; ++which_player)
{
if (!(PlayerControl[which_player] & HUMAN_CONTROL))
if (PlayerControl[which_player] & COMPUTER_CONTROL)
PlayerInput[which_player] = ComputerInput;
else if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE)
{
// Full game.
if (which_player == 0)
PlayerInput[which_player] = HumanInput[0];
else
@@ -227,6 +234,10 @@ SetPlayerInput (void)
PlayerControl[which_player] = COMPUTER_CONTROL | AWESOME_RATING;
}
}
#ifdef NETPLAY
else if (PlayerControl[which_player] & NETWORK_CONTROL)
PlayerInput[which_player] = NetworkInput;
#endif
else
PlayerInput[which_player] = HumanInput[which_player];
}
+236 -41
View File
@@ -20,6 +20,13 @@
#include "collide.h"
#include "globdata.h"
#include "init.h"
#ifdef NETPLAY
# include "netplay/netmelee.h"
# include "netplay/netmisc.h"
# include "netplay/notify.h"
# include "netplay/proto/ready.h"
# include "netplay/packetq.h"
#endif
#include "races.h"
#include "settings.h"
#include "sounds.h"
@@ -49,6 +56,151 @@ OpponentAlive (STARSHIPPTR TestStarShipPtr)
return TRUE;
}
#ifdef NETPLAY
static void
readyToEnd2Callback (NetConnection *conn, void *arg)
{
NetConnection_setState (conn, NetState_endingBattle2);
(void) arg;
}
static void
readyToEndCallback (NetConnection *conn, void *arg)
{
// This callback function gets called from inside the function that
// updates the frame counter, but this is not a problem as the
// ending frame count will at least be 1 greater than the current
// frame count.
BattleStateData *battleStateData;
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
#ifdef NETPLAY_DEBUG
fprintf (stderr, "Both sides are ready to end the battle; starting "
"end-of-battle synchronisation.\n");
#endif
NetConnection_setState (conn, NetState_endingBattle);
if (battleFrameCount + 1 > battleStateData->endFrameCount)
battleStateData->endFrameCount = battleFrameCount + 1;
Netplay_sendFrameCount (conn, battleFrameCount + 1);
// The +1 is to ensure that after the remote side receives the
// frame count it will still receive one more frame data packet,
// so it will know in advance when the last frame data packet
// will come so it won't block. It also ensures that the
// local frame counter won't go past the sent number, which
// could happen when the function triggering the call to this
// function is the frame update function which might update
// the frame counter one more time.
flushPacketQueue (conn);
#ifdef NETPLAY_DEBUG
fprintf (stderr, "NETPLAY: [%d] ==> Sent battleFrameCount %d.\n",
NetConnection_getPlayerNr(conn), battleFrameCount + 1);
#endif
Netplay_localReady(conn, readyToEnd2Callback, NULL, false);
(void) arg;
}
/*
* When one player's ship dies, there's a delay before the next ship
* can be chosen. This time depends on the time the ditty is playing
* and may differ for each side.
* To synchronise the time, the following protocol is followed:
* 1. (NetState_inBattle) The Ready protocol is used to let either
* party know when they're ready to stop the battle.
* 2. (NetState_endingBattle) Each party sends the frame number of when
* it wants to end the battle, and continues until that point, where
* it waits until it has received the frame number of the other party.
* 3. After a player has both sent and received a frame count, the
* simulation continues for each party, until the maximum of both
* frame counts has been achieved.
* 4. The Ready protocol is used to let each side signal that the it has
* reached the target frame count.
* 5. The battle ends.
*/
static bool
readyForBattleEndPlayer (NetConnection *conn, void *arg)
{
BattleStateData *battleStateData;
battleStateData = (BattleStateData *) NetConnection_getStateData(conn);
if (NetConnection_getState (conn) == NetState_interBattle)
{
// This connection is already ready. The entire synchronisation
// protocol has already been done for this connection.
return true;
}
if (NetConnection_getState (conn) == NetState_inBattle)
{
if (Netplay_isLocalReady(conn))
{
// We've already sent notice that we are ready, but we're
// still waiting for the other side to say it's ready too.
return false;
}
// We haven't yet told the other side we're ready. We do so now.
Netplay_localReady (conn, readyToEndCallback, NULL, true);
// This may set the state to endingBattle.
if (NetConnection_getState (conn) == NetState_inBattle)
return false;
}
assert (NetConnection_getState (conn) == NetState_endingBattle ||
NetConnection_getState (conn) == NetState_endingBattle2);
// Keep the simulation going as long as the target frame count
// hasn't been reached yet. Note that if the connection state is
// NetState_endingBattle, that we haven't yet received the
// remote frame count, so the target frame count may still rise.
if (battleFrameCount < battleStateData->endFrameCount)
return false;
if (NetConnection_getState (conn) == NetState_endingBattle)
{
// We have reached the target frame count, but we don't know
// the remote target frame count yet. So we wait until it has
// come in.
waitReady (conn);
// TODO: check whether all connections are still connected.
assert (NetConnection_getState (conn) == NetState_endingBattle2);
// Continue the simulation if the battleFrameCount has gone up.
if (battleFrameCount < battleStateData->endFrameCount)
return false;
}
// We are ready and wait for the other party to become ready too.
negotiateReady (conn, true, NetState_interBattle);
(void) arg;
return true;
}
#endif
static inline bool
readyForBattleEnd (void)
{
#ifndef NETPLAY
#if DEMO_MODE
// In Demo mode, the saved journal should be replayed with frame
// accuracy. PLRPlaying () isn't consistent enough.
return true;
#else /* !DEMO_MODE */
return PLRPlaying ((MUSIC_REF)~0);
#endif /* !DEMO_MODE */
#else /* defined (NETPLAY) */
if (PLRPlaying ((MUSIC_REF)~0))
return false;
if (!forAllConnectedPlayers (readyForBattleEndPlayer, NULL))
return false;
return true;
#endif /* defined (NETPLAY) */
}
void
new_ship (PELEMENT DeadShipPtr)
{
@@ -86,6 +238,10 @@ new_ship (PELEMENT DeadShipPtr)
MusicStarted = FALSE;
DeadShipPtr->turn_wait = (BYTE)(
DeadShipPtr->state_flags & (GOOD_GUY | BAD_GUY));
// DeadShipPtr->turn_wait is abused to store which
// side this element is for, probably because this
// information will be lost from state_flags (why is this
// necessary?).
for (hElement = GetHeadElement (); hElement; hElement = hSuccElement)
{
ELEMENTPTR ElementPtr;
@@ -105,9 +261,9 @@ new_ship (PELEMENT DeadShipPtr)
ElementPtr->life_span = 0;
ElementPtr->state_flags =
NONSOLID | DISAPPEARING | FINITE_LIFE;
ElementPtr->preprocess_func =
ElementPtr->postprocess_func =
ElementPtr->death_func = 0;
ElementPtr->preprocess_func = 0;
ElementPtr->postprocess_func = 0;
ElementPtr->death_func = 0;
ElementPtr->collision_func = 0;
}
}
@@ -124,23 +280,20 @@ new_ship (PELEMENT DeadShipPtr)
UnlockElement (hElement);
}
DeadShipPtr->life_span = MusicStarted ?
(ONE_SECOND * 3) / BATTLE_FRAME_RATE : 1;
DeadShipPtr->life_span =
MusicStarted ? (ONE_SECOND * 3) / BATTLE_FRAME_RATE : 1;
DeadShipPtr->death_func = new_ship;
DeadShipPtr->preprocess_func = new_ship;
SetElementStarShip (DeadShipPtr, DeadStarShipPtr);
}
if (DeadShipPtr->life_span
#if !DEMO_MODE
|| PLRPlaying ((MUSIC_REF)~0)
#endif /* DEMO_MODE */
)
if (DeadShipPtr->life_span || !readyForBattleEnd ())
{
DeadShipPtr->state_flags &= ~DISAPPEARING;
++DeadShipPtr->life_span;
return;
}
else
{
BOOLEAN RestartMusic;
@@ -155,12 +308,57 @@ new_ship (PELEMENT DeadShipPtr)
DeadStarShipPtr->RaceDescPtr);
free_ship (DeadStarShipPtr, TRUE);
UnbatchGraphics ();
#ifdef NETPLAY
initBattleStateDataConnections ();
{
bool allOk =
negotiateReadyConnections (true, NetState_interBattle);
// We are already in NetState_interBattle, but all
// sides just need to pass this checkpoint before
// going on.
if (!allOk)
{
// Some network connection has been reset.
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
BatchGraphics ();
return;
}
}
#endif /* NETPLAY */
if (GetNextStarShip (DeadStarShipPtr,
WHICH_SIDE (DeadShipPtr->turn_wait)) && RestartMusic)
{
#ifdef NETPLAY
{
bool allOk =
negotiateReadyConnections (true, NetState_inBattle);
if (!allOk)
{
// Some network connection has been reset.
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
BatchGraphics ();
return;
}
}
#endif
BattleSong (TRUE);
}
else if (LOBYTE (battle_counter) == 0
|| HIBYTE (battle_counter) == 0)
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
#ifdef NETPLAY
// Turn_wait was abused to store the side this element was on.
// We don't want this included in checksums, as it can be different
// for both sides of a connection.
// While preprocess_func == new_ship, turn_wait isn't included in
// the checksum at all all, but now that new_ship is done,
// it would be. As the value is irrelevant at this point, we can
// just set it to the same value on either side.
DeadShipPtr->turn_wait = 0;
#endif
BatchGraphics ();
}
}
@@ -225,20 +423,16 @@ explosion_preprocess (PELEMENT ShipPtr)
if (HIBYTE (LOWORD (rand_val)) < 256 * 1 / 3)
dist += DISPLAY_TO_WORLD (8);
ElementPtr->current.location.x =
ShipPtr->current.location.x
+ COSINE (angle, dist);
ShipPtr->current.location.x + COSINE (angle, dist);
ElementPtr->current.location.y =
ShipPtr->current.location.y
+ SINE (angle, dist);
ShipPtr->current.location.y + SINE (angle, dist);
ElementPtr->preprocess_func = animation_preprocess;
rand_val = TFB_Random ();
angle = LOBYTE (LOWORD (rand_val));
dist = WORLD_TO_VELOCITY (
DISPLAY_TO_WORLD (HIBYTE (LOWORD (rand_val)) % 5)
);
DISPLAY_TO_WORLD (HIBYTE (LOWORD (rand_val)) % 5));
SetVelocityComponents (&ElementPtr->velocity,
COSINE (angle, dist),
SINE (angle, dist));
COSINE (angle, dist), SINE (angle, dist));
UnlockElement (hElement);
}
} while (--i);
@@ -380,14 +574,14 @@ spawn_ion_trail (PELEMENT ElementPtr)
BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x00), 0x2b),
BUILD_COLOR (MAKE_RGB15 (0x17, 0x00, 0x00), 0x2c),
BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2d),
BUILD_COLOR (MAKE_RGB15 (0xF, 0x00, 0x00), 0x2e),
BUILD_COLOR (MAKE_RGB15 (0xB, 0x00, 0x00), 0x2f),
BUILD_COLOR (MAKE_RGB15 (0x0F, 0x00, 0x00), 0x2e),
BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2f),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x15, 0x00), 0x7a),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x11, 0x00), 0x7b),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0xE, 0x00), 0x7c),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0xA, 0x00), 0x7d),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x7, 0x00), 0x7e),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x3, 0x00), 0x7f), };
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0E, 0x00), 0x7c),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x00), 0x7d),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x07, 0x00), 0x7e),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x03, 0x00), 0x7f), };
#define NUM_TAB_COLORS (sizeof (color_tab) / sizeof (color_tab[0]))
COUNT color_index = 0;
@@ -480,11 +674,12 @@ ship_transition (PELEMENT ElementPtr)
LockElement (hShipImage, &ShipImagePtr);
ShipImagePtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID;
ShipImagePtr->life_span = ShipImagePtr->thrust_wait = TRANSITION_LIFE;
SetPrimType (&DisplayArray[ShipImagePtr->PrimIndex], STAMPFILL_PRIM);
SetPrimColor (
&DisplayArray[ShipImagePtr->PrimIndex], START_ION_COLOR
);
ShipImagePtr->life_span = ShipImagePtr->thrust_wait =
TRANSITION_LIFE;
SetPrimType (&DisplayArray[ShipImagePtr->PrimIndex],
STAMPFILL_PRIM);
SetPrimColor (&DisplayArray[ShipImagePtr->PrimIndex],
START_ION_COLOR);
ShipImagePtr->current.image = ElementPtr->current.image;
ShipImagePtr->current.location = ElementPtr->current.location;
if (!(ElementPtr->state_flags & PLAYER_SHIP))
@@ -531,17 +726,17 @@ flee_preprocess (PELEMENT ElementPtr)
COLOR c;
COLOR color_tab[] =
{
BUILD_COLOR (MAKE_RGB15 (31, 25, 25), 0x24),
BUILD_COLOR (MAKE_RGB15 (31, 19, 19), 0x25),
BUILD_COLOR (MAKE_RGB15 (31, 15, 15), 0x26),
BUILD_COLOR (MAKE_RGB15 (31, 10, 10), 0x27),
BUILD_COLOR (MAKE_RGB15 (31, 4, 4), 0x28),
BUILD_COLOR (MAKE_RGB15 (31, 0, 0), 0x29),
BUILD_COLOR (MAKE_RGB15 (27, 0, 0), 0x2A),
BUILD_COLOR (MAKE_RGB15 (23, 0, 0), 0x2B),
BUILD_COLOR (MAKE_RGB15 (19, 0, 0), 0x2C),
BUILD_COLOR (MAKE_RGB15 (14, 0, 0), 0x2D),
BUILD_COLOR (MAKE_RGB15 (10, 0, 0), 0x2E),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x19, 0x19), 0x24),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x13, 0x13), 0x25),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0F, 0x0F), 0x26),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x0A, 0x0A), 0x27),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x04, 0x04), 0x28),
BUILD_COLOR (MAKE_RGB15 (0x1F, 0x00, 0x00), 0x29),
BUILD_COLOR (MAKE_RGB15 (0x1B, 0x00, 0x00), 0x2A),
BUILD_COLOR (MAKE_RGB15 (0x17, 0x00, 0x00), 0x2B),
BUILD_COLOR (MAKE_RGB15 (0x13, 0x00, 0x00), 0x2C),
BUILD_COLOR (MAKE_RGB15 (0x0E, 0x00, 0x00), 0x2D),
BUILD_COLOR (MAKE_RGB15 (0x0A, 0x00, 0x00), 0x2E),
};
dir = HINIBBLE (ElementPtr->thrust_wait) - 1;
+79 -8
View File
@@ -38,11 +38,17 @@
#include "options.h"
#include "uqmversion.h"
#include "comm.h"
#ifdef NETPLAY
# include "libs/callback.h"
# include "libs/alarm.h"
# include "libs/net.h"
# include "netplay/netoptions.h"
#endif
#include "setup.h"
#include "starcon.h"
#if defined(GFXMODULE_SDL)
#if defined (GFXMODULE_SDL)
# include SDL_INCLUDE(SDL.h)
// Including this is actually necessary on OSX.
#endif
@@ -455,6 +461,13 @@ main (int argc, char *argv[])
InitTimeSystem ();
InitTaskSystem ();
#ifdef NETPLAY
Network_init ();
Alarm_init ();
Callback_init ();
NetManager_init ();
#endif
GraphicsLock = CreateMutex ("Graphics",
SYNC_CLASS_TOPLEVEL | SYNC_CLASS_VIDEO);
@@ -481,6 +494,10 @@ main (int argc, char *argv[])
unInitTempDir ();
uninitIO ();
#ifdef NETPLAY
Network_uninit ();
#endif
return EXIT_SUCCESS;
}
@@ -496,6 +513,13 @@ enum
STEREOSFX_OPT,
ADDON_OPT,
ACCEL_OPT,
#ifdef NETPLAY
NETHOST1_OPT,
NETPORT1_OPT,
NETHOST2_OPT,
NETPORT2_OPT,
NETDELAY_OPT,
#endif
};
static const char *optString = "+r:d:foc:b:spC:n:?hM:S:T:m:q:ug:l:i:v";
@@ -532,6 +556,13 @@ static struct option longOptions[] =
{"stereosfx", 0, NULL, STEREOSFX_OPT},
{"addon", 1, NULL, ADDON_OPT},
{"accel", 1, NULL, ACCEL_OPT},
#ifdef NETPLAY
{"nethost1", 1, NULL, NETHOST1_OPT},
{"netport1", 1, NULL, NETPORT1_OPT},
{"nethost2", 1, NULL, NETHOST2_OPT},
{"netport2", 1, NULL, NETPORT2_OPT},
{"netdelay", 1, NULL, NETDELAY_OPT},
#endif
{0, 0, 0, 0}
};
@@ -595,7 +626,7 @@ parseOptions (int argc, char *argv[], struct options_struct *options)
{
int c;
optionIndex = -1;
c = getopt_long(argc, argv, optString, longOptions, &optionIndex);
c = getopt_long (argc, argv, optString, longOptions, &optionIndex);
if (c == -1)
break;
@@ -721,9 +752,9 @@ parseOptions (int argc, char *argv[], struct options_struct *options)
}
case 'g':
{
int err = parseFloatOption (optarg, &options->gamma,
int result = parseFloatOption (optarg, &options->gamma,
"gamma correction");
if (err)
if (result == -1)
badArg = TRUE;
else
options->gammaSet = TRUE;
@@ -814,6 +845,39 @@ parseOptions (int argc, char *argv[], struct options_struct *options)
badArg = TRUE;
}
break;
#ifdef NETPLAY
case NETHOST1_OPT:
netplayOptions.peer[0].isServer = false;
netplayOptions.peer[0].host = optarg;
break;
case NETPORT1_OPT:
netplayOptions.peer[0].port = optarg;
break;
case NETHOST2_OPT:
netplayOptions.peer[1].isServer = false;
netplayOptions.peer[1].host = optarg;
break;
case NETPORT2_OPT:
netplayOptions.peer[1].port = optarg;
break;
case NETDELAY_OPT:
{
if (parseIntOption (optarg, &netplayOptions.inputDelay,
"network input delay") == -1)
{
badArg = TRUE;
break;
}
if (netplayOptions.inputDelay > 60 * BATTLE_FRAME_RATE)
{
log_add (log_Fatal, "Network input delay is absurdly "
"large.");
badArg = TRUE;
}
break;
}
#endif
default:
log_add (log_Fatal, "Error: Invalid option '%s' not found.",
longOptions[optionIndex].name);
@@ -849,7 +913,7 @@ parseVolume (const char *str, float *vol, const char *optName)
log_add (log_Error, "Error: Invalid value for '%s'.", optName);
return -1;
}
intVol = (int) strtol(str, &endPtr, 10);
intVol = (int) strtol (str, &endPtr, 10);
if (*endPtr != '\0')
{
log_add (log_Error, "Error: Junk characters in volume specified "
@@ -884,7 +948,7 @@ parseIntOption (const char *str, int *result, const char *optName)
log_add (log_Error, "Error: Invalid value for '%s'.", optName);
return -1;
}
temp = (int) strtol(str, &endPtr, 10);
temp = (int) strtol (str, &endPtr, 10);
if (*endPtr != '\0')
{
log_add (log_Error, "Error: Junk characters in argument '%s'.",
@@ -907,7 +971,7 @@ parseFloatOption (const char *str, float *f, const char *optName)
log_add (log_Error, "Error: Invalid value for '%s'.", optName);
return -1;
}
temp = (float) strtod(str, &endPtr);
temp = (float) strtod (str, &endPtr);
if (*endPtr != '\0')
{
log_add (log_Error, "Error: Junk characters in argument '%s'.",
@@ -954,6 +1018,14 @@ usage (FILE *out, const struct options_struct *defaultOptions)
"mixsdl)");
log_add (log_User, " --stereosfx (enables positional sound effects, "
"currently only for openal)");
#ifdef NETPLAY
log_add (log_User, " --nethostN=HOSTNAME (server to connect to for "
"player N (1=bottom, 2=top)");
log_add (log_User, " --netportN=PORT (port to connect to/listen on for "
"player N (1=bottom, 2=top)");
log_add (log_User, " --netdelay=FRAMES (number of frames to "
"buffer/delay network input for");
#endif
log_add (log_User, "The following options can take either '3do' or 'pc' "
"as an option:");
log_add (log_User, " -m, --music : Music version (default %s)",
@@ -973,7 +1045,6 @@ usage (FILE *out, const struct options_struct *defaultOptions)
log_add (log_User, " --scroll : ff/frev during comm. pc=per-page, "
"3do=smooth (default %s)",
PC_3DO_optString (defaultOptions->smoothScroll));
log_setOutput (old);
}
+2 -2
View File
@@ -21,8 +21,8 @@
#define UQM_MAJOR_VERSION_S "0"
#define UQM_MINOR_VERSION 5
#define UQM_MINOR_VERSION_S "5"
#define UQM_PATCH_VERSION 3
#define UQM_PATCH_VERSION_S "3"
#define UQM_PATCH_VERSION 4
#define UQM_PATCH_VERSION_S "4"
#define UQM_EXTRA_VERSION ""
/* The final version is interpreted as:
* printf ("%d.%d.%d%s", UQM_MAJOR_VERSION, UQM_MINOR_VERSION,