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