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
+1
View File
@@ -1,4 +1,5 @@
Changes towards version 0.6: Changes towards version 0.6:
- Netplay - SvdB
- Added step-by-step instructions for compilation on MSVC++ - SvdB - Added step-by-step instructions for compilation on MSVC++ - SvdB
- All output done in binary mode, due to uio/Win32 conflicts (#912) - Michael - All output done in binary mode, due to uio/Win32 conflicts (#912) - Michael
- Online key configuration menu actually functional now - Michael - Online key configuration menu actually functional now - Michael
+3 -8
View File
@@ -138,18 +138,13 @@ packages.
- "C:\Program Files\OpenAL 1.1 SDK\libs\Win32" - "C:\Program Files\OpenAL 1.1 SDK\libs\Win32"
- "C:\Build\zlib-1.2.3\lib" - "C:\Build\zlib-1.2.3\lib"
5.4. Remove ignore of msvcrt.lib 5.4. Remove unnecessary dependencies
- Still in the "Linker" part of the project properties pages, in the left - Still in the "Linker" part of the project properties pages, in the left
part of the screen, click on "Input". part of the screen, click on "Input".
- In the right part, select "Ignore Specific Library", and remove - In the right part, select "Additional Dependencies", then click on "..."
"msvcrt.lib"
5.5. Remove unnecessary dependencies
- Still in the "Linker", "Input" part of the project properties pages,
in the right part, select "Additional Dependencies", then click on "..."
- Remove odbc32.lib and odbccp32.lib - Remove odbc32.lib and odbccp32.lib
5.6. Add user32.lib 5.5. Add user32.lib
- Still in "Additional Dependencies", add "user32.lib" - Still in "Additional Dependencies", add "user32.lib"
+2 -1
View File
@@ -38,7 +38,8 @@ uqm_HAVE_REGEX='@HAVE_REGEX_H_FLAG@'
uqm_HAVE_OPENGL='@HAVE_OPENGL@' uqm_HAVE_OPENGL='@HAVE_OPENGL@'
uqm_USE_ZIP_IO='@USE_ZIP_IO@' uqm_USE_ZIP_IO='@USE_ZIP_IO@'
uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@' uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
uqm_NETPLAY='@NETPLAY@'
DEBUG='@DEBUG@' DEBUG='@DEBUG@'
export BUILD_SYSTEM HOST_SYSTEM DEBUG export BUILD_SYSTEM HOST_SYSTEM DEBUG
export uqm_SOUNDMODULE uqm_USE_INTERNAL_MIKMOD uqm_HAVE_REGEX export uqm_SOUNDMODULE uqm_USE_INTERNAL_MIKMOD uqm_HAVE_REGEX
export uqm_HAVE_OPENGL uqm_USE_ZIP_IO uqm_USE_PLATFORM_ACCEL export uqm_HAVE_OPENGL uqm_USE_ZIP_IO uqm_USE_PLATFORM_ACCEL uqm_NETPLAY
+24 -2
View File
@@ -53,7 +53,7 @@ uqm_requirements()
uqm_prepare_config() uqm_prepare_config()
{ {
# Describe the menu: # Describe the menu:
MENU_main_ITEMS="debug graphics sound mikmod ovcodec joystick \ MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
ioformat accel" ioformat accel"
case "$HOST_SYSTEM" in case "$HOST_SYSTEM" in
Darwin) Darwin)
@@ -72,6 +72,7 @@ uqm_prepare_config()
MENU_main_ITEM_sound_TYPE=CHOICE MENU_main_ITEM_sound_TYPE=CHOICE
MENU_main_ITEM_mikmod_TYPE=CHOICE MENU_main_ITEM_mikmod_TYPE=CHOICE
MENU_main_ITEM_ovcodec_TYPE=CHOICE MENU_main_ITEM_ovcodec_TYPE=CHOICE
MENU_main_ITEM_netplay_TYPE=CHOICE
MENU_main_ITEM_joystick_TYPE=CHOICE MENU_main_ITEM_joystick_TYPE=CHOICE
MENU_main_ITEM_ioformat_TYPE=CHOICE MENU_main_ITEM_ioformat_TYPE=CHOICE
MENU_main_ITEM_accel_TYPE=CHOICE MENU_main_ITEM_accel_TYPE=CHOICE
@@ -210,6 +211,27 @@ uqm_prepare_config()
CHOICE_joystick_OPTION_disabled_TITLE="disabled" CHOICE_joystick_OPTION_disabled_TITLE="disabled"
CHOICE_joystick_DEFAULT=enabled CHOICE_joystick_DEFAULT=enabled
CHOICE_netplay_OPTIONS="none full ipv4"
CHOICE_netplay_TITLE="Network Supermelee support"
CHOICE_netplay_OPTION_none_TITLE="disabled"
CHOICE_netplay_OPTION_none_ACTIOn=netplay_none_action
netplay_none_action() {
NETPLAY=""
}
CHOICE_netplay_OPTION_full_TITLE="IPv4 and IPv6 (experimental)"
CHOICE_netplay_OPTION_full_ACTION=netplay_full_action
netplay_full_action() {
CFLAGS="$CFLAGS -DNETPLAY=NETPLAY_FULL"
NETPLAY="FULL"
}
CHOICE_netplay_OPTION_ipv4_TITLE="IPv4; no IPv6 (experimental)"
CHOICE_netplay_OPTION_ipv4_ACTION=netplay_ipv4_action
netplay_ipv4_action() {
CFLAGS="$CFLAGS -DNETPLAY=NETPLAY_IPV4"
NETPLAY="IPV4"
}
CHOICE_netplay_DEFAULT=full
CHOICE_ioformat_OPTIONS="stdio stdio_zip" CHOICE_ioformat_OPTIONS="stdio stdio_zip"
CHOICE_ioformat_TITLE="Supported file i/o methods" CHOICE_ioformat_TITLE="Supported file i/o methods"
CHOICE_ioformat_OPTION_stdio_TITLE="Only direct file i/o" CHOICE_ioformat_OPTION_stdio_TITLE="Only direct file i/o"
@@ -311,7 +333,7 @@ uqm_process_config() {
# Make build.vars from build.vars.in, substituting variables. # Make build.vars from build.vars.in, substituting variables.
SUBSTITUTE_VARS="BUILD_SYSTEM HOST_SYSTEM COMPILE CFLAGS LDFLAGS \ SUBSTITUTE_VARS="BUILD_SYSTEM HOST_SYSTEM COMPILE CFLAGS LDFLAGS \
MAKE ECHON SED DEBUG JOYSTICK \ MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
SOUNDMODULE USE_INTERNAL_MIKMOD \ SOUNDMODULE USE_INTERNAL_MIKMOD \
HAVE_OPENGL HAVE_REGEX_H_FLAG USE_ZIP_IO \ HAVE_OPENGL HAVE_REGEX_H_FLAG USE_ZIP_IO \
USE_PLATFORM_ACCEL \ USE_PLATFORM_ACCEL \
+1 -1
View File
@@ -15,7 +15,7 @@
# along with this program; if not, write to the Free Software # along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
BUILDLOG=/dev/null BUILDLOG=/tmp/build.log
TEMPFILE="/tmp/build.$$.tmp" TEMPFILE="/tmp/build.$$.tmp"
+2 -2
View File
@@ -90,8 +90,8 @@ case "$HOST_SYSTEM" in
LIB_SDL_image_LDFLAGS="-framework SDL_image" LIB_SDL_image_LDFLAGS="-framework SDL_image"
;; ;;
*) *)
LIB_SDL_image_CFLAGS="$LIB_SDL_CFLAGS" LIB_SDL_image_CFLAGS="$LIB_SDL_CFLAGS -I/opt/SDL_image/include/SDL"
LIB_SDL_image_LDFLAGS="$LIB_SDL_LDFLAGS -lSDL_image" LIB_SDL_image_LDFLAGS="$LIB_SDL_LDFLAGS -L/opt/SDL_image/lib -lSDL_image"
;; ;;
esac esac
LIB_SDL_image_VERSION="" LIB_SDL_image_VERSION=""
+62
View File
@@ -2246,3 +2246,65 @@ dispatch
exit menu exit menu
#(Unconnected. Press LEFT to connect.)
Unconnected. Press LEFT to connect.
#(Awaiting incoming connection...)
Awaiting incoming connection...
Press RIGHT to cancel.
#(Attempting outgoing connection...)
Attempting outgoing connection...
Press RIGHT to cancel.
#(Connected. Press RIGHT to disconnect.)
Connected. Press RIGHT to disconnect.
#(Confirmation cancelled. Press FIRE to reconfirm.)
Confirmation cancelled. Press FIRE to reconfirm.
#(Connection for bottom player not established.)
Connection for bottom player not established.
#(Connection for top player not established.)
Connection for top player not established.
#(Waiting for remote confirmation.)
Waiting for remote confirmation.
#(Bottom player is connected.)
Bottom player is connected.
#(Top player is connected.)
Top player is connected.
#(Bottom player: connection failed.)
Bottom player: connection failed.
#(Top player: connection failed.)
Top player: connection failed.
#(Bottom player: connection closed.)
Bottom player: connection closed.
#(Top player: connection closed.)
Top player: connection closed.
#(Connection for bottom player not ready.)
Connection for bottom player not ready.
#(Connection for top player not ready.)
Connection for top player not ready.
+4
View File
@@ -29,3 +29,7 @@ melebkgd.27.png -1 0 0 50
melebkgd.28.png -1 0 -2 -22 melebkgd.28.png -1 0 -2 -22
melebkgd.29.png -1 0 -268 -217 melebkgd.29.png -1 0 -268 -217
melebkgd.30.png -1 0 -268 -217 melebkgd.30.png -1 0 -268 -217
netplay.0.png -1 0 -256 -24
netplay.1.png -1 0 -256 -24
netplay.2.png -1 0 -256 -167
netplay.3.png -1 0 -256 -167
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+21
View File
@@ -0,0 +1,21 @@
As the game currently unfortunately works with polling, this is how
to integrate network handling with the game.
In the function called periodically by DoInput(), there should be a call
to netInput() somewhere at the beginning, and a call to flushPacketQueues()
somewhere at the end.
netInput() checks all connections for incoming packets, and calls
the appropriate packet handlers.
flushPacketQueues() sends all pending packets on their way, for all
connections.
In between, you can call functions that enqueue packets.
You would also check the network state here to determine whether you need to
act on some packet that has been delivered to the local party.
+223
View File
@@ -0,0 +1,223 @@
There are three types of negotiations used to synchronised the parties
of a network connection.
- Continue when we know the other is ready ("Ready")
This is used when both parties need to sending information to the
other side, but what each party is doing does not interfere with
what the other party is doing.
- Only speak in your own turn ("Turn")
This is used when the parties have changes to make to common data.
- Mutual agreement on an action ("Confirm")
This is used to end a state where both parties are modifying
common data. Both parties have to agree with the data for either
party to continue.
============================================================================
"Ready" negotiation.
Sometimes the parties need to notify eachother of their local state,
and then go on when both are ready. For this purpose both parties signify
that they are ready by sending the READY message. When a party is ready
and has received notice that the other party is ready, it can go on.
States:
0. notReady - send nor received READY
!localReady && !remoteReady
1. localReady - sent READY, not yet received READY
localReady && !remoteReady
2. remoteReady - received READY, not yet sent READY
!localReady && remoteReady
3. ready - sent and received READY
Messages:
- READY - "I have nothing further to send at this point"
From state 0 (notReady):
local decision -> Send READY, goto 1
received READY -> goto 2
From state 1 (localReady):
received READY -> goto 3
From state 2 (remoteReady):
local decision -> Send READY goto 3
============================================================================
"Turn" negotiation.
For some actions (like changing a shared configuration option), it is
important that both sides don't just send changes at once.
To handle this, only one party may send these packets at any moment.
If the party whose turn it isn't wants to speak, or if the party whose
turn it is doesn't have anything further to say, he can send an ENDTURN
packet. The other party should confirm this by sending another ENDTURN
packet back.
States:
0. myTurn - I may speak
myTurn && !endTurn
1. endMyTurn - I've given up speaking, waiting for confirmation
myTurn && endTurn
2. yourTurn - You may speak
!myTurn && !endTurn
3. endYourTurn - I want to speak, waiting for confirmation
!myTurn && endTurn
Messages:
- ENDTURN - "this party ready to change turns"
From state 0 (myTurn):
local decision -> Send ENDTURN, goto 1
received ENDTURN -> Send ENDTURN, goto 2
From state 1 (endMyTurn):
received ENDTURN -> Send ENDTURN, goto 2
From state 2 (yourTurn):
local decision -> Send ENDTURN, goto 3
received ENDTURN -> Send ENDTURN, goto 0
From state 3 (endYourTurn):
received ENDTURN -> Send ENDTURN, goto 0
============================================================================
"Confirm" negotiation.
Some actions (like agreeing on a configuration) require confirmation
from both parties. This section documents the handshaking protocol involved.
Each player must manually confirm the action.
After a player has confirmed an action, he may cancel it as long as
he hasn't received a confirmation from the other party.
All messages arrive in the order sent.
States:
0. waiting
!handshake.canceling && !handshake.localOk && !handshake.remoteOk
1. localOk (cancelable) - sent CONFIRM1 (since last CANCEL)
!handshake.canceling && handshake.localOk && !handshake.remoteOk
2. remoteOk (cancelable) - received CONFIRM1
!handshake.canceling && !handshake.localOk && handshake.remoteOk
3. committed - sent CONFIRM1 (since last CANCEL,
received CONFIRM1,
sent CONFIRM2 (since last CANCEL)
!handshake.canceling && handshake.localOk && handshake.remoteOk
4. cancelWaiting - sent CANCEL
handshake.canceling && !handshake.localOk && !handshake.remoteOk
5. cancelLocalOk - sent CANCEL and ready to send CONFIRM1,
but received no CANCELACK
handshake.canceling && handshake.localOk && !handshake.remoteOk
6. cancelRemoteOk - sent CANCEL and received CONFIRM1,
but received no CANCELACK
handshake.canceling && !handshake.localOk && handshake.remoteOk
7. cancelCommitted - sent CANCEL and ready to send CONFIRM2,
received CONFIRM1,
but received no CANCELACK
handshake.canceling && handshake.localOk && handshake.remoteOk
8. done - sent and received CONFIRM1 and CONFIRM2
(since last CANCEL)
Handshake messages:
- CONFIRM1 - "the current local configuration OK for me"
- CONFIRM2 - "acknowledging your CONFIRM1; my own configuration is unchanged
since I sent CONFIRM1 (after the last CANCEL)"
- CANCEL - "forget about my earlier CONFIRM1"
- CANCELACK - "received your CANCEL"
MESSAGE(x) indicates any other message.
From state 0: (waiting)
local confirmation -> Send CONFIRM1, goto 1
local changes -> Send MESSAGE(changes) (goto 0)
received CONFIRM1 -> goto 2
received MESSAGE(changes) -> Process(changes) (goto 0)
From state 1: (localOk)
local cancel -> Send CANCEL, goto 4
received CONFIRM1 -> Send CONFIRM2, goto 3
received CONFIRM2 -> Send CONFIRM2, goto 8
received MESSAGE(changes) -> Process(changes), Send CANCEL, goto 4
From state 2: (remoteOk)
local confirmation -> Send CONFIRM2, goto 3
local changes -> Send MESSAGE(changes), (goto 2)
received CANCEL -> Send CANCELACK, goto 0
From state 3: (committed)
received CONFIRM2 -> goto 8
received CANCEL -> Send CANCELACK, goto 1
From state 4: (cancelWaiting)
local changes -> Send MESSAGE(changes), (goto 4)
local confirmation -> goto 5
received CONFIRM1 -> goto 6
received CONFIRM2 -> goto 6
received CANCELACK -> goto 0
received MESSAGE(changes) -> Process(changes), (goto 4)
From state 5: (cancelLocalOk)
local cancel -> goto 4
received CONFIRM1 -> goto 7
received CONFIRM2 -> goto 7
received CANCELACK -> SEND CONFIRM1 goto 1
received MESSAGE(changes) -> Process(changes), goto 4
From state 6: (cancelRemoteOk)
local confirmation -> goto 7
local changes -> Send MESSAGE(changes), (goto 6)
received CONFIRM2 -> (goto 6)
received CANCEL -> Send CANCELACK, goto 4
received CANCELACK -> goto 2
From state 7: (cancelCommitted)
received CONFIRM2 -> (goto 7)
received CANCEL -> Send CANCELACK, goto 5
received CANCELACK -> Send CONFIRM2, goto 3
On receiving local confirmation, sending CONFIRM2 is a shortcut for
sending CONFIRM1 followed by CONFIRM2. Receiving CONFIRM2 from localOk
and cancelLocalOk is accepted just for this shortcut.
To prove there are no race conditions, I examine all the combinations
of states and messages that are underway. Whenever the order of actions
isn't fixed, the result should be the same (eg. recv(CONFIRM1) followed
by send(CANCEL) should leave the party in the same state as when
the send(CANCEL) preceded the recv(CONFIRM1)).
I also check whether it is possible for packets to arrive that
aren't expected.
============================================================================
Battle ending negotiation.
This negotation consists of:
1. a 'Ready' negotiation, before stopping sending frame data
2. communication of each sides current battle frame count
3. the side running behind processes more frames to catch up
States:
0. Playing
1. localReady
2. remoteReady
3. countSent
4. catchingUp
5. awaitingCatchup
// Unfinished... partially described in readyForBattleEndPlayer()
+63
View File
@@ -0,0 +1,63 @@
NetState_unconnected is the initial state. When a connection attempt is made,
the state is set to NetState_connecting.
The state field of a NetConnection is NULL.
NetState_connecting indicates a connection is in progress.
When the connection is established, an INIT packet is sent, the state
is changed to NetState_init and InputFunc is set to DoNetworkInit.
The state field of a NetConnection is a ConnectStateData structure.
NetState_init is for initialising the connection before actual game
information is sent. When an INIT packet is received, the state is set
to NetState_inSetup and InputFunc is set to DoMelee.
The state field of a NetConnection is a BattleStateData structure.
NetState_inSetup is the state in which the fleet configuration is negotiated.
Only the side who has myTurn set may send this ship information, by means
of FLEET and TEAMNAME packets.
The Turn negotiation is used to change myTurn.
The Confirm negotiation is used to end this state and go to
NetState_preBattle. At this time InputFunc is set to DoPreMelee.
The state field of a NetConnection is a BattleStateData structure.
NetState_preBattle is used for non-interactive battle negotiations.
One side sends the random seed; the other receives it.
The Ready negotiation is used to end this state and go to
NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
NetState_interBattle is used to allow either side to do some local
initialisations before moving on.
The Ready negotiation is used to end this state and go to
NetState_selectShip, or if there are no more ships to be selected,
to NetState_inBattle.
The state field of a NetConnection is a BattleStateData structure.
NetState_selectShip is where a side may select his ship. The other
side is waiting for notice of this selection.
As soon as the selection has been sent or received, the state is changed
back to NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
NetState_inBattle is where the actual melee takes place.
Both sides send their input until the game is over, at which point
the Ready negotiation is used to end this state and go to
the NetState_endingBattle state. Until the Ready negotiation has been
completed, the simulation is continuing.
The state field of a NetConnection is a BattleStateData structure.
NetState_endingBattle is where the local side waits for the remote
battle frame count, after it has sent its own. When it arrives,
the state changes to NetState_endingBattle2.
The state field of a NetConnection is a BattleStateData structure.
NetState_endingBattle2 is where the side with the lowest battle frame count
catches up with the other other side, while the other side waits.
The Ready negotiation is used to signal that each side is ready,
and the state changes back to NetState_interBattle.
The state field of a NetConnection is a BattleStateData structure.
When a connection is aborted, the state is returned to NetState_unconnected.
+103
View File
@@ -0,0 +1,103 @@
High priority items:
- test compilation on release mode (on both Linux and Windows)
Medium-priority:
- Test disconnection at various points.
Whenever there's a call to NetInput(), connections may get lost, and
this needs to be checked.
- Setup menu for network options.
- Don't check PlayerControl in netmelee etc. Make it an invariant that
(netConnections[player] != NULL) if
((PlayerControl[player] & NETWORK_CONTROL) == NETWORK_CONTROL)
- For the battle ending synchronisation, set the end at at least
getBattleInputDelay() + 1 frames in the future (instead of just 1),
so that there will be no hickup during the end synchronisation.
Also check this value for incoming packets.
- Disconnect notification.
- If a player only moves away from 'Battle!' there's no need for the other
to have to reconfirm.
- decent pause handling
- make compilation of crc.c and checksum.c conditional.
- negotiate checksum interval
- disconnect during battle will trigger the setup disconnect notification.
Low-priority:
- Different coloured icons for unconnected/connecting/connected.
- Send error packets; at least when there's a protocol version mismatch.
- Check whether the random seed and frame delay have been agreed before
continuing (in doConfirmSettings).
- Replacement for TOS. It is IPv4 only.
- Integrate network check functions with doInput
It will be easy to get rid of the separate threads then too.
- The state changes from interBattle to interBattle. That shouldn't happen,
but it doesn't seem to cause any problems. Need to investigate.
Addition: negotiateReadyConnections() is called again just to make sure
all sides pass this checkpoint. This is not a problem. It should be
documented in STATES though.
- More files define NETCONNECTION_INTERNAL than they should.
- voice transmission during the game (using an external lib)
read ramjee94adaptive.pdf
- Keep-alive packets. Store time of last packet sent, use alarms to determine
when to send the next one. Count received messages?
Future improvements/optimisations:
- For BSD sockets: use dup2() to move fds to lower values, so that less fds
have to be checked on select().
- Use writev() to send multiple packets in one syscall, instead of
calling send() for each packet.
- Refusing games with both parties network controlled is not always
necessary. In theory it should be possible to have a client work
as "proxy". The client can watch as the actual players play the game.
Checking for "loops" would be tricky (eventually there should be a human
or computer controller for each side).
- Concurrent selection of ships. Note that if this is handled properly, it
will also be easy to take care of the "Allow Shofixti to choose last" bug.
Note that one party will still have to send his choice to the other side
first, which may be "eploited". Encryption would take care of this,
but at the least make sure the same player who gets to chose first
every time.
- meta-server. Use HTTP? Existing libs can be used, no problems with NAT,
human-readable. Speed is not an issue.
- move to UDP. Repeat past battleinput packets for each new packet that
is sent until they are confirmed.
Bugs:
- as positions are dependant on the screen resolution, you won't be able
to keep sync on games with a different resolution.
- Both sides need identical battle frame rates. This value is not
negotiated.
- When melee selection is aborted, the game crashes.
Final actions:
- Check out TODO, XXX, WORK tags
- memleak testing
- check for and remove mtrace()/muntrace() calls.
- update documentation
- FILES, also note which part of the separation they are in
- check coding style (search for '\>(')
- compile with maximum warnings
When done:
- cvs diff
- Update uqm version number
- Update changelog
To put in the announcement of Netplay:
- Slow connections is acceptable. Packet loss isn't.
Bugs and todos unrelated to netplay.
- When you insert a new ship, the cursor moves to the next square,
but the picture at the right doesn't change.
- The "Battle!" icon is not positioned correctly.
- other player being able to choose the next ship after 3 seconds
of inactivity
- DoRunAway() shouldn't be handled in ProcessInput()
+42
View File
@@ -0,0 +1,42 @@
To use Netplay you need to pass some parameters to uqm. In future versions
you should be able to set everything you need to set from within the
game itself.
--netport1 specifies the port the bottom player will connect to or accept
incoming connections on. If this parameter is not specified, 21837 will
be used.
Your firewall needs to be set up to allow TCP connections from/to
the used port.
--nethost1 specifies the name or ip number of the host to connect to for
the bottom player. If this parameter is not specified, UQM will
instead accept incoming connections.
--netport2
As --netport1, but for the top player
--nethost2
As --netport2, but for the bottom player
--netdelay
The desired input delay (in frames). A keypress or keyrelease
will only take effect after this many frames. The result is that while
higher values make your ship to seem to respond more sluggishly,
they give the keypress information more time to reach the remote party.
If the game stutters, this is because it is waiting for this
keypress information to arrive, which is an indication that the
input delay is too low.
As SuperMelee runs at 24 frames per second, each frame delayed will
delay the input by about 42ms.
The delay used is the maximum of the desired value for both parties.
The default is 0. Values lower than 4 will be hardly noticable
in terms of responsiveness.
Future versions may automatically decide on the best value to use.
In SuperMelee, there is now an extra control method, "network control".
If you change the control method to that, and press LEFT, UQM will start
paying attention to incoming connections or try to establish an outgoing
connection, depending on the specified parameters. Pressing RIGHT will
cancel connection attempts or abort an already established connection.
You'll get a message when a connection has been established. In the mean
time you can already set up your own team.
The game won't start until both parties have confirmed the current setup.
If one player moves away from the 'Battle!' button, both parties need to
reconfirm.
+285 -12
View File
@@ -44,7 +44,7 @@ RSC=rc.exe
# PROP Ignore_Export_Lib 0 # PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # 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 # SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG"
@@ -52,8 +52,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo # ADD BASE BSC32 /nologo
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe 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 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 /nologo /subsystem:windows /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 # Begin Special Build Tool
SOURCE="$(InputPath)" SOURCE="$(InputPath)"
PostBuild_Desc=Stripping debug info... PostBuild_Desc=Stripping debug info...
@@ -74,7 +74,7 @@ PostBuild_Cmds=rebase -b 0x400000 -x . "../../uqm.exe"
# PROP Ignore_Export_Lib 0 # PROP Ignore_Export_Lib 0
# PROP Target_Dir "" # PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # 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 # SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG"
@@ -82,8 +82,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo # ADD BASE BSC32 /nologo
# ADD BSC32 /nologo /o"UrQuanMasters.bsc" # ADD BSC32 /nologo /o"UrQuanMasters.bsc"
LINK32=link.exe 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 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 /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 # SUBTRACT LINK32 /nodefaultlib
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel" !ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
@@ -102,7 +102,7 @@ LINK32=link.exe
# PROP Target_Dir "" # 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 # 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 # 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 # SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD 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 BASE BSC32 /nologo /o"UrQuanMasters.bsc"
# ADD BSC32 /nologo /o"UrQuanMasters.bsc" # ADD BSC32 /nologo /o"UrQuanMasters.bsc"
LINK32=link.exe 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 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 /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 # SUBTRACT LINK32 /nodefaultlib
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel" !ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
@@ -130,7 +130,7 @@ LINK32=link.exe
# PROP Target_Dir "" # 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 # 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 # 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 # SUBTRACT CPP /YX
# ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG"
@@ -138,8 +138,8 @@ BSC32=bscmake.exe
# ADD BASE BSC32 /nologo # ADD BASE BSC32 /nologo
# ADD BSC32 /nologo # ADD BSC32 /nologo
LINK32=link.exe 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 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 /nologo /subsystem:windows /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 # Begin Special Build Tool
SOURCE="$(InputPath)" SOURCE="$(InputPath)"
PostBuild_Desc=Stripping debug info... PostBuild_Desc=Stripping debug info...
@@ -1333,6 +1333,114 @@ SOURCE=..\sc2code\libs\mikmod\virtch2.c
SOURCE=..\sc2code\libs\mikmod\virtch_common.c SOURCE=..\sc2code\libs\mikmod\virtch_common.c
# End Source File # End Source File
# End Group # 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 # Begin Source File
SOURCE=..\sc2code\libs\alarm.h SOURCE=..\sc2code\libs\alarm.h
@@ -1387,6 +1495,10 @@ SOURCE=..\sc2code\libs\misc.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=..\sc2code\libs\net.h
# End Source File
# Begin Source File
SOURCE=..\sc2code\libs\platform.h SOURCE=..\sc2code\libs\platform.h
# End Source File # End Source File
# Begin Source File # Begin Source File
@@ -2333,6 +2445,167 @@ SOURCE=..\sc2code\comm\zoqfot\zoqfotc.c
SOURCE=..\sc2code\comm\commall.h SOURCE=..\sc2code\comm\commall.h
# End Source File # 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 # End Group
# Begin Group "planets" # Begin Group "planets"
-1
View File
@@ -110,4 +110,3 @@ readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
} }
#endif #endif
+272
View File
@@ -128,6 +128,278 @@ typedef unsigned short wchar_t;
typedef unsigned int wint_t; typedef unsigned int wint_t;
#endif #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 // Use SDL_INCLUDE and SDL_IMAGE_INCLUDE to portably include the SDL files
// from the right location. // from the right location.
// TODO: Where the SDL and SDL_image headers are located could be detected // 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 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 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" tactrans.c trans.c uqmdebug.c util.c velocity.c weapon.c"
if [ -n "$uqm_NETPLAY" ]; then
uqm_SUBDIRS="$uqm_SUBDIRS netplay"
fi
+207 -10
View File
@@ -21,6 +21,12 @@
#include "controls.h" #include "controls.h"
#include "init.h" #include "init.h"
#include "intel.h" #include "intel.h"
#ifdef NETPLAY
# include "netplay/netmelee.h"
# ifdef NETPLAY_CHECKSUM
# include "netplay/checksum.h"
# endif
#endif
#include "resinst.h" #include "resinst.h"
#include "setup.h" #include "setup.h"
#include "settings.h" #include "settings.h"
@@ -32,6 +38,15 @@
QUEUE disp_q; QUEUE disp_q;
SIZE battle_counter; SIZE battle_counter;
BOOLEAN instantVictory; 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 static BOOLEAN
RunAwayAllowed (void) RunAwayAllowed (void)
@@ -67,7 +82,7 @@ DoRunAway (STARSHIPPTR StarShipPtr)
~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED); ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
SetPrimColor (&DisplayArray[ElementPtr->PrimIndex], 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); SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMPFILL_PRIM);
CyborgDescPtr->ship_input_state = 0; CyborgDescPtr->ship_input_state = 0;
@@ -75,17 +90,54 @@ DoRunAway (STARSHIPPTR StarShipPtr)
UnlockElement (StarShipPtr->hShip); 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 static void
ProcessInput (void) ProcessInput (void)
{ {
BOOLEAN CanRunAway; BOOLEAN CanRunAway;
size_t sideI;
#ifdef NETPLAY
netInput ();
#endif
CanRunAway = RunAwayAllowed (); CanRunAway = RunAwayAllowed ();
for (cur_player = NUM_SIDES - 1; cur_player >= 0; --cur_player) for (sideI = 0; sideI < NUM_SIDES; sideI++)
{ {
HSTARSHIP hBattleShip, hNextShip; HSTARSHIP hBattleShip, hNextShip;
cur_player = battleInputOrder[sideI];
for (hBattleShip = GetHeadLink (&race_q[cur_player]); for (hBattleShip = GetHeadLink (&race_q[cur_player]);
hBattleShip != 0; hBattleShip = hNextShip) hBattleShip != 0; hBattleShip = hNextShip)
{ {
@@ -99,10 +151,24 @@ ProcessInput (void)
{ {
CyborgDescPtr = StarShipPtr; CyborgDescPtr = StarShipPtr;
InputState = (*(PlayerInput[cur_player]))(); InputState = (*(PlayerInput[cur_player]))(cur_player,
StarShipPtr);
#if CREATE_JOURNAL #if CREATE_JOURNAL
JournalInput (InputState); JournalInput (InputState);
#endif /* CREATE_JOURNAL */ #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; CyborgDescPtr->ship_input_state = 0;
if (CyborgDescPtr->RaceDescPtr->ship_info.crew_level) if (CyborgDescPtr->RaceDescPtr->ship_info.crew_level)
@@ -129,6 +195,10 @@ ProcessInput (void)
} }
} }
#ifdef NETPLAY
flushPacketQueues ();
#endif
if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT)) if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT))
GLOBAL (CurrentActivity) &= ~IN_BATTLE; GLOBAL (CurrentActivity) &= ~IN_BATTLE;
} }
@@ -180,7 +250,39 @@ DoBattle (BATTLE_STATE *bs)
bs->MenuRepeatDelay = 0; bs->MenuRepeatDelay = 0;
SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE); 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 (); 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); LockMutex (GraphicsLock);
if (bs->first_time) if (bs->first_time)
{ {
@@ -220,7 +322,79 @@ DoBattle (BATTLE_STATE *bs)
+ BATTLE_FRAME_RATE / (battle_speed + 1)); + BATTLE_FRAME_RATE / (battle_speed + 1));
bs->NextTime = GetTimeCounter (); 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 BOOLEAN
@@ -233,7 +407,10 @@ Battle (void)
SetResourceIndex (hResIndex); SetResourceIndex (hResIndex);
#if !(DEMO_MODE || CREATE_JOURNAL) #if !(DEMO_MODE || CREATE_JOURNAL)
if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE) {
// In Supermelee, the RNG is already initialised.
TFB_SeedRandom (GetTimeCounter ()); TFB_SeedRandom (GetTimeCounter ());
}
#else /* DEMO_MODE */ #else /* DEMO_MODE */
if (BattleSeed == 0) if (BattleSeed == 0)
BattleSeed = TFB_Random (); BattleSeed = TFB_Random ();
@@ -262,21 +439,41 @@ Battle (void)
CountLinks (&race_q[1]) CountLinks (&race_q[1])
); );
while (num_ships--) if (!selectAllShips (num_ships))
{
if (!GetNextStarShip (NULL_PTR, num_ships))
goto AbortBattle; goto AbortBattle;
}
BattleSong (TRUE); BattleSong (TRUE);
bs.NextTime = 0; bs.NextTime = 0;
bs.InputFunc = &DoBattle; setupBattleInputOrder ();
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE); #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); UnlockMutex (GraphicsLock);
DoInput ((PVOID)&bs, FALSE); DoInput ((PVOID)&bs, FALSE);
LockMutex (GraphicsLock); LockMutex (GraphicsLock);
AbortBattle: AbortBattle:
#ifdef NETPLAY
uninitBattleInputBuffers();
#ifdef NETPLAY_CHECKSUM
uninitChecksumBuffers ();
#endif
#endif /* NETPLAY */
StopMusic (); StopMusic ();
StopSound (); StopSound ();
} }
+4
View File
@@ -22,6 +22,10 @@
extern QUEUE disp_q; extern QUEUE disp_q;
extern SIZE battle_counter; extern SIZE battle_counter;
extern BOOLEAN instantVictory; extern BOOLEAN instantVictory;
#ifdef NETPLAY
typedef DWORD BattleFrameCounter;
extern BattleFrameCounter battleFrameCount;
#endif
extern BOOLEAN Battle (void); extern BOOLEAN Battle (void);
+2 -4
View File
@@ -60,8 +60,6 @@
static int ambient_anim_task (void *data); 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 MAX_RESPONSES 8
#define BACKGROUND_VOL \ #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 // pText is the text to be fitted. pText->CharCount will be set to the
// number of characters that fitted. // number of characters that fitted.
// startNext will be filled with the start of the first word that // 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 // doesn't fit in one line, or if an entire line fits, to the character
// past the newline, or if the entire string fitted, to the end of the // past the newline, or if the entire string fits, to the end of the
// string. // string.
// maxWidth is the maximum number of pixels that a line may be wide // 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 // 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 void uninit_communication (void);
extern int do_subtitles (UNICODE *pStr); extern int do_subtitles (UNICODE *pStr);
extern void AlienTalkSegue (COUNT wait_track); extern void AlienTalkSegue (COUNT wait_track);
BOOLEAN getLineWithinWidth(TEXT *pText, const unsigned char **startNext,
SIZE maxWidth, COUNT maxChars);
extern RECT CommWndRect; /* comm window rect */ extern RECT CommWndRect; /* comm window rect */
+8 -4
View File
@@ -19,6 +19,9 @@
#ifndef _CONTROLS_H_ #ifndef _CONTROLS_H_
#define _CONTROLS_H_ #define _CONTROLS_H_
#include "races.h"
// For STARSHIPPTR
#include "libs/compiler.h" #include "libs/compiler.h"
#include "libs/strlib.h" #include "libs/strlib.h"
@@ -85,8 +88,9 @@ typedef UBYTE BATTLE_INPUT_STATE;
#define BATTLE_ESCAPE ((BATTLE_INPUT_STATE)(1 << 5)) #define BATTLE_ESCAPE ((BATTLE_INPUT_STATE)(1 << 5))
#define BATTLE_DOWN ((BATTLE_INPUT_STATE)(1 << 6)) #define BATTLE_DOWN ((BATTLE_INPUT_STATE)(1 << 6))
typedef BATTLE_INPUT_STATE (*battle_summary_func) (void); typedef BATTLE_INPUT_STATE (*battle_summary_func) (COUNT player,
extern battle_summary_func ComputerInput, HumanInput[]; STARSHIPPTR StarShipPtr);
extern battle_summary_func ComputerInput, HumanInput[], NetworkInput;
extern battle_summary_func PlayerInput[]; extern battle_summary_func PlayerInput[];
extern CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState; extern CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState;
@@ -107,8 +111,8 @@ extern void WaitForNoInput (SIZE Duration);
extern BOOLEAN ConfirmExit (void); extern BOOLEAN ConfirmExit (void);
extern void DoInput (PVOID pInputState, BOOLEAN resetInput); extern void DoInput (PVOID pInputState, BOOLEAN resetInput);
BATTLE_INPUT_STATE p1_combat_summary (void); BATTLE_INPUT_STATE p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
BATTLE_INPUT_STATE p2_combat_summary (void); BATTLE_INPUT_STATE p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
extern volatile BOOLEAN GamePaused, ExitRequested; extern volatile BOOLEAN GamePaused, ExitRequested;
+4 -5
View File
@@ -1010,7 +1010,7 @@ Avoid (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR EvalDescPtr)
} }
BATTLE_INPUT_STATE BATTLE_INPUT_STATE
tactical_intelligence (void) tactical_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
{ {
ELEMENTPTR ShipPtr; ELEMENTPTR ShipPtr;
ELEMENT Ship; ELEMENT Ship;
@@ -1019,10 +1019,9 @@ tactical_intelligence (void)
COUNT ConcernCounter; COUNT ConcernCounter;
EVALUATE_DESC ObjectsOfConcern[10]; EVALUATE_DESC ObjectsOfConcern[10];
BOOLEAN ShipMoved, UltraManeuverable; BOOLEAN ShipMoved, UltraManeuverable;
STARSHIPPTR StarShipPtr, EnemyStarShipPtr; STARSHIPPTR EnemyStarShipPtr;
RACE_DESCPTR RDPtr, EnemyRDPtr; RACE_DESCPTR RDPtr, EnemyRDPtr;
StarShipPtr = CyborgDescPtr;
RDPtr = StarShipPtr->RaceDescPtr; RDPtr = StarShipPtr->RaceDescPtr;
if (RDPtr->cyborg_control.ManeuverabilityIndex == 0) if (RDPtr->cyborg_control.ManeuverabilityIndex == 0)
@@ -1037,7 +1036,7 @@ tactical_intelligence (void)
} }
ShipMoved = TRUE; ShipMoved = TRUE;
if (PlayerControl[cur_player] & STANDARD_RATING) if (PlayerControl[player] & STANDARD_RATING)
++StarShipPtr->special_counter; ++StarShipPtr->special_counter;
#ifdef DEBUG_CYBORG #ifdef DEBUG_CYBORG
@@ -1260,7 +1259,7 @@ if (!(ShipPtr->state_flags & FINITE_LIFE)
ed.which_turn = 0; ed.which_turn = 0;
} }
} }
else if (!(PlayerControl[cur_player] & AWESOME_RATING)) else if (!(PlayerControl[player] & AWESOME_RATING))
ed.which_turn = 0; ed.which_turn = 0;
else else
{ {
+2
View File
@@ -43,6 +43,7 @@
typedef QUEUE_HANDLE HELEMENT; typedef QUEUE_HANDLE HELEMENT;
// Bits for ELEMENT_FLAGS:
#define GOOD_GUY (1 << 0) #define GOOD_GUY (1 << 0)
#define BAD_GUY (1 << 1) #define BAD_GUY (1 << 1)
#define PLAYER_SHIP (1 << 2) #define PLAYER_SHIP (1 << 2)
@@ -65,6 +66,7 @@ typedef QUEUE_HANDLE HELEMENT;
#define CREW_OBJECT (1 << 14) #define CREW_OBJECT (1 << 14)
#define BACKGROUND_OBJECT (1 << 15) #define BACKGROUND_OBJECT (1 << 15)
#define HYPERJUMP_LIFE 15 #define HYPERJUMP_LIFE 15
#define NUM_EXPLOSION_FRAMES 12 #define NUM_EXPLOSION_FRAMES 12
+1 -1
View File
@@ -260,7 +260,7 @@ InitGalaxy (void)
{ {
SetPrimType (&DisplayArray[p], STAMP_PRIM); SetPrimType (&DisplayArray[p], STAMP_PRIM);
SetPrimColor (&DisplayArray[p], 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; DisplayArray[p].Object.Stamp.frame = stars_in_space;
} }
else else
+9 -3
View File
@@ -26,7 +26,7 @@
#include "libs/threadlib.h" #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]; battle_summary_func PlayerInput[NUM_PLAYERS];
#define ACCELERATION_INCREMENT (ONE_SECOND / 12) #define ACCELERATION_INCREMENT (ONE_SECOND / 12)
@@ -379,7 +379,7 @@ GetMenuSounds (MENU_SOUND_FLAGS *s0, MENU_SOUND_FLAGS *s1)
/* These can really be refactored */ /* These can really be refactored */
BATTLE_INPUT_STATE BATTLE_INPUT_STATE
p1_combat_summary (void) p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
{ {
BATTLE_INPUT_STATE InputState = 0; BATTLE_INPUT_STATE InputState = 0;
if (CurrentInputState.key[PlayerOne][KEY_UP]) if (CurrentInputState.key[PlayerOne][KEY_UP])
@@ -396,11 +396,14 @@ p1_combat_summary (void)
InputState |= BATTLE_ESCAPE; InputState |= BATTLE_ESCAPE;
if (CurrentInputState.key[PlayerOne][KEY_DOWN]) if (CurrentInputState.key[PlayerOne][KEY_DOWN])
InputState |= BATTLE_DOWN; InputState |= BATTLE_DOWN;
(void) player;
(void) StarShipPtr;
return InputState; return InputState;
} }
BATTLE_INPUT_STATE BATTLE_INPUT_STATE
p2_combat_summary (void) p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
{ {
BATTLE_INPUT_STATE InputState = 0; BATTLE_INPUT_STATE InputState = 0;
if (CurrentInputState.key[PlayerTwo][KEY_UP]) if (CurrentInputState.key[PlayerTwo][KEY_UP])
@@ -415,6 +418,9 @@ p2_combat_summary (void)
InputState |= BATTLE_SPECIAL; InputState |= BATTLE_SPECIAL;
if (CurrentInputState.key[PlayerTwo][KEY_DOWN]) if (CurrentInputState.key[PlayerTwo][KEY_DOWN])
InputState |= BATTLE_DOWN; InputState |= BATTLE_DOWN;
(void) player;
(void) StarShipPtr;
return InputState; return InputState;
} }
+3 -1
View File
@@ -46,6 +46,7 @@
#define FLAGSHIP_STRING_COUNT 13 #define FLAGSHIP_STRING_COUNT 13
#define ORBITSCAN_STRING_COUNT 19 #define ORBITSCAN_STRING_COUNT 19
#define MAINMENU_STRING_COUNT 56 #define MAINMENU_STRING_COUNT 56
#define NETMELEE_STRING_COUNT 16
enum { enum {
STAR_STRING_BASE = 0, STAR_STRING_BASE = 0,
@@ -69,8 +70,9 @@ enum {
FLAGSHIP_STRING_BASE = STATUS_STRING_BASE + STATUS_STRING_COUNT, FLAGSHIP_STRING_BASE = STATUS_STRING_BASE + STATUS_STRING_COUNT,
ORBITSCAN_STRING_BASE = FLAGSHIP_STRING_BASE + FLAGSHIP_STRING_COUNT, ORBITSCAN_STRING_BASE = FLAGSHIP_STRING_BASE + FLAGSHIP_STRING_COUNT,
MAINMENU_STRING_BASE = ORBITSCAN_STRING_BASE + ORBITSCAN_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; SIZE cur_player;
BATTLE_INPUT_STATE BATTLE_INPUT_STATE
computer_intelligence (void) computer_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
{ {
BATTLE_INPUT_STATE InputState; BATTLE_INPUT_STATE InputState;
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
return 0; 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 // allow a player to warp-escape in cyborg mode
if (cur_player == 0) if (player == 0)
InputState |= (*(HumanInput[cur_player])) () & BATTLE_ESCAPE; InputState |= (*(HumanInput[player])) (player, StarShipPtr)
& BATTLE_ESCAPE;
} }
else 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; InputState = 0;
else else
{ {
+8 -5
View File
@@ -63,8 +63,10 @@ enum
extern STARSHIPPTR CyborgDescPtr; extern STARSHIPPTR CyborgDescPtr;
extern SIZE cur_player; extern SIZE cur_player;
extern BATTLE_INPUT_STATE computer_intelligence (void); extern BATTLE_INPUT_STATE computer_intelligence (COUNT player,
extern BATTLE_INPUT_STATE tactical_intelligence (void); STARSHIPPTR StarShipPtr);
extern BATTLE_INPUT_STATE tactical_intelligence (COUNT player,
STARSHIPPTR StarShipPtr);
extern void ship_intelligence (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR extern void ship_intelligence (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR
ObjectsOfConcern, COUNT ConcernCounter); ObjectsOfConcern, COUNT ConcernCounter);
extern BOOLEAN ship_weapons (ELEMENTPTR ShipPtr, ELEMENTPTR OtherPtr, 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 HUMAN_CONTROL (BYTE)(1 << 0)
#define CYBORG_CONTROL (BYTE)(1 << 1) #define CYBORG_CONTROL (BYTE)(1 << 1)
#define PSYTRON_CONTROL (BYTE)(1 << 2) #define PSYTRON_CONTROL (BYTE)(1 << 2)
#define NETWORK_CONTROL (BYTE)(1 << 3)
#define COMPUTER_CONTROL (CYBORG_CONTROL | PSYTRON_CONTROL) #define COMPUTER_CONTROL (CYBORG_CONTROL | PSYTRON_CONTROL)
#define STANDARD_RATING (BYTE)(1 << 3) #define STANDARD_RATING (BYTE)(1 << 4)
#define GOOD_RATING (BYTE)(1 << 4) #define GOOD_RATING (BYTE)(1 << 5)
#define AWESOME_RATING (BYTE)(1 << 5) #define AWESOME_RATING (BYTE)(1 << 6)
#endif /* _INTEL_H */ #endif /* _INTEL_H */
+1 -1
View File
@@ -1,5 +1,5 @@
uqm_SUBDIRS="callback decomp file graphics heap input list math memory 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 if [ -n "$uqm_USE_INTERNAL_MIKMOD" ]; then
uqm_SUBDIRS="$uqm_SUBDIRS mikmod" uqm_SUBDIRS="$uqm_SUBDIRS mikmod"
fi 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 */
+961 -169
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/sndlib.h"
#include "libs/reslib.h" #include "libs/reslib.h"
typedef struct melee_state MELEE_STATE;
enum enum
{ {
@@ -68,7 +69,7 @@ enum
extern FRAME PickMeleeFrame; 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_TEAM_CHARS 30
#define MAX_VIS_TEAMS 5 #define MAX_VIS_TEAMS 5
@@ -82,11 +83,12 @@ typedef COUNT FleetShipIndex;
typedef struct typedef struct
{ {
BYTE ShipList[MELEE_FLEET_SIZE]; BYTE ShipList[MELEE_FLEET_SIZE];
UNICODE TeamName[MAX_TEAM_CHARS + 25]; /* in case default names in starcon.txt UNICODE TeamName[MAX_TEAM_CHARS + 1 + 24];
are unknowingly mangled */ /* The +1 is for the terminating \0; the +24 is in case some
* default name in starcon.txt is unknowingly mangled. */
} TEAM_IMAGE; } TEAM_IMAGE;
typedef struct melee_state struct melee_state
{ {
BOOLEAN (*InputFunc) (struct melee_state *pInputState); BOOLEAN (*InputFunc) (struct melee_state *pInputState);
COUNT MenuRepeatDelay; COUNT MenuRepeatDelay;
@@ -104,13 +106,24 @@ typedef struct melee_state
TEAM_IMAGE PreBuiltList[NUM_PREBUILT]; TEAM_IMAGE PreBuiltList[NUM_PREBUILT];
MUSIC_REF hMusic; MUSIC_REF hMusic;
} MELEE_STATE; };
typedef MELEE_STATE *PMELEE_STATE; typedef MELEE_STATE *PMELEE_STATE;
extern PMELEE_STATE volatile pMeleeState; extern PMELEE_STATE volatile pMeleeState;
extern void Melee (void); 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 */ #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;
}

Some files were not shown because too many files have changed in this diff Show More