Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
matcher.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#include "global.h"
20#include "matcher.h"
21#include "encrypt.h"
22#include "timezone.h"
23#include "debug.h"
24
25#ifdef _WINDOWS
26#define usleep(x) Sleep((x)/100000)
27#endif
28
30{
32 m_baseNick = "matcher";
33 m_joinSuccess = false;
34 done = 0;
35
36 m_rotateLogs = false;
37 quiet = false;
38}
39
41{
42 Wstring res;
43 Global.GetString(key, res);
44 return res;
45}
46
47void logIt(const char *Txt)
48{
49 // intentionally crash if we can't open it
50 FILE *fp = fopen("logIt.txt", "w");
51 fputs(Txt, fp);
52 fclose(fp);
53}
54
56{
57 int delay = -1;
58 Global.config.getInt("ROTATEDELAY", delay);
59 DBGMSG("ROTATEDELAY: " << delay);
60
61 do
62 {
63 static time_t lastLogTime = 0;
64 time_t now = time(NULL);
65 if (now > lastLogTime + 300)
66 {
67 lastLogTime = now;
68 INFMSG("still here" << endl);
69 }
70
71 logIt("peerThink\n");
72 peerThink(m_peer);
73
74 logIt("peerIsConnected\n");
75 if (peerIsConnected(m_peer))
76 {
77 logIt("checkMatches()\n");
79 logIt("checkMatches() done\n");
80 }
81 else
82 done = true;
83
84 msleep(1);
85
86 // rotate logs if it's time
87 if (delay != -1)
88 {
89#ifdef _UNIX
90 Xtime xtime;
91 time_t curtime;
92 curtime = time(NULL);
93 // get the number of seconds that have passed since midnight
94 // of the current day.
95 curtime -= TimezoneOffset();
96 time_t timeofday = curtime % (delay);
97 if ((timeofday > 0) && (timeofday <= 300))
98 {
101 }
102#endif
103 }
104 }
105 while (!done);
106 DBGMSG("Bailing out of readLoop!" << endl);
107 INFMSG("Bailing out of readLoop!" << endl);
108 ERRMSG("Bailing out of readLoop!" << endl);
109}
110
112
113static void DisconnectedCallback ( PEER peer, const char * reason, void * param)
114{
115 DBGMSG("Disconnected: " << reason);
116 MatcherClass *matcher = (MatcherClass *)param;
117 if (matcher)
118 matcher->handleDisconnect( reason );
119}
120
121static void RoomMessageCallback ( PEER peer, RoomType roomType, const char * nick, const char * message, MessageType messageType, void * param)
122{
123 DBGMSG("(PUBLIC) " << nick << ": " << message);
124 MatcherClass *matcher = (MatcherClass *)param;
125 if (matcher)
126 matcher->handleRoomMessage( nick, message, messageType );
127}
128
129static void PlayerMessageCallback ( PEER peer, const char * nick, const char * message, MessageType messageType, void * param)
130{
131 DBGMSG("(PRIVATE) " << nick << ": " << message);
132 MatcherClass *matcher = (MatcherClass *)param;
133 if (matcher)
134 matcher->handlePlayerMessage( nick, message, messageType );
135}
136
137static void PlayerJoinedCallback ( PEER peer, RoomType roomType, const char * nick, void * param)
138{
139 DBGMSG(nick << " joined the room");
140 MatcherClass *matcher = (MatcherClass *)param;
141 if (matcher)
142 matcher->handlePlayerJoined( nick );
143}
144
145static void PlayerLeftCallback ( PEER peer, RoomType roomType, const char * nick, const char * reason, void * param)
146{
147 DBGMSG(nick << " left the room");
148 MatcherClass *matcher = (MatcherClass *)param;
149 if (matcher)
150 matcher->handlePlayerLeft( nick );
151}
152
153static void PlayerChangedNickCallback ( PEER peer, RoomType roomType, const char * oldNick, const char * newNick, void * param)
154{
155 INFMSG(oldNick << " changed nicks to " << newNick);
156 MatcherClass *matcher = (MatcherClass *)param;
157 if (matcher)
158 matcher->handlePlayerChangedNick( oldNick, newNick );
159}
160
161static void EnumPlayersCallback ( PEER peer, PEERBool success, RoomType roomType, int index, const char * nick, int flags, void * param)
162{
163 MatcherClass *matcher = (MatcherClass *)param;
164 if (matcher)
165 matcher->handlePlayerEnum( success == PEERTrue, index, nick, flags);
166}
167
168static int s_groupID = 0;
169static void ListGroupRoomsCallback ( PEER peer, PEERBool success, int groupID, SBServer server, const char * name, int numWaiting, int maxWaiting, int numGames, int numPlaying, void * param)
170{
171 if (success && name && !strcasecmp(name, "QuickMatch"))
172 {
173 s_groupID = groupID;
174 }
175}
176
177static void ConnectCallback ( PEER peer, PEERBool success, void * param)
178{
179 MatcherClass *matcher = (MatcherClass *)param;
180 if (matcher)
181 matcher->handleConnect( success == PEERTrue );
182}
183
184static void JoinCallback ( PEER peer, PEERBool success, PEERJoinResult result, RoomType roomType, void * param)
185{
186 MatcherClass *matcher = (MatcherClass *)param;
187 if (matcher)
188 matcher->handleJoin( success == PEERTrue );
189}
190
191static void NickErrorCallback ( PEER peer, int type, const char * badNick, void * param)
192{
193 ERRMSG("Nick error with " << badNick);
194
195 if(type == PEER_IN_USE)
196 {
197 int len = strlen(badNick);
198 std::string nickStr = badNick;
199 int newVal = 0;
200 if (badNick[len-1] == '}' && badNick[len-3] == '{' && isdigit(badNick[len-2]))
201 {
202 newVal = badNick[len-2] - '0' + 1;
203 nickStr.erase(len-3, 3);
204 }
205
206 nickStr.append("{");
207 char tmp[2];
208 tmp[0] = '0'+newVal;
209 tmp[1] = '\0';
210 nickStr.append(tmp);
211 nickStr.append("}");
212 DBGMSG("Nickname taken: was "<<badNick<<", new val = "<<newVal<<", new nick = "<<nickStr.c_str());
213
214 if (newVal < 10)
215 {
216 // Retry the connect with a similar nick.
217 peerRetryWithNick(peer, nickStr.c_str());
218 }
219 else
220 {
221 // Cancel the connect.
222 peerRetryWithNick(peer, NULL);
223 MatcherClass *matcher = (MatcherClass *)param;
224 if (matcher)
225 matcher->handleNickError( badNick );
226 }
227 }
228 else
229 {
230 // Cancel the connect.
231 peerRetryWithNick(peer, NULL);
232 MatcherClass *matcher = (MatcherClass *)param;
233 if (matcher)
234 matcher->handleNickError( badNick );
235 }
236}
237
239
240void callbackEach( CHAT chat, CHATBool success, int index, const char *channel,
241 const char *topic, int numUsers, void *param )
242{
243 DEBUG_LOG(("Chat channel success: %d\n", success));
244 if (!success)
245 {
246 return;
247 }
248 DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
249 index, channel, topic, numUsers));
250}
251
252void callbackAll( CHAT chat, CHATBool success, int numChannels, const char **channels,
253 const char **topics, int *numUsers, void *param )
254{
255 DEBUG_LOG(("Chat channels success: %d\n", success));
256 if (!success)
257 {
258 return;
259 }
260
261 DEBUG_LOG(("%d channels found\n", numChannels));
262
263 for (int i=0; i<numChannels; ++i)
264 {
265 DEBUG_LOG(("Channel[%d]: %s (%s), %d users\n",
266 i, channels[i], topics[i], numUsers[i]));
267 }
268}
269
270void MatcherClass::handleConnect( bool success )
271{
272 m_connectSuccess = success;
273
274 //DEBUG_LOG(("Enumerating chat channels\n"));
275 //chatEnumChannels( peerGetChat(m_peer), "", callbackEach, callbackAll, NULL, CHATTrue );
276 //DEBUG_LOG(("Done enumerating chat channels\n"));
277}
278
279void MatcherClass::handleGroupRoomList( bool success, int groupID, const char *name )
280{
281}
282
283void MatcherClass::handleJoin( bool success )
284{
285 m_joinSuccess = success;
286 if (m_joinSuccess)
287 {
288 DBGMSG("Joined room - listing players");
289 peerEnumPlayers(m_peer, GroupRoom, EnumPlayersCallback, this);
290 }
291}
292
293void MatcherClass::handleNickError( const char *badNick )
294{
295 exit(1);
296}
297
298static void AuthenticateCDKeyCallback
299(
300 PEER peer,
301 int result,
302 const char * message,
303 void * param
304)
305{
306 bool *val = (bool *)param;
307 if (val)
308 {
309 *val = (result >= 1);
310 }
311}
312
314{
315 // Game-specific initializations, if neccessary
316 init();
317
318 // Check for possible quit from init()-based self-tests
319 if (done)
320 return ;
321
322 // Defaults.
324 Wstring title = "gmtest";
325 Wstring secretKey = "HA6zkS";
326 Wstring serialNo = "";
327 m_profileID = 0;
328 Global.config.getString("Nick", m_baseNick, "LOGIN");
329 DBGMSG("base nick is " << m_baseNick.get());
330 m_baseNick.toLower();
331 Global.config.getString("Title", title, "LOGIN");
332 Global.config.getString("SecretKey", secretKey, "LOGIN");
333 Global.config.getInt("ProfileID", m_profileID, "LOGIN");
334 Global.config.getString("CDKey", serialNo, "LOGIN");
335 PEERCallbacks callbacks;
336 PEERBool pingRooms[NumRooms];
337 PEERBool crossPingRooms[NumRooms];
338
339 // Setup the callbacks.
341 memset(&callbacks, 0, sizeof(PEERCallbacks));
342 callbacks.disconnected = DisconnectedCallback;
343 callbacks.playerChangedNick = PlayerChangedNickCallback;
344 callbacks.playerJoined = PlayerJoinedCallback;
345 callbacks.playerLeft = PlayerLeftCallback;
346 callbacks.roomMessage = RoomMessageCallback;
347 callbacks.playerMessage = PlayerMessageCallback;
348 callbacks.param = this;
349
350 // Init.
352 m_peer = peerInitialize(&callbacks);
353 if(!m_peer)
354 {
355 ERRMSG("Failed to init peer object" << endl);
356 return;
357 }
358
359 // Ping/cross-ping in no room.
361 pingRooms[TitleRoom] = PEERFalse;
362 pingRooms[GroupRoom] = PEERFalse;
363 pingRooms[StagingRoom] = PEERFalse;
364 crossPingRooms[TitleRoom] = PEERFalse;
365 crossPingRooms[GroupRoom] = PEERFalse;
366 crossPingRooms[StagingRoom] = PEERFalse;
367
368 // Set the title.
370 if(!peerSetTitle(m_peer, title.get(), secretKey.get(), title.get(), secretKey.get(), 0, 30, PEERTrue, pingRooms, crossPingRooms))
371 {
372 peerShutdown(m_peer);
373 ERRMSG("Failed to set the title" << endl);
374 return;
375 }
376
377 // Connect.
379 m_connectSuccess = false;
380 m_nick = m_baseNick.get();
381 peerConnect(m_peer, m_baseNick.get(), m_profileID, NickErrorCallback, ConnectCallback, this, PEERTrue);
383 {
384 peerShutdown(m_peer);
385 ERRMSG("Failed to connect" << endl);
386 return;
387 }
388
389 bool cdOk = false;
390 peerAuthenticateCDKey(m_peer, serialNo.get(), AuthenticateCDKeyCallback, &cdOk, PEERTrue);
391 if (!cdOk)
392 {
393 peerShutdown(m_peer);
394 ERRMSG("Failed to auth CDKey " << serialNo.get() << endl);
395 return;
396 }
397
398 m_groupID = 0;
399 peerListGroupRooms(m_peer, NULL, ListGroupRoomsCallback, &m_groupID, PEERTrue);
400 m_groupID = s_groupID;
401
402 DBGMSG("QuickMatch room is " << m_groupID);
403
404 // Join the title room.
406 peerJoinGroupRoom(m_peer, m_groupID, JoinCallback, this, PEERTrue);
407 if(!m_joinSuccess)
408 {
409 peerDisconnect(m_peer);
410 peerShutdown(m_peer);
411 ERRMSG("Failed to join the title room" << endl);
412 return;
413 }
414
415 // Connected, so lets do our thing
416 readLoop();
417
418 peerDisconnect(m_peer);
419 peerShutdown(m_peer);
420}
421
#define NULL
Definition BaseType.h:92
#define DEBUG_LOG(m)
Definition Debug.h:157
void PlayerMessageCallback(PEER peer, const char *nick, const char *message, MessageType messageType, void *param)
Called when a private message is received from another player.
void RoomMessageCallback(PEER peer, RoomType roomType, const char *nick, const char *message, MessageType messageType, void *param)
Called when a message arrives in a room.
#define INFMSG(X)
Definition wdebug.h:64
#define ERRMSG(X)
Definition wdebug.h:86
#define DBGMSG(X)
Definition wdebug.h:128
void rotateOutput(void)
Definition main.cpp:300
void rotateParanoid(void)
Definition main.cpp:376
virtual void handlePlayerEnum(bool success, int gameSpyIndex, const char *nick, int flags)
Definition matcher.h:61
void connectAndLoop(void)
Definition matcher.cpp:313
virtual void handleDisconnect(const char *reason)
Definition matcher.h:49
void handleConnect(bool success)
Definition matcher.cpp:270
bool m_rotateLogs
Definition matcher.h:87
int m_profileID
Definition matcher.h:77
Wstring getString(const Wstring &key)
Definition matcher.cpp:40
virtual void handlePlayerLeft(const char *nick)
Definition matcher.h:57
virtual void handlePlayerJoined(const char *nick)
Definition matcher.h:55
PEER m_peer
Definition matcher.h:78
virtual void init(void)
Definition matcher.h:43
virtual void handlePlayerChangedNick(const char *oldNick, const char *newNick)
Definition matcher.h:59
virtual void handlePlayerMessage(const char *nick, const char *message, MessageType messageType)
Definition matcher.h:53
virtual void handleRoomMessage(const char *nick, const char *message, MessageType messageType)
Definition matcher.h:51
int m_groupID
Definition matcher.h:85
void handleGroupRoomList(bool success, int groupID, const char *name)
Definition matcher.cpp:279
bool quiet
Definition matcher.h:84
bool m_connectSuccess
Definition matcher.h:79
void readLoop(void)
Definition matcher.cpp:55
virtual void checkMatches(void)
Definition matcher.h:46
time_t m_lastRotation
Definition matcher.h:88
Wstring m_baseNick
Definition matcher.h:75
bool m_joinSuccess
Definition matcher.h:80
std::string m_nick
Definition matcher.h:76
void handleNickError(const char *badNick)
Definition matcher.cpp:293
void handleJoin(bool success)
Definition matcher.cpp:283
char * get(void)
Definition wstring.cpp:336
Definition xtime.h:56
GlobalClass Global
Definition global.cpp:22
int TimezoneOffset(void)
Definition timezone.cpp:66
void logIt(const char *Txt)
Definition matcher.cpp:47
void callbackEach(CHAT chat, CHATBool success, int index, const char *channel, const char *topic, int numUsers, void *param)
Definition matcher.cpp:240
void callbackAll(CHAT chat, CHATBool success, int numChannels, const char **channels, const char **topics, int *numUsers, void *param)
Definition matcher.cpp:252
else return(RetVal)
long time_t
Definition wolapi.h:436