1 /**
2 * @file nthread.cpp
3 *
4 * Implementation of functions for managing game ticks.
5 */
6 #include "all.h"
7 #include "../3rdParty/Storm/Source/storm.h"
8
9 DEVILUTION_BEGIN_NAMESPACE
10
11 BYTE sgbNetUpdateRate;
12 DWORD gdwMsgLenTbl[MAX_PLRS];
13 static CCritSect sgMemCrit;
14 DWORD gdwDeltaBytesSec;
15 BOOLEAN nthread_should_run;
16 DWORD gdwTurnsInTransit;
17 uintptr_t glpMsgTbl[MAX_PLRS];
18 SDL_threadID glpNThreadId;
19 char sgbSyncCountdown;
20 int turn_upper_bit;
21 BOOLEAN sgbTicsOutOfSync;
22 char sgbPacketCountdown;
23 BOOLEAN sgbThreadIsRunning;
24 DWORD gdwLargestMsgSize;
25 DWORD gdwNormalMsgSize;
26 int last_tick;
27
28 /* data */
29 static SDL_Thread *sghThread = NULL;
30
nthread_terminate_game(const char * pszFcn)31 void nthread_terminate_game(const char *pszFcn)
32 {
33 DWORD sErr;
34
35 sErr = SErrGetLastError();
36 if (sErr == STORM_ERROR_INVALID_PLAYER) {
37 return;
38 } else if (sErr == STORM_ERROR_GAME_TERMINATED) {
39 gbGameDestroyed = TRUE;
40 } else if (sErr == STORM_ERROR_NOT_IN_GAME) {
41 gbGameDestroyed = TRUE;
42 } else {
43 app_fatal("%s:\n%s", pszFcn, TraceLastError());
44 }
45 }
46
nthread_send_and_recv_turn(DWORD cur_turn,int turn_delta)47 DWORD nthread_send_and_recv_turn(DWORD cur_turn, int turn_delta)
48 {
49 DWORD new_cur_turn;
50 int turn, turn_tmp;
51 DWORD curTurnsInTransit;
52
53 new_cur_turn = cur_turn;
54 if (!SNetGetTurnsInTransit(&curTurnsInTransit)) {
55 nthread_terminate_game("SNetGetTurnsInTransit");
56 return 0;
57 }
58 while (curTurnsInTransit++ < gdwTurnsInTransit) {
59
60 turn_tmp = turn_upper_bit | (new_cur_turn & 0x7FFFFFFF);
61 turn_upper_bit = 0;
62 turn = turn_tmp;
63
64 if (!SNetSendTurn((char *)&turn, sizeof(turn))) {
65 nthread_terminate_game("SNetSendTurn");
66 return 0;
67 }
68
69 new_cur_turn += turn_delta;
70 if (new_cur_turn >= 0x7FFFFFFF)
71 new_cur_turn &= 0xFFFF;
72 }
73 return new_cur_turn;
74 }
75
nthread_recv_turns(BOOL * pfSendAsync)76 BOOL nthread_recv_turns(BOOL *pfSendAsync)
77 {
78 *pfSendAsync = FALSE;
79 sgbPacketCountdown--;
80 if (sgbPacketCountdown) {
81 last_tick += gnTickDelay;
82 return TRUE;
83 }
84 sgbSyncCountdown--;
85 sgbPacketCountdown = sgbNetUpdateRate;
86 if (sgbSyncCountdown != 0) {
87
88 *pfSendAsync = TRUE;
89 last_tick += gnTickDelay;
90 return TRUE;
91 }
92 #ifdef __3DS__
93 return FALSE;
94 #else
95 if (!SNetReceiveTurns(0, MAX_PLRS, (char **)glpMsgTbl, gdwMsgLenTbl, (LPDWORD)player_state)) {
96 if (SErrGetLastError() != STORM_ERROR_NO_MESSAGES_WAITING)
97 nthread_terminate_game("SNetReceiveTurns");
98 sgbTicsOutOfSync = FALSE;
99 sgbSyncCountdown = 1;
100 sgbPacketCountdown = 1;
101 return FALSE;
102 } else {
103 if (!sgbTicsOutOfSync) {
104 sgbTicsOutOfSync = TRUE;
105 last_tick = SDL_GetTicks();
106 }
107 sgbSyncCountdown = 4;
108 multi_msg_countdown();
109 *pfSendAsync = TRUE;
110 last_tick += gnTickDelay;
111 return TRUE;
112 }
113 #endif
114 }
115
nthread_handler(void * data)116 static unsigned int nthread_handler(void *data)
117 {
118 int delta;
119 BOOL received;
120
121 if (nthread_should_run) {
122 while (1) {
123 sgMemCrit.Enter();
124 if (!nthread_should_run)
125 break;
126 nthread_send_and_recv_turn(0, 0);
127 if (nthread_recv_turns(&received))
128 delta = last_tick - SDL_GetTicks();
129 else
130 delta = gnTickDelay;
131 sgMemCrit.Leave();
132 if (delta > 0)
133 SDL_Delay(delta);
134 if (!nthread_should_run)
135 return 0;
136 }
137 sgMemCrit.Leave();
138 }
139 return 0;
140 }
141
nthread_set_turn_upper_bit()142 void nthread_set_turn_upper_bit()
143 {
144 turn_upper_bit = 0x80000000;
145 }
146
nthread_start(BOOL set_turn_upper_bit)147 void nthread_start(BOOL set_turn_upper_bit)
148 {
149 const char *err, *err2;
150 DWORD largestMsgSize;
151 _SNETCAPS caps;
152
153 last_tick = SDL_GetTicks();
154 sgbPacketCountdown = 1;
155 sgbSyncCountdown = 1;
156 sgbTicsOutOfSync = TRUE;
157 if (set_turn_upper_bit)
158 nthread_set_turn_upper_bit();
159 else
160 turn_upper_bit = 0;
161 caps.size = 36;
162 if (!SNetGetProviderCaps(&caps)) {
163 err = TraceLastError();
164 app_fatal("SNetGetProviderCaps:\n%s", err);
165 }
166 gdwTurnsInTransit = caps.defaultturnsintransit;
167 if (!caps.defaultturnsintransit)
168 gdwTurnsInTransit = 1;
169 if (caps.defaultturnssec <= 20 && caps.defaultturnssec)
170 sgbNetUpdateRate = 20 / caps.defaultturnssec;
171 else
172 sgbNetUpdateRate = 1;
173 largestMsgSize = 512;
174 if (caps.maxmessagesize < 0x200)
175 largestMsgSize = caps.maxmessagesize;
176 gdwDeltaBytesSec = caps.bytessec >> 2;
177 gdwLargestMsgSize = largestMsgSize;
178 gdwNormalMsgSize = caps.bytessec * sgbNetUpdateRate / 20;
179 gdwNormalMsgSize *= 3;
180 gdwNormalMsgSize >>= 2;
181 if (caps.maxplayers > MAX_PLRS)
182 caps.maxplayers = MAX_PLRS;
183 gdwNormalMsgSize /= caps.maxplayers;
184 while (gdwNormalMsgSize < 0x80) {
185 gdwNormalMsgSize *= 2;
186 sgbNetUpdateRate *= 2;
187 }
188 if (gdwNormalMsgSize > largestMsgSize)
189 gdwNormalMsgSize = largestMsgSize;
190 if (gbIsMultiplayer) {
191 sgbThreadIsRunning = FALSE;
192 sgMemCrit.Enter();
193 nthread_should_run = TRUE;
194 sghThread = CreateThread(nthread_handler, &glpNThreadId);
195 if (sghThread == NULL) {
196 err2 = TraceLastError();
197 app_fatal("nthread2:\n%s", err2);
198 }
199 }
200 }
201
nthread_cleanup()202 void nthread_cleanup()
203 {
204 nthread_should_run = FALSE;
205 gdwTurnsInTransit = 0;
206 gdwNormalMsgSize = 0;
207 gdwLargestMsgSize = 0;
208 if (sghThread != NULL && glpNThreadId != SDL_GetThreadID(NULL)) {
209 if (!sgbThreadIsRunning)
210 sgMemCrit.Leave();
211 SDL_WaitThread(sghThread, NULL);
212 sghThread = NULL;
213 }
214 }
215
nthread_ignore_mutex(BOOL bStart)216 void nthread_ignore_mutex(BOOL bStart)
217 {
218 if (sghThread != NULL) {
219 if (bStart)
220 sgMemCrit.Leave();
221 else
222 sgMemCrit.Enter();
223 sgbThreadIsRunning = bStart;
224 }
225 }
226
227 /**
228 * @brief Checks if it's time for the logic to advance
229 * @return True if the engine should tick
230 */
nthread_has_500ms_passed()231 BOOL nthread_has_500ms_passed()
232 {
233 DWORD currentTickCount;
234 int ticksElapsed;
235
236 currentTickCount = SDL_GetTicks();
237 ticksElapsed = currentTickCount - last_tick;
238 if (!gbIsMultiplayer && ticksElapsed > gnTickDelay * 10) {
239 last_tick = currentTickCount;
240 ticksElapsed = 0;
241 }
242 return ticksElapsed >= 0;
243 }
244
245 DEVILUTION_END_NAMESPACE
246