1 /*
2 * Copyright 2006 Serge van den Boom <svdb@stack.nl>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 // See doc/devel/netplay/protocol
20
21 #define NETCONNECTION_INTERNAL
22 #include "../netplay.h"
23 #include "reset.h"
24
25 #include "types.h"
26 #include "../packetsenders.h"
27 #include "../../melee.h"
28 // For resetFeedback.
29
30 #include <assert.h>
31
32 // Reset packets are sent to indicate that a game is to be reset.
33 // i.e. the game is to return to the SuperMelee fleet setup menu.
34 // The reset will occur when a reset packet has both been sent and
35 // received. When a reset packet is received and the local side had not
36 // sent a reset packet itself, the local side will confirm the reset.
37 // When both sides initiate a reset simultaneously, the reset packets
38 // of each side will act as a confirmation for the other side.
39 //
40 // When a reset packet has been sent, no further gameplay packets should be
41 // sent until the game has been reset. Non-gameplay packets such as 'ping'
42 // are allowed.
43 // When a reset packet has been received, all further incoming gameplay
44 // packets are ignored until the game has been reset.
45 //
46 // conn->stateFlags.reset.localReset is set when a reset packet is sent.
47 // conn->stateFlags.reset.remoteReset is set when a reset packet is
48 // received.
49 //
50 // When either localReset or remoteReset gets set and the other flag isn't
51 // set, Netplay_connectionReset() gets called.
52 //
53 // As soon as the following three conditions are met, the reset callback is
54 // called and the localReset and remoteReset flags are cleared.
55 // - conn->stateFlags.reset.localReset is set
56 // - conn->stateFlags.reset.remoteReset is set
57 // - the reset callback is non-NULL.
58 //
59 // Elsewhere in the UQM source:
60 // When the local side causes a reset, it calls Netplay_localReset().
61 // When a remote reset packet is received, Netplay_remoteReset() is called
62 // (which will sent a reset packet back as confirmation, as required).
63 // At the end of melee, the reset callback is set (for each connection),
64 // and the game will wait until the reset callback for each connection has
65 // been called (when the forementioned conditions have become true)
66 // (or until the connection is terminated).
67
68
69 // This function is called when one side initiates a reset.
70 static void
Netplay_connectionReset(NetConnection * conn,NetplayResetReason reason,bool byRemote)71 Netplay_connectionReset(NetConnection *conn, NetplayResetReason reason,
72 bool byRemote) {
73 switch (NetConnection_getState(conn)) {
74 case NetState_unconnected:
75 case NetState_connecting:
76 case NetState_init:
77 case NetState_inSetup:
78 break;
79 case NetState_preBattle:
80 case NetState_interBattle:
81 case NetState_selectShip:
82 case NetState_inBattle:
83 case NetState_endingBattle:
84 case NetState_endingBattle2:
85 resetFeedback(conn, reason, byRemote);
86 break;
87 }
88 }
89
90 static void
Netplay_doConnectionResetCallback(NetConnection * conn)91 Netplay_doConnectionResetCallback(NetConnection *conn) {
92 NetConnection_ResetCallback callback;
93 void *resetArg;
94
95 callback = conn->resetCallback;
96 resetArg = conn->resetCallbackArg;
97
98 NetConnection_setResetCallback(conn, NULL, NULL);
99 // Clear the resetCallback field before performing the callback,
100 // so that it can be set again from inside the callback
101 // function.
102 callback(conn, resetArg);
103 }
104
105 static void
Netplay_resetConditionTriggered(NetConnection * conn)106 Netplay_resetConditionTriggered(NetConnection *conn) {
107 if (conn->resetCallback == NULL)
108 return;
109
110 if (!conn->stateFlags.reset.localReset ||
111 !conn->stateFlags.reset.remoteReset)
112 return;
113
114 conn->stateFlags.reset.localReset = false;
115 conn->stateFlags.reset.remoteReset = false;
116
117 Netplay_doConnectionResetCallback(conn);
118 }
119
120 void
Netplay_setResetCallback(NetConnection * conn,NetConnection_ResetCallback callback,void * resetArg)121 Netplay_setResetCallback(NetConnection *conn,
122 NetConnection_ResetCallback callback, void *resetArg) {
123 NetConnection_setResetCallback(conn, callback, resetArg);
124
125 Netplay_resetConditionTriggered(conn);
126 }
127
128 void
Netplay_localReset(NetConnection * conn,NetplayResetReason reason)129 Netplay_localReset(NetConnection *conn, NetplayResetReason reason) {
130 assert(!conn->stateFlags.reset.localReset);
131
132 conn->stateFlags.reset.localReset = true;
133 if (conn->stateFlags.reset.remoteReset) {
134 // Both sides have initiated/confirmed the reset.
135 Netplay_resetConditionTriggered(conn);
136 } else {
137 sendReset(conn, reason);
138 Netplay_connectionReset(conn, reason, false);
139 }
140 }
141
142 void
Netplay_remoteReset(NetConnection * conn,NetplayResetReason reason)143 Netplay_remoteReset(NetConnection *conn, NetplayResetReason reason) {
144 assert(!conn->stateFlags.reset.remoteReset);
145 // Should already be checked when the packet arrives.
146
147 conn->stateFlags.reset.remoteReset = true;
148 if (!conn->stateFlags.reset.localReset) {
149 sendReset(conn, reason);
150 conn->stateFlags.reset.localReset = true;
151 Netplay_connectionReset(conn, reason, true);
152 }
153
154 Netplay_resetConditionTriggered(conn);
155 }
156
157 bool
Netplay_isLocalReset(const NetConnection * conn)158 Netplay_isLocalReset(const NetConnection *conn) {
159 return conn->stateFlags.reset.localReset;
160 }
161
162 bool
Netplay_isRemoteReset(const NetConnection * conn)163 Netplay_isRemoteReset(const NetConnection *conn) {
164 return conn->stateFlags.reset.remoteReset;
165 }
166
167