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:
|
||||
- Netplay - SvdB
|
||||
- Added step-by-step instructions for compilation on MSVC++ - SvdB
|
||||
- All output done in binary mode, due to uio/Win32 conflicts (#912) - 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:\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
|
||||
part of the screen, click on "Input".
|
||||
- In the right part, select "Ignore Specific Library", and remove
|
||||
"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 "..."
|
||||
- In the right part, select "Additional Dependencies", then click on "..."
|
||||
- Remove odbc32.lib and odbccp32.lib
|
||||
|
||||
5.6. Add user32.lib
|
||||
5.5. 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_USE_ZIP_IO='@USE_ZIP_IO@'
|
||||
uqm_USE_PLATFORM_ACCEL='@USE_PLATFORM_ACCEL@'
|
||||
uqm_NETPLAY='@NETPLAY@'
|
||||
DEBUG='@DEBUG@'
|
||||
export BUILD_SYSTEM HOST_SYSTEM DEBUG
|
||||
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()
|
||||
{
|
||||
# Describe the menu:
|
||||
MENU_main_ITEMS="debug graphics sound mikmod ovcodec joystick \
|
||||
MENU_main_ITEMS="debug graphics sound mikmod ovcodec netplay joystick \
|
||||
ioformat accel"
|
||||
case "$HOST_SYSTEM" in
|
||||
Darwin)
|
||||
@@ -72,6 +72,7 @@ uqm_prepare_config()
|
||||
MENU_main_ITEM_sound_TYPE=CHOICE
|
||||
MENU_main_ITEM_mikmod_TYPE=CHOICE
|
||||
MENU_main_ITEM_ovcodec_TYPE=CHOICE
|
||||
MENU_main_ITEM_netplay_TYPE=CHOICE
|
||||
MENU_main_ITEM_joystick_TYPE=CHOICE
|
||||
MENU_main_ITEM_ioformat_TYPE=CHOICE
|
||||
MENU_main_ITEM_accel_TYPE=CHOICE
|
||||
@@ -210,6 +211,27 @@ uqm_prepare_config()
|
||||
CHOICE_joystick_OPTION_disabled_TITLE="disabled"
|
||||
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_TITLE="Supported file i/o methods"
|
||||
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.
|
||||
SUBSTITUTE_VARS="BUILD_SYSTEM HOST_SYSTEM COMPILE CFLAGS LDFLAGS \
|
||||
MAKE ECHON SED DEBUG JOYSTICK \
|
||||
MAKE ECHON SED DEBUG JOYSTICK NETPLAY \
|
||||
SOUNDMODULE USE_INTERNAL_MIKMOD \
|
||||
HAVE_OPENGL HAVE_REGEX_H_FLAG USE_ZIP_IO \
|
||||
USE_PLATFORM_ACCEL \
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
BUILDLOG=/dev/null
|
||||
BUILDLOG=/tmp/build.log
|
||||
TEMPFILE="/tmp/build.$$.tmp"
|
||||
|
||||
|
||||
|
||||
@@ -90,8 +90,8 @@ case "$HOST_SYSTEM" in
|
||||
LIB_SDL_image_LDFLAGS="-framework SDL_image"
|
||||
;;
|
||||
*)
|
||||
LIB_SDL_image_CFLAGS="$LIB_SDL_CFLAGS"
|
||||
LIB_SDL_image_LDFLAGS="$LIB_SDL_LDFLAGS -lSDL_image"
|
||||
LIB_SDL_image_CFLAGS="$LIB_SDL_CFLAGS -I/opt/SDL_image/include/SDL"
|
||||
LIB_SDL_image_LDFLAGS="$LIB_SDL_LDFLAGS -L/opt/SDL_image/lib -lSDL_image"
|
||||
;;
|
||||
esac
|
||||
LIB_SDL_image_VERSION=""
|
||||
|
||||
@@ -2246,3 +2246,65 @@ dispatch
|
||||
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.29.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 Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
@@ -52,8 +52,8 @@ BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /machine:I386
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=Stripping debug info...
|
||||
@@ -74,7 +74,7 @@ PostBuild_Cmds=rebase -b 0x400000 -x . "../../uqm.exe"
|
||||
# PROP Ignore_Export_Lib 0
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /W3 /GX /Zi /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
@@ -82,8 +82,8 @@ BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# SUBTRACT LINK32 /nodefaultlib
|
||||
|
||||
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Debug NoAccel"
|
||||
@@ -102,7 +102,7 @@ LINK32=link.exe
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "ZLIB_DLL" /D "USE_PLATFORM_ACCEL" /FR /FD /GZ /c
|
||||
# SUBTRACT BASE CPP /YX
|
||||
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /GZ /c
|
||||
# ADD CPP /nologo /MDd /W3 /GX /Zi /Od /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "DEBUG" /D "_DEBUG" /D "DEBUG_TRACK_SEM" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /GZ /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "_DEBUG"
|
||||
# ADD RSC /l 0x409 /d "_DEBUG"
|
||||
@@ -110,8 +110,8 @@ BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo /o"UrQuanMasters.bsc"
|
||||
# ADD BSC32 /nologo /o"UrQuanMasters.bsc"
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /debug /machine:I386 /nodefaultlib:"msvcrt.lib" /out:"../../uqmdebug.exe" /pdbtype:sept
|
||||
# SUBTRACT LINK32 /nodefaultlib
|
||||
|
||||
!ELSEIF "$(CFG)" == "UrQuanMasters - Win32 Release NoAccel"
|
||||
@@ -130,7 +130,7 @@ LINK32=link.exe
|
||||
# PROP Target_Dir ""
|
||||
# ADD BASE CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "ZLIB_DLL" /D "USE_PLATFORM_ACCEL" /FD /c
|
||||
# SUBTRACT BASE CPP /YX
|
||||
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /c
|
||||
# ADD CPP /nologo /MD /W3 /GX /Zi /O2 /I "." /I ".." /I "..\sc2code" /I "..\sc2code\libs" /I "..\sc2code\ships" /I "..\regex" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D _VW=320 /D _VH=240 /D "HAVE_OPENGL" /D "GFXMODULE_SDL" /D "HAVE_OPENAL" /D "HAVE_ZIP" /D "HAVE_JOYSTICK" /D "NETPLAY" /D "ZLIB_DLL" /D "USE_INTERNAL_MIKMOD" /FR /FD /c
|
||||
# SUBTRACT CPP /YX
|
||||
# ADD BASE RSC /l 0x409 /d "NDEBUG"
|
||||
# ADD RSC /l 0x409 /d "NDEBUG"
|
||||
@@ -138,8 +138,8 @@ BSC32=bscmake.exe
|
||||
# ADD BASE BSC32 /nologo
|
||||
# ADD BSC32 /nologo
|
||||
LINK32=link.exe
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:console /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib zdll.lib ws2_32.lib /nologo /subsystem:windows /pdb:none /debug /machine:I386 /nodefaultlib:"msvcrtd.lib" /out:"../../uqm.exe"
|
||||
# Begin Special Build Tool
|
||||
SOURCE="$(InputPath)"
|
||||
PostBuild_Desc=Stripping debug info...
|
||||
@@ -1333,6 +1333,114 @@ SOURCE=..\sc2code\libs\mikmod\virtch2.c
|
||||
SOURCE=..\sc2code\libs\mikmod\virtch_common.c
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "network"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\bytesex.h
|
||||
# End Source File
|
||||
# Begin Group "connect"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\connect.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\connect.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\listen.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\listen.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\resolve.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\connect\resolve.h
|
||||
# End Source File
|
||||
# End Group
|
||||
|
||||
# Begin Group "netmanager"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\ndesc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\ndesc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\ndindex.ci
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\netmanager_common.ci
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\netmanager.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\netmanager_win.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netmanager\netmanager_win.h
|
||||
# End Source File
|
||||
# End Group
|
||||
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netport.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\netport.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\network_win.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\network.h
|
||||
# End Source File
|
||||
# Begin Group "socket"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\socket\socket.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\socket\socket.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\socket\socket_win.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\network\socket\socket_win.h
|
||||
# End Source File
|
||||
# End Group
|
||||
|
||||
# End Group
|
||||
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\alarm.h
|
||||
@@ -1387,6 +1495,10 @@ SOURCE=..\sc2code\libs\misc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\net.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\libs\platform.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
@@ -2333,6 +2445,167 @@ SOURCE=..\sc2code\comm\zoqfot\zoqfotc.c
|
||||
|
||||
SOURCE=..\sc2code\comm\commall.h
|
||||
# End Source File
|
||||
# End Group
|
||||
# Begin Group "netplay"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\checksum.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\checksum.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\checkbuf.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\checkbuf.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\crc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\crc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\nc_connect.ci
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netconnection.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netconnection.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netinput.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netinput.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netoptions.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netoptions.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netmelee.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netmelee.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netmisc.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netmisc.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netplay.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netrcv.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netrcv.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netsend.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netsend.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netstate.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\netstate.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\notify.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\notify.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packet.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packet.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packethandlers.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packethandlers.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packetsenders.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packetsenders.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packetq.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\packetq.h
|
||||
# End Source File
|
||||
# Begin Group "proto"
|
||||
|
||||
# PROP Default_Filter ""
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\proto\npconfirm.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\proto\npconfirm.h
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\proto\ready.c
|
||||
# End Source File
|
||||
# Begin Source File
|
||||
|
||||
SOURCE=..\sc2code\netplay\proto\ready.h
|
||||
# End Source File
|
||||
# End Group
|
||||
|
||||
# End Group
|
||||
# Begin Group "planets"
|
||||
|
||||
|
||||
@@ -110,4 +110,3 @@ readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
+272
@@ -128,6 +128,278 @@ typedef unsigned short wchar_t;
|
||||
typedef unsigned int wint_t;
|
||||
#endif
|
||||
|
||||
// errno error numbers. The values used don't matter, as long as they
|
||||
// don't conflict with existing errno error numbers.
|
||||
#if defined (PORT_WANT_ERRNO) && defined (_MSC_VER)
|
||||
# include <errno.h>
|
||||
# ifndef E2BIG
|
||||
# define E2BIG 0x01000001
|
||||
# endif
|
||||
# ifndef EACCES
|
||||
# define EACCES 0x01000002
|
||||
# endif
|
||||
# ifndef EADDRINUSE
|
||||
# define EADDRINUSE 0x01000003
|
||||
# endif
|
||||
# ifndef EADDRNOTAVAIL
|
||||
# define EADDRNOTAVAIL 0x01000004
|
||||
# endif
|
||||
# ifndef EAFNOSUPPORT
|
||||
# define EAFNOSUPPORT 0x01000005
|
||||
# endif
|
||||
# ifndef EAGAIN
|
||||
# ifdef EWOULDBLOCK
|
||||
# define EAGAIN EWOULDBLOCK
|
||||
# else
|
||||
# define EAGAIN 0x01000006
|
||||
# endif
|
||||
# endif
|
||||
# ifndef EALREADY
|
||||
# define EALREADY 0x01000007
|
||||
# endif
|
||||
# ifndef EBADF
|
||||
# define EBADF 0x01000008
|
||||
# endif
|
||||
# ifndef EBADMSG
|
||||
# define EBADMSG 0x01000009
|
||||
# endif
|
||||
# ifndef EBUSY
|
||||
# define EBUSY 0x0100000a
|
||||
# endif
|
||||
# ifndef ECANCELED
|
||||
# define ECANCELED 0x0100000b
|
||||
# endif
|
||||
# ifndef ECHILD
|
||||
# define ECHILD 0x0100000c
|
||||
# endif
|
||||
# ifndef ECONNABORTED
|
||||
# define ECONNABORTED 0x0100000d
|
||||
# endif
|
||||
# ifndef ECONNREFUSED
|
||||
# define ECONNREFUSED 0x0100000e
|
||||
# endif
|
||||
# ifndef ECONNRESET
|
||||
# define ECONNRESET 0x0100000f
|
||||
# endif
|
||||
# ifndef EDEADLK
|
||||
# define EDEADLK 0x01000010
|
||||
# endif
|
||||
# ifndef EDESTADDRREQ
|
||||
# define EDESTADDRREQ 0x01000011
|
||||
# endif
|
||||
# ifndef EDOM
|
||||
# define EDOM 0x01000012
|
||||
# endif
|
||||
// Reserved in POSIX
|
||||
//# ifndef //EDQUOT
|
||||
//# define //EDQUOT 0x01000013
|
||||
//# endif
|
||||
# ifndef EEXIST
|
||||
# define EEXIST 0x01000014
|
||||
# endif
|
||||
# ifndef EFAULT
|
||||
# define EFAULT 0x01000015
|
||||
# endif
|
||||
# ifndef EFBIG
|
||||
# define EFBIG 0x01000016
|
||||
# endif
|
||||
# ifndef EHOSTUNREACH
|
||||
# define EHOSTUNREACH 0x01000017
|
||||
# endif
|
||||
# ifndef EIDRM
|
||||
# define EIDRM 0x01000018
|
||||
# endif
|
||||
# ifndef EILSEQ
|
||||
# define EILSEQ 0x01000019
|
||||
# endif
|
||||
# ifndef EINPROGRESS
|
||||
# define EINPROGRESS 0x0100001a
|
||||
# endif
|
||||
# ifndef EINTR
|
||||
# define EINTR 0x0100001b
|
||||
# endif
|
||||
# ifndef EINVAL
|
||||
# define EINVAL 0x0100001c
|
||||
# endif
|
||||
# ifndef EIO
|
||||
# define EIO 0x0100001d
|
||||
# endif
|
||||
# ifndef EISCONN
|
||||
# define EISCONN 0x0100001e
|
||||
# endif
|
||||
# ifndef EISDIR
|
||||
# define EISDIR 0x0100001f
|
||||
# endif
|
||||
# ifndef ELOOP
|
||||
# define ELOOP 0x01000020
|
||||
# endif
|
||||
# ifndef EMFILE
|
||||
# define EMFILE 0x01000021
|
||||
# endif
|
||||
# ifndef EMLINK
|
||||
# define EMLINK 0x01000022
|
||||
# endif
|
||||
# ifndef EMSGSIZE
|
||||
# define EMSGSIZE 0x01000023
|
||||
# endif
|
||||
// Reserved in POSIX
|
||||
//# ifndef //EMULTIHOP
|
||||
//# define //EMULTIHOP 0x01000024
|
||||
//# endif
|
||||
# ifndef ENAMETOOLONG
|
||||
# define ENAMETOOLONG 0x01000025
|
||||
# endif
|
||||
# ifndef ENETDOWN
|
||||
# define ENETDOWN 0x01000026
|
||||
# endif
|
||||
# ifndef ENETRESET
|
||||
# define ENETRESET 0x01000027
|
||||
# endif
|
||||
# ifndef ENETUNREACH
|
||||
# define ENETUNREACH 0x01000028
|
||||
# endif
|
||||
# ifndef ENFILE
|
||||
# define ENFILE 0x01000029
|
||||
# endif
|
||||
# ifndef ENOBUFS
|
||||
# define ENOBUFS 0x0100002a
|
||||
# endif
|
||||
# ifndef ENODATA
|
||||
# define ENODATA 0x0100002b
|
||||
# endif
|
||||
# ifndef ENODEV
|
||||
# define ENODEV 0x0100002c
|
||||
# endif
|
||||
# ifndef ENOENT
|
||||
# define ENOENT 0x0100002d
|
||||
# endif
|
||||
# ifndef ENOEXEC
|
||||
# define ENOEXEC 0x0100002e
|
||||
# endif
|
||||
# ifndef ENOLCK
|
||||
# define ENOLCK 0x0100002f
|
||||
# endif
|
||||
// Reserved in POSIX
|
||||
//# ifndef ENOLINK
|
||||
//# define ENOLINK 0x01000030
|
||||
//# endif
|
||||
# ifndef ENOMEM
|
||||
# define ENOMEM 0x01000031
|
||||
# endif
|
||||
# ifndef ENOMSG
|
||||
# define ENOMSG 0x01000032
|
||||
# endif
|
||||
# ifndef ENOPROTOOPT
|
||||
# define ENOPROTOOPT 0x01000033
|
||||
# endif
|
||||
# ifndef ENOSPC
|
||||
# define ENOSPC 0x01000034
|
||||
# endif
|
||||
# ifndef ENOSR
|
||||
# define ENOSR 0x01000035
|
||||
# endif
|
||||
# ifndef ENOSTR
|
||||
# define ENOSTR 0x01000036
|
||||
# endif
|
||||
# ifndef ENOSYS
|
||||
# define ENOSYS 0x01000037
|
||||
# endif
|
||||
# ifndef ENOTCONN
|
||||
# define ENOTCONN 0x01000038
|
||||
# endif
|
||||
# ifndef ENOTDIR
|
||||
# define ENOTDIR 0x01000039
|
||||
# endif
|
||||
# ifndef ENOTEMPTY
|
||||
# define ENOTEMPTY 0x0100003a
|
||||
# endif
|
||||
# ifndef ENOTSOCK
|
||||
# define ENOTSOCK 0x0100003b
|
||||
# endif
|
||||
# ifndef ENOTSUP
|
||||
# define ENOTSUP 0x0100003c
|
||||
# endif
|
||||
# ifndef ENOTTY
|
||||
# define ENOTTY 0x0100003d
|
||||
# endif
|
||||
# ifndef ENXIO
|
||||
# define ENXIO 0x0100003e
|
||||
# endif
|
||||
# ifndef EOPNOTSUPP
|
||||
# define EOPNOTSUPP 0x0100003f
|
||||
# endif
|
||||
# ifndef EOVERFLOW
|
||||
# define EOVERFLOW 0x01000040
|
||||
# endif
|
||||
# ifndef EPERM
|
||||
# define EPERM 0x01000041
|
||||
# endif
|
||||
# ifndef EPIPE
|
||||
# define EPIPE 0x01000042
|
||||
# endif
|
||||
# ifndef EPROTO
|
||||
# define EPROTO 0x01000043
|
||||
# endif
|
||||
# ifndef EPROTONOSUPPORT
|
||||
# define EPROTONOSUPPORT 0x01000044
|
||||
# endif
|
||||
# ifndef EPROTOTYPE
|
||||
# define EPROTOTYPE 0x01000045
|
||||
# endif
|
||||
# ifndef ERANGE
|
||||
# define ERANGE 0x01000046
|
||||
# endif
|
||||
# ifndef EROFS
|
||||
# define EROFS 0x01000047
|
||||
# endif
|
||||
# ifndef ESPIPE
|
||||
# define ESPIPE 0x01000048
|
||||
# endif
|
||||
# ifndef ESRCH
|
||||
# define ESRCH 0x01000049
|
||||
# endif
|
||||
// Reserved in POSIX
|
||||
//# ifndef //ESTALE
|
||||
//# define //ESTALE 0x0100004a
|
||||
//# endif
|
||||
# ifndef ETIME
|
||||
# define ETIME 0x0100004b
|
||||
# endif
|
||||
# ifndef ETIMEDOUT
|
||||
# define ETIMEDOUT 0x0100004c
|
||||
# endif
|
||||
# ifndef ETXTBSY
|
||||
# define ETXTBSY 0x0100004d
|
||||
# endif
|
||||
# ifndef EWOULDBLOCK
|
||||
# ifdef EAGAIN
|
||||
# define EWOULDBLOCK EAGAIN
|
||||
# else
|
||||
# define EWOULDBLOCK 0x0100004e
|
||||
# endif
|
||||
# endif
|
||||
# ifndef EXDEV
|
||||
# define EXDEV 0x0100004f
|
||||
# endif
|
||||
|
||||
// Non-POSIX:
|
||||
# ifndef EHOSTDOWN
|
||||
# define EHOSTDOWN 0x01100001
|
||||
# endif
|
||||
# ifndef EPFNOSUPPORT
|
||||
# define EPFNOSUPPORT 0x01100002
|
||||
# endif
|
||||
# ifndef EPROCLIM
|
||||
# define EPROCLIM 0x01100003
|
||||
# endif
|
||||
# ifndef ESHUTDOWN
|
||||
# define ESHUTDOWN 0x01100004
|
||||
# endif
|
||||
# ifndef ESOCKTNOSUPPORT
|
||||
# define ESOCKTNOSUPPORT 0x01100005
|
||||
# endif
|
||||
#endif /* defined (PORT_WANT_ERRNO) && defined (_MSC_VER) */
|
||||
|
||||
// Use SDL_INCLUDE and SDL_IMAGE_INCLUDE to portably include the SDL files
|
||||
// from the right location.
|
||||
// TODO: Where the SDL and SDL_image headers are located could be detected
|
||||
|
||||
@@ -9,3 +9,7 @@ uqm_CFILES="battle.c border.c build.c cleanup.c clock.c collide.c comm.c
|
||||
settings.c setup.c setupmenu.c ship.c shipstat.c shipyard.c
|
||||
sis.c sounds.c starbase.c starcon.c starmap.c state.c status.c
|
||||
tactrans.c trans.c uqmdebug.c util.c velocity.c weapon.c"
|
||||
if [ -n "$uqm_NETPLAY" ]; then
|
||||
uqm_SUBDIRS="$uqm_SUBDIRS netplay"
|
||||
fi
|
||||
|
||||
|
||||
+209
-12
@@ -21,6 +21,12 @@
|
||||
#include "controls.h"
|
||||
#include "init.h"
|
||||
#include "intel.h"
|
||||
#ifdef NETPLAY
|
||||
# include "netplay/netmelee.h"
|
||||
# ifdef NETPLAY_CHECKSUM
|
||||
# include "netplay/checksum.h"
|
||||
# endif
|
||||
#endif
|
||||
#include "resinst.h"
|
||||
#include "setup.h"
|
||||
#include "settings.h"
|
||||
@@ -32,6 +38,15 @@
|
||||
QUEUE disp_q;
|
||||
SIZE battle_counter;
|
||||
BOOLEAN instantVictory;
|
||||
size_t battleInputOrder[NUM_SIDES];
|
||||
// Indices of the sides in the order their input is processed.
|
||||
// Network sides are last so that the sides will never be waiting
|
||||
// on eachother, and games with a 0 frame delay are theoretically
|
||||
// possible.
|
||||
#ifdef NETPLAY
|
||||
BattleFrameCounter battleFrameCount;
|
||||
// Used for synchronisation purposes during netplay.
|
||||
#endif
|
||||
|
||||
static BOOLEAN
|
||||
RunAwayAllowed (void)
|
||||
@@ -67,7 +82,7 @@ DoRunAway (STARSHIPPTR StarShipPtr)
|
||||
~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED);
|
||||
|
||||
SetPrimColor (&DisplayArray[ElementPtr->PrimIndex],
|
||||
BUILD_COLOR (MAKE_RGB15 (0xB, 0x00, 0x00), 0x2E));
|
||||
BUILD_COLOR (MAKE_RGB15 (0x0B, 0x00, 0x00), 0x2E));
|
||||
SetPrimType (&DisplayArray[ElementPtr->PrimIndex], STAMPFILL_PRIM);
|
||||
|
||||
CyborgDescPtr->ship_input_state = 0;
|
||||
@@ -75,17 +90,54 @@ DoRunAway (STARSHIPPTR StarShipPtr)
|
||||
UnlockElement (StarShipPtr->hShip);
|
||||
}
|
||||
|
||||
static void
|
||||
setupBattleInputOrder(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
#ifndef NETPLAY
|
||||
for (i = 0; i < NUM_SIDES; i++)
|
||||
battleInputOrder[i] = i;
|
||||
#else
|
||||
int j;
|
||||
|
||||
i = 0;
|
||||
// First put the locally controlled players in the array.
|
||||
for (j = 0; j < NUM_SIDES; j++) {
|
||||
if (!(PlayerControl[j] & NETWORK_CONTROL)) {
|
||||
battleInputOrder[i] = j;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// Next put the network controlled players in the array.
|
||||
for (j = 0; j < NUM_SIDES; j++) {
|
||||
if (PlayerControl[j] & NETWORK_CONTROL) {
|
||||
battleInputOrder[i] = j;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
ProcessInput (void)
|
||||
{
|
||||
BOOLEAN CanRunAway;
|
||||
size_t sideI;
|
||||
|
||||
#ifdef NETPLAY
|
||||
netInput ();
|
||||
#endif
|
||||
|
||||
CanRunAway = RunAwayAllowed ();
|
||||
|
||||
for (cur_player = NUM_SIDES - 1; cur_player >= 0; --cur_player)
|
||||
for (sideI = 0; sideI < NUM_SIDES; sideI++)
|
||||
{
|
||||
HSTARSHIP hBattleShip, hNextShip;
|
||||
|
||||
cur_player = battleInputOrder[sideI];
|
||||
|
||||
for (hBattleShip = GetHeadLink (&race_q[cur_player]);
|
||||
hBattleShip != 0; hBattleShip = hNextShip)
|
||||
{
|
||||
@@ -99,10 +151,24 @@ ProcessInput (void)
|
||||
{
|
||||
CyborgDescPtr = StarShipPtr;
|
||||
|
||||
InputState = (*(PlayerInput[cur_player]))();
|
||||
InputState = (*(PlayerInput[cur_player]))(cur_player,
|
||||
StarShipPtr);
|
||||
#if CREATE_JOURNAL
|
||||
JournalInput (InputState);
|
||||
#endif /* CREATE_JOURNAL */
|
||||
#ifdef NETPLAY
|
||||
if (PlayerControl[cur_player] & HUMAN_CONTROL)
|
||||
{
|
||||
BattleInputBuffer *bib = getBattleInputBuffer(cur_player);
|
||||
sendBattleInputConnections (InputState);
|
||||
flushPacketQueues ();
|
||||
|
||||
BattleInputBuffer_push (bib, InputState);
|
||||
// Add this input to the end of the buffer.
|
||||
BattleInputBuffer_pop (bib, &InputState);
|
||||
// Get the input from the front of the buffer.
|
||||
}
|
||||
#endif
|
||||
|
||||
CyborgDescPtr->ship_input_state = 0;
|
||||
if (CyborgDescPtr->RaceDescPtr->ship_info.crew_level)
|
||||
@@ -128,6 +194,10 @@ ProcessInput (void)
|
||||
UnlockStarShip (&race_q[cur_player], hBattleShip);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef NETPLAY
|
||||
flushPacketQueues ();
|
||||
#endif
|
||||
|
||||
if (GLOBAL (CurrentActivity) & (CHECK_LOAD | CHECK_ABORT))
|
||||
GLOBAL (CurrentActivity) &= ~IN_BATTLE;
|
||||
@@ -180,7 +250,39 @@ DoBattle (BATTLE_STATE *bs)
|
||||
bs->MenuRepeatDelay = 0;
|
||||
SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
|
||||
|
||||
#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
|
||||
if (getNumNetConnections() > 0 &&
|
||||
battleFrameCount % NETPLAY_CHECKSUM_INTERVAL == 0)
|
||||
{
|
||||
crc_State state;
|
||||
Checksum checksum;
|
||||
|
||||
crc_init(&state);
|
||||
crc_processDispQueue (&state);
|
||||
checksum = (Checksum) crc_finish (&state);
|
||||
|
||||
sendChecksumConnections ((uint32) battleFrameCount,
|
||||
(uint32) checksum);
|
||||
flushPacketQueues ();
|
||||
addLocalChecksum (battleFrameCount, checksum);
|
||||
}
|
||||
#endif
|
||||
ProcessInput ();
|
||||
// Also calls NetInput()
|
||||
#if defined (NETPLAY) && defined (NETPLAY_CHECKSUM)
|
||||
if (getNumNetConnections() > 0)
|
||||
{
|
||||
size_t delay = getBattleInputDelay();
|
||||
|
||||
if (battleFrameCount >= delay
|
||||
&& (battleFrameCount - delay) % NETPLAY_CHECKSUM_INTERVAL == 0)
|
||||
{
|
||||
if (!verifyChecksums (battleFrameCount - delay))
|
||||
GLOBAL(CurrentActivity) |= CHECK_ABORT;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LockMutex (GraphicsLock);
|
||||
if (bs->first_time)
|
||||
{
|
||||
@@ -220,7 +322,79 @@ DoBattle (BATTLE_STATE *bs)
|
||||
+ BATTLE_FRAME_RATE / (battle_speed + 1));
|
||||
bs->NextTime = GetTimeCounter ();
|
||||
}
|
||||
return (BOOLEAN) ((GLOBAL (CurrentActivity) & IN_BATTLE) != 0);
|
||||
|
||||
if ((GLOBAL (CurrentActivity) & IN_BATTLE) == 0)
|
||||
return FALSE;
|
||||
|
||||
#ifdef NETPLAY
|
||||
battleFrameCount++;
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Let each player pick his ship.
|
||||
static BOOLEAN
|
||||
selectAllShips (SIZE num_ships)
|
||||
{
|
||||
#ifndef NETPLAY
|
||||
while (num_ships--)
|
||||
{
|
||||
if (!GetNextStarShip (NULL_PTR, num_ships == 1))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
#else
|
||||
// On network play, what is the top player to one party may be
|
||||
// the bottom player the other. To keep both sides synchronised
|
||||
// the order is always the same.
|
||||
SIZE order[2];
|
||||
|
||||
if (num_ships == 1) {
|
||||
// HyperSpace in full game.
|
||||
return GetNextStarShip (NULL_PTR, 0);
|
||||
}
|
||||
|
||||
if (num_ships != 2)
|
||||
{
|
||||
fprintf(stderr, "More than two players is not supported.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if ((PlayerControl[0] & NETWORK_CONTROL) &&
|
||||
(PlayerControl[1] & NETWORK_CONTROL))
|
||||
{
|
||||
fprintf(stderr, "Only one side at a time can be network "
|
||||
"controlled.\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Iff 'myTurn' is set on a connection, the local party will be
|
||||
// processed first.
|
||||
// If neither is network controlled, the top player (1) is handled first.
|
||||
if (((PlayerControl[0] & NETWORK_CONTROL) &&
|
||||
!NetConnection_getDiscriminant (netConnections[0])) ||
|
||||
((PlayerControl[1] & NETWORK_CONTROL) &&
|
||||
NetConnection_getDiscriminant (netConnections[1])))
|
||||
{
|
||||
order[0] = 0;
|
||||
order[1] = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
order[0] = 1;
|
||||
order[1] = 0;
|
||||
}
|
||||
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
if (!GetNextStarShip (NULL_PTR, order[i]))
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
@@ -233,7 +407,10 @@ Battle (void)
|
||||
SetResourceIndex (hResIndex);
|
||||
|
||||
#if !(DEMO_MODE || CREATE_JOURNAL)
|
||||
TFB_SeedRandom (GetTimeCounter ());
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) != SUPER_MELEE) {
|
||||
// In Supermelee, the RNG is already initialised.
|
||||
TFB_SeedRandom (GetTimeCounter ());
|
||||
}
|
||||
#else /* DEMO_MODE */
|
||||
if (BattleSeed == 0)
|
||||
BattleSeed = TFB_Random ();
|
||||
@@ -262,21 +439,41 @@ Battle (void)
|
||||
CountLinks (&race_q[1])
|
||||
);
|
||||
|
||||
while (num_ships--)
|
||||
{
|
||||
if (!GetNextStarShip (NULL_PTR, num_ships))
|
||||
goto AbortBattle;
|
||||
}
|
||||
if (!selectAllShips (num_ships))
|
||||
goto AbortBattle;
|
||||
|
||||
BattleSong (TRUE);
|
||||
bs.NextTime = 0;
|
||||
bs.InputFunc = &DoBattle;
|
||||
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE);
|
||||
setupBattleInputOrder ();
|
||||
#ifdef NETPLAY
|
||||
initBattleInputBuffers ();
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
initChecksumBuffers ();
|
||||
#endif
|
||||
battleFrameCount = 0;
|
||||
initBattleStateDataConnections ();
|
||||
{
|
||||
bool allOk = negotiateReadyConnections (true, NetState_inBattle);
|
||||
if (!allOk)
|
||||
goto AbortBattle;
|
||||
}
|
||||
#endif /* NETPLAY */
|
||||
bs.InputFunc = DoBattle;
|
||||
bs.first_time = (BOOLEAN)(LOBYTE (GLOBAL (CurrentActivity)) ==
|
||||
IN_HYPERSPACE);
|
||||
|
||||
UnlockMutex (GraphicsLock);
|
||||
DoInput ((PVOID)&bs, FALSE);
|
||||
LockMutex (GraphicsLock);
|
||||
|
||||
AbortBattle:
|
||||
#ifdef NETPLAY
|
||||
uninitBattleInputBuffers();
|
||||
#ifdef NETPLAY_CHECKSUM
|
||||
uninitChecksumBuffers ();
|
||||
#endif
|
||||
#endif /* NETPLAY */
|
||||
|
||||
StopMusic ();
|
||||
StopSound ();
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
extern QUEUE disp_q;
|
||||
extern SIZE battle_counter;
|
||||
extern BOOLEAN instantVictory;
|
||||
#ifdef NETPLAY
|
||||
typedef DWORD BattleFrameCounter;
|
||||
extern BattleFrameCounter battleFrameCount;
|
||||
#endif
|
||||
|
||||
extern BOOLEAN Battle (void);
|
||||
|
||||
|
||||
@@ -60,8 +60,6 @@
|
||||
|
||||
static int ambient_anim_task (void *data);
|
||||
|
||||
static BOOLEAN getLineWithinWidth(TEXT *pText,
|
||||
const unsigned char **startNext, SIZE maxWidth, COUNT maxChars);
|
||||
|
||||
#define MAX_RESPONSES 8
|
||||
#define BACKGROUND_VOL \
|
||||
@@ -355,8 +353,8 @@ add_text (int status, PTEXT pTextIn)
|
||||
// pText is the text to be fitted. pText->CharCount will be set to the
|
||||
// number of characters that fitted.
|
||||
// startNext will be filled with the start of the first word that
|
||||
// doesn't fit in one line, or if an entire line fitted, to the character
|
||||
// past the newline, or if the entire string fitted, to the end of the
|
||||
// doesn't fit in one line, or if an entire line fits, to the character
|
||||
// past the newline, or if the entire string fits, to the end of the
|
||||
// string.
|
||||
// maxWidth is the maximum number of pixels that a line may be wide
|
||||
// maxChars is the maximum number of characters (not bytes) that are to
|
||||
|
||||
@@ -24,6 +24,8 @@ extern void init_communication (void);
|
||||
extern void uninit_communication (void);
|
||||
extern int do_subtitles (UNICODE *pStr);
|
||||
extern void AlienTalkSegue (COUNT wait_track);
|
||||
BOOLEAN getLineWithinWidth(TEXT *pText, const unsigned char **startNext,
|
||||
SIZE maxWidth, COUNT maxChars);
|
||||
|
||||
extern RECT CommWndRect; /* comm window rect */
|
||||
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#ifndef _CONTROLS_H_
|
||||
#define _CONTROLS_H_
|
||||
|
||||
#include "races.h"
|
||||
// For STARSHIPPTR
|
||||
|
||||
#include "libs/compiler.h"
|
||||
#include "libs/strlib.h"
|
||||
|
||||
@@ -85,8 +88,9 @@ typedef UBYTE BATTLE_INPUT_STATE;
|
||||
#define BATTLE_ESCAPE ((BATTLE_INPUT_STATE)(1 << 5))
|
||||
#define BATTLE_DOWN ((BATTLE_INPUT_STATE)(1 << 6))
|
||||
|
||||
typedef BATTLE_INPUT_STATE (*battle_summary_func) (void);
|
||||
extern battle_summary_func ComputerInput, HumanInput[];
|
||||
typedef BATTLE_INPUT_STATE (*battle_summary_func) (COUNT player,
|
||||
STARSHIPPTR StarShipPtr);
|
||||
extern battle_summary_func ComputerInput, HumanInput[], NetworkInput;
|
||||
extern battle_summary_func PlayerInput[];
|
||||
|
||||
extern CONTROLLER_INPUT_STATE CurrentInputState, PulsedInputState;
|
||||
@@ -107,8 +111,8 @@ extern void WaitForNoInput (SIZE Duration);
|
||||
extern BOOLEAN ConfirmExit (void);
|
||||
extern void DoInput (PVOID pInputState, BOOLEAN resetInput);
|
||||
|
||||
BATTLE_INPUT_STATE p1_combat_summary (void);
|
||||
BATTLE_INPUT_STATE p2_combat_summary (void);
|
||||
BATTLE_INPUT_STATE p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
|
||||
BATTLE_INPUT_STATE p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr);
|
||||
|
||||
extern volatile BOOLEAN GamePaused, ExitRequested;
|
||||
|
||||
|
||||
@@ -1010,7 +1010,7 @@ Avoid (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR EvalDescPtr)
|
||||
}
|
||||
|
||||
BATTLE_INPUT_STATE
|
||||
tactical_intelligence (void)
|
||||
tactical_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
|
||||
{
|
||||
ELEMENTPTR ShipPtr;
|
||||
ELEMENT Ship;
|
||||
@@ -1019,10 +1019,9 @@ tactical_intelligence (void)
|
||||
COUNT ConcernCounter;
|
||||
EVALUATE_DESC ObjectsOfConcern[10];
|
||||
BOOLEAN ShipMoved, UltraManeuverable;
|
||||
STARSHIPPTR StarShipPtr, EnemyStarShipPtr;
|
||||
STARSHIPPTR EnemyStarShipPtr;
|
||||
RACE_DESCPTR RDPtr, EnemyRDPtr;
|
||||
|
||||
StarShipPtr = CyborgDescPtr;
|
||||
RDPtr = StarShipPtr->RaceDescPtr;
|
||||
|
||||
if (RDPtr->cyborg_control.ManeuverabilityIndex == 0)
|
||||
@@ -1037,7 +1036,7 @@ tactical_intelligence (void)
|
||||
}
|
||||
|
||||
ShipMoved = TRUE;
|
||||
if (PlayerControl[cur_player] & STANDARD_RATING)
|
||||
if (PlayerControl[player] & STANDARD_RATING)
|
||||
++StarShipPtr->special_counter;
|
||||
|
||||
#ifdef DEBUG_CYBORG
|
||||
@@ -1260,7 +1259,7 @@ if (!(ShipPtr->state_flags & FINITE_LIFE)
|
||||
ed.which_turn = 0;
|
||||
}
|
||||
}
|
||||
else if (!(PlayerControl[cur_player] & AWESOME_RATING))
|
||||
else if (!(PlayerControl[player] & AWESOME_RATING))
|
||||
ed.which_turn = 0;
|
||||
else
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
|
||||
typedef QUEUE_HANDLE HELEMENT;
|
||||
|
||||
// Bits for ELEMENT_FLAGS:
|
||||
#define GOOD_GUY (1 << 0)
|
||||
#define BAD_GUY (1 << 1)
|
||||
#define PLAYER_SHIP (1 << 2)
|
||||
@@ -65,6 +66,7 @@ typedef QUEUE_HANDLE HELEMENT;
|
||||
#define CREW_OBJECT (1 << 14)
|
||||
#define BACKGROUND_OBJECT (1 << 15)
|
||||
|
||||
|
||||
#define HYPERJUMP_LIFE 15
|
||||
|
||||
#define NUM_EXPLOSION_FRAMES 12
|
||||
|
||||
@@ -260,7 +260,7 @@ InitGalaxy (void)
|
||||
{
|
||||
SetPrimType (&DisplayArray[p], STAMP_PRIM);
|
||||
SetPrimColor (&DisplayArray[p],
|
||||
BUILD_COLOR (MAKE_RGB15 (0xB, 0xB, 0x1F), 0x09));
|
||||
BUILD_COLOR (MAKE_RGB15 (0x0B, 0x0B, 0x1F), 0x09));
|
||||
DisplayArray[p].Object.Stamp.frame = stars_in_space;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "libs/threadlib.h"
|
||||
|
||||
|
||||
battle_summary_func ComputerInput, HumanInput[NUM_PLAYERS];
|
||||
battle_summary_func ComputerInput, HumanInput[NUM_PLAYERS], NetworkInput;
|
||||
battle_summary_func PlayerInput[NUM_PLAYERS];
|
||||
|
||||
#define ACCELERATION_INCREMENT (ONE_SECOND / 12)
|
||||
@@ -379,7 +379,7 @@ GetMenuSounds (MENU_SOUND_FLAGS *s0, MENU_SOUND_FLAGS *s1)
|
||||
/* These can really be refactored */
|
||||
|
||||
BATTLE_INPUT_STATE
|
||||
p1_combat_summary (void)
|
||||
p1_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
|
||||
{
|
||||
BATTLE_INPUT_STATE InputState = 0;
|
||||
if (CurrentInputState.key[PlayerOne][KEY_UP])
|
||||
@@ -396,11 +396,14 @@ p1_combat_summary (void)
|
||||
InputState |= BATTLE_ESCAPE;
|
||||
if (CurrentInputState.key[PlayerOne][KEY_DOWN])
|
||||
InputState |= BATTLE_DOWN;
|
||||
|
||||
(void) player;
|
||||
(void) StarShipPtr;
|
||||
return InputState;
|
||||
}
|
||||
|
||||
BATTLE_INPUT_STATE
|
||||
p2_combat_summary (void)
|
||||
p2_combat_summary (COUNT player, STARSHIPPTR StarShipPtr)
|
||||
{
|
||||
BATTLE_INPUT_STATE InputState = 0;
|
||||
if (CurrentInputState.key[PlayerTwo][KEY_UP])
|
||||
@@ -415,6 +418,9 @@ p2_combat_summary (void)
|
||||
InputState |= BATTLE_SPECIAL;
|
||||
if (CurrentInputState.key[PlayerTwo][KEY_DOWN])
|
||||
InputState |= BATTLE_DOWN;
|
||||
|
||||
(void) player;
|
||||
(void) StarShipPtr;
|
||||
return InputState;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#define FLAGSHIP_STRING_COUNT 13
|
||||
#define ORBITSCAN_STRING_COUNT 19
|
||||
#define MAINMENU_STRING_COUNT 56
|
||||
#define NETMELEE_STRING_COUNT 16
|
||||
|
||||
enum {
|
||||
STAR_STRING_BASE = 0,
|
||||
@@ -69,8 +70,9 @@ enum {
|
||||
FLAGSHIP_STRING_BASE = STATUS_STRING_BASE + STATUS_STRING_COUNT,
|
||||
ORBITSCAN_STRING_BASE = FLAGSHIP_STRING_BASE + FLAGSHIP_STRING_COUNT,
|
||||
MAINMENU_STRING_BASE = ORBITSCAN_STRING_BASE + ORBITSCAN_STRING_COUNT,
|
||||
NETMELEE_STRING_BASE = MAINMENU_STRING_BASE + MAINMENU_STRING_COUNT,
|
||||
|
||||
GAMESTR_COUNT = MAINMENU_STRING_BASE + MAINMENU_STRING_COUNT
|
||||
GAMESTR_COUNT = NETMELEE_STRING_BASE + NETMELEE_STRING_COUNT
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -29,27 +29,28 @@
|
||||
SIZE cur_player;
|
||||
|
||||
BATTLE_INPUT_STATE
|
||||
computer_intelligence (void)
|
||||
computer_intelligence (COUNT player, STARSHIPPTR StarShipPtr)
|
||||
{
|
||||
BATTLE_INPUT_STATE InputState;
|
||||
|
||||
if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE)
|
||||
return 0;
|
||||
|
||||
if (CyborgDescPtr)
|
||||
if (StarShipPtr)
|
||||
{
|
||||
if (PlayerControl[cur_player] & CYBORG_CONTROL)
|
||||
if (PlayerControl[player] & CYBORG_CONTROL)
|
||||
{
|
||||
InputState = tactical_intelligence ();
|
||||
InputState = tactical_intelligence (player, StarShipPtr);
|
||||
|
||||
// allow a player to warp-escape in cyborg mode
|
||||
if (cur_player == 0)
|
||||
InputState |= (*(HumanInput[cur_player])) () & BATTLE_ESCAPE;
|
||||
if (player == 0)
|
||||
InputState |= (*(HumanInput[player])) (player, StarShipPtr)
|
||||
& BATTLE_ESCAPE;
|
||||
}
|
||||
else
|
||||
InputState = (*(HumanInput[cur_player])) ();
|
||||
InputState = (*(HumanInput[player])) (player, StarShipPtr);
|
||||
}
|
||||
else if (!(PlayerControl[cur_player] & PSYTRON_CONTROL))
|
||||
else if (!(PlayerControl[player] & PSYTRON_CONTROL))
|
||||
InputState = 0;
|
||||
else
|
||||
{
|
||||
|
||||
@@ -63,8 +63,10 @@ enum
|
||||
extern STARSHIPPTR CyborgDescPtr;
|
||||
extern SIZE cur_player;
|
||||
|
||||
extern BATTLE_INPUT_STATE computer_intelligence (void);
|
||||
extern BATTLE_INPUT_STATE tactical_intelligence (void);
|
||||
extern BATTLE_INPUT_STATE computer_intelligence (COUNT player,
|
||||
STARSHIPPTR StarShipPtr);
|
||||
extern BATTLE_INPUT_STATE tactical_intelligence (COUNT player,
|
||||
STARSHIPPTR StarShipPtr);
|
||||
extern void ship_intelligence (ELEMENTPTR ShipPtr, EVALUATE_DESCPTR
|
||||
ObjectsOfConcern, COUNT ConcernCounter);
|
||||
extern BOOLEAN ship_weapons (ELEMENTPTR ShipPtr, ELEMENTPTR OtherPtr,
|
||||
@@ -80,11 +82,12 @@ extern BOOLEAN ThrustShip (ELEMENTPTR ShipPtr, COUNT angle);
|
||||
#define HUMAN_CONTROL (BYTE)(1 << 0)
|
||||
#define CYBORG_CONTROL (BYTE)(1 << 1)
|
||||
#define PSYTRON_CONTROL (BYTE)(1 << 2)
|
||||
#define NETWORK_CONTROL (BYTE)(1 << 3)
|
||||
#define COMPUTER_CONTROL (CYBORG_CONTROL | PSYTRON_CONTROL)
|
||||
|
||||
#define STANDARD_RATING (BYTE)(1 << 3)
|
||||
#define GOOD_RATING (BYTE)(1 << 4)
|
||||
#define AWESOME_RATING (BYTE)(1 << 5)
|
||||
#define STANDARD_RATING (BYTE)(1 << 4)
|
||||
#define GOOD_RATING (BYTE)(1 << 5)
|
||||
#define AWESOME_RATING (BYTE)(1 << 6)
|
||||
|
||||
|
||||
#endif /* _INTEL_H */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
uqm_SUBDIRS="callback decomp file graphics heap input list math memory
|
||||
resource sound strings task threads time uio video log"
|
||||
network resource sound strings task threads time uio video log"
|
||||
if [ -n "$uqm_USE_INTERNAL_MIKMOD" ]; then
|
||||
uqm_SUBDIRS="$uqm_SUBDIRS mikmod"
|
||||
fi
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
+982
-190
File diff suppressed because it is too large
Load Diff
+18
-5
@@ -25,6 +25,7 @@
|
||||
#include "libs/sndlib.h"
|
||||
#include "libs/reslib.h"
|
||||
|
||||
typedef struct melee_state MELEE_STATE;
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -68,7 +69,7 @@ enum
|
||||
|
||||
extern FRAME PickMeleeFrame;
|
||||
|
||||
#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x1)
|
||||
#define PICK_BG_COLOR BUILD_COLOR (MAKE_RGB15 (0x00, 0x01, 0x0F), 0x01)
|
||||
|
||||
#define MAX_TEAM_CHARS 30
|
||||
#define MAX_VIS_TEAMS 5
|
||||
@@ -82,11 +83,12 @@ typedef COUNT FleetShipIndex;
|
||||
typedef struct
|
||||
{
|
||||
BYTE ShipList[MELEE_FLEET_SIZE];
|
||||
UNICODE TeamName[MAX_TEAM_CHARS + 25]; /* in case default names in starcon.txt
|
||||
are unknowingly mangled */
|
||||
UNICODE TeamName[MAX_TEAM_CHARS + 1 + 24];
|
||||
/* The +1 is for the terminating \0; the +24 is in case some
|
||||
* default name in starcon.txt is unknowingly mangled. */
|
||||
} TEAM_IMAGE;
|
||||
|
||||
typedef struct melee_state
|
||||
struct melee_state
|
||||
{
|
||||
BOOLEAN (*InputFunc) (struct melee_state *pInputState);
|
||||
COUNT MenuRepeatDelay;
|
||||
@@ -104,13 +106,24 @@ typedef struct melee_state
|
||||
TEAM_IMAGE PreBuiltList[NUM_PREBUILT];
|
||||
|
||||
MUSIC_REF hMusic;
|
||||
} MELEE_STATE;
|
||||
};
|
||||
typedef MELEE_STATE *PMELEE_STATE;
|
||||
|
||||
extern PMELEE_STATE volatile pMeleeState;
|
||||
|
||||
extern void Melee (void);
|
||||
|
||||
void teamStringChanged (PMELEE_STATE pMS, int player);
|
||||
void entireFleetChanged (PMELEE_STATE pMS, int player);
|
||||
void fleetShipChanged (PMELEE_STATE pMS, int player, size_t index);
|
||||
|
||||
void updateTeamName (PMELEE_STATE pMS, COUNT side, const char *name,
|
||||
size_t len);
|
||||
bool updateFleetShip (PMELEE_STATE pMS, COUNT side, COUNT index, BYTE ship);
|
||||
void updateRandomSeed (PMELEE_STATE pMS, COUNT side, DWORD seed);
|
||||
void connectedFeedback (PMELEE_STATE pMS, COUNT side);
|
||||
void errorFeedback (PMELEE_STATE pMS, COUNT side);
|
||||
void closeFeedback (PMELEE_STATE pMS, COUNT side);
|
||||
|
||||
#endif /* _MELEE_H */
|
||||
|
||||
|
||||
@@ -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