Netplay
git-svn-id: svn://svn.code.sf.net/p/sc2/code/trunk@2463 8092fc87-c524-0410-9efc-e669fe64eaf9
This commit is contained in:
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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=""
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -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"
|
||||||
|
|
||||||
|
|||||||
@@ -110,4 +110,3 @@ readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+272
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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 ();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,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
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
uqm_SUBDIRS="connect netmanager socket"
|
||||||
|
uqm_CFILES="netport.c network_bsd.c"
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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) */
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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
File diff suppressed because it is too large
Load Diff
+18
-5
@@ -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 */
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -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"
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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 */
|
||||||
|
|
||||||
@@ -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
Reference in New Issue
Block a user