1/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
2#include "gamecontext.h"
3
4#include <engine/antibot.h>
5
6#include <engine/shared/config.h>
7#include <game/server/entities/character.h>
8#include <game/server/gamemodes/DDRace.h>
9#include <game/server/player.h>
10#include <game/server/save.h>
11#include <game/server/teams.h>
12
13bool CheckClientId(int ClientId);
14
15void CGameContext::ConGoLeft(IConsole::IResult *pResult, void *pUserData)
16{
17 CGameContext *pSelf = (CGameContext *)pUserData;
18 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
19
20 if(!CheckClientId(ClientId: pResult->m_ClientId))
21 return;
22 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: -1 * Tiles, Y: 0);
23}
24
25void CGameContext::ConGoRight(IConsole::IResult *pResult, void *pUserData)
26{
27 CGameContext *pSelf = (CGameContext *)pUserData;
28 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
29
30 if(!CheckClientId(ClientId: pResult->m_ClientId))
31 return;
32 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: Tiles, Y: 0);
33}
34
35void CGameContext::ConGoDown(IConsole::IResult *pResult, void *pUserData)
36{
37 CGameContext *pSelf = (CGameContext *)pUserData;
38 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
39
40 if(!CheckClientId(ClientId: pResult->m_ClientId))
41 return;
42 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: 0, Y: Tiles);
43}
44
45void CGameContext::ConGoUp(IConsole::IResult *pResult, void *pUserData)
46{
47 CGameContext *pSelf = (CGameContext *)pUserData;
48 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
49
50 if(!CheckClientId(ClientId: pResult->m_ClientId))
51 return;
52 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: 0, Y: -1 * Tiles);
53}
54
55void CGameContext::ConMove(IConsole::IResult *pResult, void *pUserData)
56{
57 CGameContext *pSelf = (CGameContext *)pUserData;
58 if(!CheckClientId(ClientId: pResult->m_ClientId))
59 return;
60 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: pResult->GetInteger(Index: 0),
61 Y: pResult->GetInteger(Index: 1));
62}
63
64void CGameContext::ConMoveRaw(IConsole::IResult *pResult, void *pUserData)
65{
66 CGameContext *pSelf = (CGameContext *)pUserData;
67 if(!CheckClientId(ClientId: pResult->m_ClientId))
68 return;
69 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: pResult->GetInteger(Index: 0),
70 Y: pResult->GetInteger(Index: 1), Raw: true);
71}
72
73void CGameContext::MoveCharacter(int ClientId, int X, int Y, bool Raw)
74{
75 CCharacter *pChr = GetPlayerChar(ClientId);
76
77 if(!pChr)
78 return;
79
80 pChr->Move(RelPos: vec2((Raw ? 1 : 32) * X, (Raw ? 1 : 32) * Y));
81 pChr->m_DDRaceState = DDRACE_CHEAT;
82}
83
84void CGameContext::ConKillPlayer(IConsole::IResult *pResult, void *pUserData)
85{
86 CGameContext *pSelf = (CGameContext *)pUserData;
87 if(!CheckClientId(ClientId: pResult->m_ClientId))
88 return;
89 int Victim = pResult->GetVictim();
90
91 if(pSelf->m_apPlayers[Victim])
92 {
93 pSelf->m_apPlayers[Victim]->KillCharacter(Weapon: WEAPON_GAME);
94 char aBuf[512];
95 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s was killed by %s",
96 pSelf->Server()->ClientName(ClientId: Victim),
97 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId));
98 pSelf->SendChat(ClientId: -1, Team: CGameContext::CHAT_ALL, pText: aBuf);
99 }
100}
101
102void CGameContext::ConNinja(IConsole::IResult *pResult, void *pUserData)
103{
104 CGameContext *pSelf = (CGameContext *)pUserData;
105 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_NINJA, Remove: false);
106}
107
108void CGameContext::ConUnNinja(IConsole::IResult *pResult, void *pUserData)
109{
110 CGameContext *pSelf = (CGameContext *)pUserData;
111 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_NINJA, Remove: true);
112}
113
114void CGameContext::ConEndlessHook(IConsole::IResult *pResult, void *pUserData)
115{
116 CGameContext *pSelf = (CGameContext *)pUserData;
117 if(!CheckClientId(ClientId: pResult->m_ClientId))
118 return;
119 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
120 if(pChr)
121 {
122 pChr->SetEndlessHook(true);
123 }
124}
125
126void CGameContext::ConUnEndlessHook(IConsole::IResult *pResult, void *pUserData)
127{
128 CGameContext *pSelf = (CGameContext *)pUserData;
129 if(!CheckClientId(ClientId: pResult->m_ClientId))
130 return;
131 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
132 if(pChr)
133 {
134 pChr->SetEndlessHook(false);
135 }
136}
137
138void CGameContext::ConSuper(IConsole::IResult *pResult, void *pUserData)
139{
140 CGameContext *pSelf = (CGameContext *)pUserData;
141 if(!CheckClientId(ClientId: pResult->m_ClientId))
142 return;
143 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
144 if(pChr && !pChr->IsSuper())
145 {
146 pChr->SetSuper(true);
147 pChr->UnFreeze();
148 }
149}
150
151void CGameContext::ConUnSuper(IConsole::IResult *pResult, void *pUserData)
152{
153 CGameContext *pSelf = (CGameContext *)pUserData;
154 if(!CheckClientId(ClientId: pResult->m_ClientId))
155 return;
156 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
157 if(pChr && pChr->IsSuper())
158 {
159 pChr->SetSuper(false);
160 }
161}
162
163void CGameContext::ConSolo(IConsole::IResult *pResult, void *pUserData)
164{
165 CGameContext *pSelf = (CGameContext *)pUserData;
166 if(!CheckClientId(ClientId: pResult->m_ClientId))
167 return;
168 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
169 if(pChr)
170 pChr->SetSolo(true);
171}
172
173void CGameContext::ConUnSolo(IConsole::IResult *pResult, void *pUserData)
174{
175 CGameContext *pSelf = (CGameContext *)pUserData;
176 if(!CheckClientId(ClientId: pResult->m_ClientId))
177 return;
178 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
179 if(pChr)
180 pChr->SetSolo(false);
181}
182
183void CGameContext::ConFreeze(IConsole::IResult *pResult, void *pUserData)
184{
185 CGameContext *pSelf = (CGameContext *)pUserData;
186 if(!CheckClientId(ClientId: pResult->m_ClientId))
187 return;
188 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
189 if(pChr)
190 pChr->Freeze();
191}
192
193void CGameContext::ConUnFreeze(IConsole::IResult *pResult, void *pUserData)
194{
195 CGameContext *pSelf = (CGameContext *)pUserData;
196 if(!CheckClientId(ClientId: pResult->m_ClientId))
197 return;
198 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
199 if(pChr)
200 pChr->UnFreeze();
201}
202
203void CGameContext::ConDeep(IConsole::IResult *pResult, void *pUserData)
204{
205 CGameContext *pSelf = (CGameContext *)pUserData;
206 if(!CheckClientId(ClientId: pResult->m_ClientId))
207 return;
208 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
209 if(pChr)
210 pChr->SetDeepFrozen(true);
211}
212
213void CGameContext::ConUnDeep(IConsole::IResult *pResult, void *pUserData)
214{
215 CGameContext *pSelf = (CGameContext *)pUserData;
216 if(!CheckClientId(ClientId: pResult->m_ClientId))
217 return;
218 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
219 if(pChr)
220 {
221 pChr->SetDeepFrozen(false);
222 pChr->UnFreeze();
223 }
224}
225
226void CGameContext::ConLiveFreeze(IConsole::IResult *pResult, void *pUserData)
227{
228 CGameContext *pSelf = (CGameContext *)pUserData;
229 if(!CheckClientId(ClientId: pResult->m_ClientId))
230 return;
231 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
232 if(pChr)
233 pChr->SetLiveFrozen(true);
234}
235
236void CGameContext::ConUnLiveFreeze(IConsole::IResult *pResult, void *pUserData)
237{
238 CGameContext *pSelf = (CGameContext *)pUserData;
239 if(!CheckClientId(ClientId: pResult->m_ClientId))
240 return;
241 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
242 if(pChr)
243 pChr->SetLiveFrozen(false);
244}
245
246void CGameContext::ConShotgun(IConsole::IResult *pResult, void *pUserData)
247{
248 CGameContext *pSelf = (CGameContext *)pUserData;
249 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_SHOTGUN, Remove: false);
250}
251
252void CGameContext::ConGrenade(IConsole::IResult *pResult, void *pUserData)
253{
254 CGameContext *pSelf = (CGameContext *)pUserData;
255 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_GRENADE, Remove: false);
256}
257
258void CGameContext::ConLaser(IConsole::IResult *pResult, void *pUserData)
259{
260 CGameContext *pSelf = (CGameContext *)pUserData;
261 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_LASER, Remove: false);
262}
263
264void CGameContext::ConJetpack(IConsole::IResult *pResult, void *pUserData)
265{
266 CGameContext *pSelf = (CGameContext *)pUserData;
267 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
268 if(pChr)
269 pChr->SetJetpack(true);
270}
271
272void CGameContext::ConWeapons(IConsole::IResult *pResult, void *pUserData)
273{
274 CGameContext *pSelf = (CGameContext *)pUserData;
275 pSelf->ModifyWeapons(pResult, pUserData, Weapon: -1, Remove: false);
276}
277
278void CGameContext::ConUnShotgun(IConsole::IResult *pResult, void *pUserData)
279{
280 CGameContext *pSelf = (CGameContext *)pUserData;
281 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_SHOTGUN, Remove: true);
282}
283
284void CGameContext::ConUnGrenade(IConsole::IResult *pResult, void *pUserData)
285{
286 CGameContext *pSelf = (CGameContext *)pUserData;
287 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_GRENADE, Remove: true);
288}
289
290void CGameContext::ConUnLaser(IConsole::IResult *pResult, void *pUserData)
291{
292 CGameContext *pSelf = (CGameContext *)pUserData;
293 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_LASER, Remove: true);
294}
295
296void CGameContext::ConUnJetpack(IConsole::IResult *pResult, void *pUserData)
297{
298 CGameContext *pSelf = (CGameContext *)pUserData;
299 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
300 if(pChr)
301 pChr->SetJetpack(false);
302}
303
304void CGameContext::ConUnWeapons(IConsole::IResult *pResult, void *pUserData)
305{
306 CGameContext *pSelf = (CGameContext *)pUserData;
307 pSelf->ModifyWeapons(pResult, pUserData, Weapon: -1, Remove: true);
308}
309
310void CGameContext::ConAddWeapon(IConsole::IResult *pResult, void *pUserData)
311{
312 CGameContext *pSelf = (CGameContext *)pUserData;
313 pSelf->ModifyWeapons(pResult, pUserData, Weapon: pResult->GetInteger(Index: 0), Remove: false);
314}
315
316void CGameContext::ConRemoveWeapon(IConsole::IResult *pResult, void *pUserData)
317{
318 CGameContext *pSelf = (CGameContext *)pUserData;
319 pSelf->ModifyWeapons(pResult, pUserData, Weapon: pResult->GetInteger(Index: 0), Remove: true);
320}
321
322void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData,
323 int Weapon, bool Remove)
324{
325 CGameContext *pSelf = (CGameContext *)pUserData;
326 CCharacter *pChr = GetPlayerChar(ClientId: pResult->m_ClientId);
327 if(!pChr)
328 return;
329
330 if(clamp(val: Weapon, lo: -1, hi: NUM_WEAPONS - 1) != Weapon)
331 {
332 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "info",
333 pStr: "invalid weapon id");
334 return;
335 }
336
337 if(Weapon == -1)
338 {
339 pChr->GiveWeapon(Weapon: WEAPON_SHOTGUN, Remove);
340 pChr->GiveWeapon(Weapon: WEAPON_GRENADE, Remove);
341 pChr->GiveWeapon(Weapon: WEAPON_LASER, Remove);
342 }
343 else
344 {
345 pChr->GiveWeapon(Weapon, Remove);
346 }
347
348 pChr->m_DDRaceState = DDRACE_CHEAT;
349}
350
351void CGameContext::Teleport(CCharacter *pChr, vec2 Pos)
352{
353 pChr->SetPosition(Pos);
354 pChr->m_Pos = Pos;
355 pChr->m_PrevPos = Pos;
356 pChr->m_DDRaceState = DDRACE_CHEAT;
357}
358
359void CGameContext::ConToTeleporter(IConsole::IResult *pResult, void *pUserData)
360{
361 CGameContext *pSelf = (CGameContext *)pUserData;
362 unsigned int TeleTo = pResult->GetInteger(Index: 0);
363
364 if(!pSelf->Collision()->TeleOuts(Number: TeleTo - 1).empty())
365 {
366 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
367 if(pChr)
368 {
369 int TeleOut = pSelf->m_World.m_Core.RandomOr0(BelowThis: pSelf->Collision()->TeleOuts(Number: TeleTo - 1).size());
370 pSelf->Teleport(pChr, Pos: pSelf->Collision()->TeleOuts(Number: TeleTo - 1)[TeleOut]);
371 }
372 }
373}
374
375void CGameContext::ConToCheckTeleporter(IConsole::IResult *pResult, void *pUserData)
376{
377 CGameContext *pSelf = (CGameContext *)pUserData;
378 unsigned int TeleTo = pResult->GetInteger(Index: 0);
379
380 if(!pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1).empty())
381 {
382 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
383 if(pChr)
384 {
385 int TeleOut = pSelf->m_World.m_Core.RandomOr0(BelowThis: pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1).size());
386 pSelf->Teleport(pChr, Pos: pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1)[TeleOut]);
387 pChr->m_TeleCheckpoint = TeleTo;
388 }
389 }
390}
391
392void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData)
393{
394 CGameContext *pSelf = (CGameContext *)pUserData;
395 int Tele = pResult->NumArguments() == 2 ? pResult->GetInteger(Index: 0) : pResult->m_ClientId;
396 int TeleTo = pResult->NumArguments() ? pResult->GetInteger(Index: pResult->NumArguments() - 1) : pResult->m_ClientId;
397 int AuthLevel = pSelf->Server()->GetAuthedState(ClientId: pResult->m_ClientId);
398
399 if(Tele != pResult->m_ClientId && AuthLevel < g_Config.m_SvTeleOthersAuthLevel)
400 {
401 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "tele", pStr: "you aren't allowed to tele others");
402 return;
403 }
404
405 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Tele);
406 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
407
408 if(pChr && pPlayer && pSelf->GetPlayerChar(ClientId: TeleTo))
409 {
410 vec2 Pos = pSelf->m_apPlayers[TeleTo]->m_ViewPos;
411 if(!pPlayer->IsPaused() && !pResult->NumArguments())
412 {
413 Pos += vec2(pChr->Core()->m_Input.m_TargetX, pChr->Core()->m_Input.m_TargetY);
414 }
415 pSelf->Teleport(pChr, Pos);
416 pChr->ResetJumps();
417 pChr->UnFreeze();
418 pChr->SetVelocity(vec2(0, 0));
419 }
420}
421
422void CGameContext::ConKill(IConsole::IResult *pResult, void *pUserData)
423{
424 CGameContext *pSelf = (CGameContext *)pUserData;
425 if(!CheckClientId(ClientId: pResult->m_ClientId))
426 return;
427 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
428
429 if(!pPlayer || (pPlayer->m_LastKill && pPlayer->m_LastKill + pSelf->Server()->TickSpeed() * g_Config.m_SvKillDelay > pSelf->Server()->Tick()))
430 return;
431
432 pPlayer->m_LastKill = pSelf->Server()->Tick();
433 pPlayer->KillCharacter(Weapon: WEAPON_SELF);
434}
435
436void CGameContext::ConForcePause(IConsole::IResult *pResult, void *pUserData)
437{
438 CGameContext *pSelf = (CGameContext *)pUserData;
439 int Victim = pResult->GetVictim();
440 int Seconds = 0;
441 if(pResult->NumArguments() > 1)
442 Seconds = clamp(val: pResult->GetInteger(Index: 1), lo: 0, hi: 360);
443
444 CPlayer *pPlayer = pSelf->m_apPlayers[Victim];
445 if(!pPlayer)
446 return;
447
448 pPlayer->ForcePause(Time: Seconds);
449}
450
451bool CGameContext::TryVoteMute(const NETADDR *pAddr, int Secs, const char *pReason)
452{
453 // find a matching vote mute for this ip, update expiration time if found
454 for(int i = 0; i < m_NumVoteMutes; i++)
455 {
456 if(net_addr_comp_noport(a: &m_aVoteMutes[i].m_Addr, b: pAddr) == 0)
457 {
458 m_aVoteMutes[i].m_Expire = Server()->Tick() + Secs * Server()->TickSpeed();
459 str_copy(dst: m_aVoteMutes[i].m_aReason, src: pReason, dst_size: sizeof(m_aVoteMutes[i].m_aReason));
460 return true;
461 }
462 }
463
464 // nothing to update create new one
465 if(m_NumVoteMutes < MAX_VOTE_MUTES)
466 {
467 m_aVoteMutes[m_NumVoteMutes].m_Addr = *pAddr;
468 m_aVoteMutes[m_NumVoteMutes].m_Expire = Server()->Tick() + Secs * Server()->TickSpeed();
469 str_copy(dst: m_aVoteMutes[m_NumVoteMutes].m_aReason, src: pReason, dst_size: sizeof(m_aVoteMutes[m_NumVoteMutes].m_aReason));
470 m_NumVoteMutes++;
471 return true;
472 }
473 // no free slot found
474 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "votemute", pStr: "vote mute array is full");
475 return false;
476}
477
478void CGameContext::VoteMute(const NETADDR *pAddr, int Secs, const char *pReason, const char *pDisplayName, int AuthedId)
479{
480 if(!TryVoteMute(pAddr, Secs, pReason))
481 return;
482 if(!pDisplayName)
483 return;
484
485 char aBuf[128];
486 if(pReason[0])
487 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' banned '%s' for %d seconds from voting (%s)",
488 Server()->ClientName(ClientId: AuthedId), pDisplayName, Secs, pReason);
489 else
490 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' banned '%s' for %d seconds from voting",
491 Server()->ClientName(ClientId: AuthedId), pDisplayName, Secs);
492 SendChat(ClientId: -1, Team: CHAT_ALL, pText: aBuf);
493}
494
495bool CGameContext::VoteUnmute(const NETADDR *pAddr, const char *pDisplayName, int AuthedId)
496{
497 for(int i = 0; i < m_NumVoteMutes; i++)
498 {
499 if(net_addr_comp_noport(a: &m_aVoteMutes[i].m_Addr, b: pAddr) == 0)
500 {
501 m_NumVoteMutes--;
502 m_aVoteMutes[i] = m_aVoteMutes[m_NumVoteMutes];
503 if(pDisplayName)
504 {
505 char aBuf[128];
506 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' unbanned '%s' from voting.",
507 Server()->ClientName(ClientId: AuthedId), pDisplayName);
508 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "voteunmute", pStr: aBuf);
509 }
510 return true;
511 }
512 }
513 return false;
514}
515
516bool CGameContext::TryMute(const NETADDR *pAddr, int Secs, const char *pReason, bool InitialChatDelay)
517{
518 // find a matching mute for this ip, update expiration time if found
519 for(int i = 0; i < m_NumMutes; i++)
520 {
521 if(net_addr_comp_noport(a: &m_aMutes[i].m_Addr, b: pAddr) == 0)
522 {
523 const int NewExpire = Server()->Tick() + Secs * Server()->TickSpeed();
524 if(NewExpire > m_aMutes[i].m_Expire)
525 {
526 m_aMutes[i].m_Expire = NewExpire;
527 str_copy(dst: m_aMutes[i].m_aReason, src: pReason, dst_size: sizeof(m_aMutes[i].m_aReason));
528 m_aMutes[i].m_InitialChatDelay = InitialChatDelay;
529 }
530 return true;
531 }
532 }
533
534 // nothing to update create new one
535 if(m_NumMutes < MAX_MUTES)
536 {
537 m_aMutes[m_NumMutes].m_Addr = *pAddr;
538 m_aMutes[m_NumMutes].m_Expire = Server()->Tick() + Secs * Server()->TickSpeed();
539 str_copy(dst: m_aMutes[m_NumMutes].m_aReason, src: pReason, dst_size: sizeof(m_aMutes[m_NumMutes].m_aReason));
540 m_aMutes[m_NumMutes].m_InitialChatDelay = InitialChatDelay;
541 m_NumMutes++;
542 return true;
543 }
544 // no free slot found
545 Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes", pStr: "mute array is full");
546 return false;
547}
548
549void CGameContext::Mute(const NETADDR *pAddr, int Secs, const char *pDisplayName, const char *pReason, bool InitialChatDelay)
550{
551 if(Secs <= 0)
552 return;
553 if(!TryMute(pAddr, Secs, pReason, InitialChatDelay))
554 return;
555 if(InitialChatDelay)
556 return;
557 if(!pDisplayName)
558 return;
559
560 char aBuf[128];
561 if(pReason[0])
562 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' has been muted for %d seconds (%s)", pDisplayName, Secs, pReason);
563 else
564 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' has been muted for %d seconds", pDisplayName, Secs);
565 SendChat(ClientId: -1, Team: CHAT_ALL, pText: aBuf);
566}
567
568void CGameContext::ConVoteMute(IConsole::IResult *pResult, void *pUserData)
569{
570 CGameContext *pSelf = (CGameContext *)pUserData;
571 int Victim = pResult->GetVictim();
572
573 if(Victim < 0 || Victim > MAX_CLIENTS || !pSelf->m_apPlayers[Victim])
574 {
575 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "votemute", pStr: "Client ID not found");
576 return;
577 }
578
579 NETADDR Addr;
580 pSelf->Server()->GetClientAddr(ClientId: Victim, pAddr: &Addr);
581
582 int Seconds = clamp(val: pResult->GetInteger(Index: 1), lo: 1, hi: 86400);
583 const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "";
584 pSelf->VoteMute(pAddr: &Addr, Secs: Seconds, pReason, pDisplayName: pSelf->Server()->ClientName(ClientId: Victim), AuthedId: pResult->m_ClientId);
585}
586
587void CGameContext::ConVoteUnmute(IConsole::IResult *pResult, void *pUserData)
588{
589 CGameContext *pSelf = (CGameContext *)pUserData;
590 int Victim = pResult->GetVictim();
591
592 if(Victim < 0 || Victim > MAX_CLIENTS || !pSelf->m_apPlayers[Victim])
593 {
594 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "voteunmute", pStr: "Client ID not found");
595 return;
596 }
597
598 NETADDR Addr;
599 pSelf->Server()->GetClientAddr(ClientId: Victim, pAddr: &Addr);
600
601 bool Found = pSelf->VoteUnmute(pAddr: &Addr, pDisplayName: pSelf->Server()->ClientName(ClientId: Victim), AuthedId: pResult->m_ClientId);
602 if(Found)
603 {
604 char aBuf[128];
605 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' unbanned '%s' from voting.",
606 pSelf->Server()->ClientName(ClientId: pResult->m_ClientId), pSelf->Server()->ClientName(ClientId: Victim));
607 pSelf->SendChat(ClientId: -1, Team: 0, pText: aBuf);
608 }
609}
610
611void CGameContext::ConVoteMutes(IConsole::IResult *pResult, void *pUserData)
612{
613 CGameContext *pSelf = (CGameContext *)pUserData;
614
615 if(pSelf->m_NumVoteMutes <= 0)
616 {
617 // Just to make sure.
618 pSelf->m_NumVoteMutes = 0;
619 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "votemutes",
620 pStr: "There are no active vote mutes.");
621 return;
622 }
623
624 char aIpBuf[64];
625 char aBuf[128];
626 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "votemutes",
627 pStr: "Active vote mutes:");
628 for(int i = 0; i < pSelf->m_NumVoteMutes; i++)
629 {
630 net_addr_str(addr: &pSelf->m_aVoteMutes[i].m_Addr, string: aIpBuf, max_length: sizeof(aIpBuf), add_port: false);
631 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d: \"%s\", %d seconds left (%s)", i,
632 aIpBuf, (pSelf->m_aVoteMutes[i].m_Expire - pSelf->Server()->Tick()) / pSelf->Server()->TickSpeed(), pSelf->m_aVoteMutes[i].m_aReason);
633 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "votemutes", pStr: aBuf);
634 }
635}
636
637void CGameContext::ConMute(IConsole::IResult *pResult, void *pUserData)
638{
639 CGameContext *pSelf = (CGameContext *)pUserData;
640 pSelf->Console()->Print(
641 Level: IConsole::OUTPUT_LEVEL_STANDARD,
642 pFrom: "mutes",
643 pStr: "Use either 'muteid <client_id> <seconds> <reason>' or 'muteip <ip> <seconds> <reason>'");
644}
645
646// mute through client id
647void CGameContext::ConMuteId(IConsole::IResult *pResult, void *pUserData)
648{
649 CGameContext *pSelf = (CGameContext *)pUserData;
650 int Victim = pResult->GetVictim();
651
652 if(Victim < 0 || Victim > MAX_CLIENTS || !pSelf->m_apPlayers[Victim])
653 {
654 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "muteid", pStr: "Client id not found.");
655 return;
656 }
657
658 NETADDR Addr;
659 pSelf->Server()->GetClientAddr(ClientId: Victim, pAddr: &Addr);
660
661 const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "";
662
663 pSelf->Mute(pAddr: &Addr, Secs: clamp(val: pResult->GetInteger(Index: 1), lo: 1, hi: 86400),
664 pDisplayName: pSelf->Server()->ClientName(ClientId: Victim), pReason);
665}
666
667// mute through ip, arguments reversed to workaround parsing
668void CGameContext::ConMuteIp(IConsole::IResult *pResult, void *pUserData)
669{
670 CGameContext *pSelf = (CGameContext *)pUserData;
671 NETADDR Addr;
672 if(net_addr_from_str(addr: &Addr, string: pResult->GetString(Index: 0)))
673 {
674 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes",
675 pStr: "Invalid network address to mute");
676 }
677 const char *pReason = pResult->NumArguments() > 2 ? pResult->GetString(Index: 2) : "";
678 pSelf->Mute(pAddr: &Addr, Secs: clamp(val: pResult->GetInteger(Index: 1), lo: 1, hi: 86400), NULL, pReason);
679}
680
681// unmute by mute list index
682void CGameContext::ConUnmute(IConsole::IResult *pResult, void *pUserData)
683{
684 CGameContext *pSelf = (CGameContext *)pUserData;
685 int Index = pResult->GetInteger(Index: 0);
686
687 if(Index < 0 || Index >= pSelf->m_NumMutes)
688 return;
689
690 char aIpBuf[64];
691 char aBuf[64];
692 net_addr_str(addr: &pSelf->m_aMutes[Index].m_Addr, string: aIpBuf, max_length: sizeof(aIpBuf), add_port: false);
693 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Unmuted %s", aIpBuf);
694 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes", pStr: aBuf);
695
696 pSelf->m_NumMutes--;
697 pSelf->m_aMutes[Index] = pSelf->m_aMutes[pSelf->m_NumMutes];
698}
699
700// unmute by player id
701void CGameContext::ConUnmuteId(IConsole::IResult *pResult, void *pUserData)
702{
703 CGameContext *pSelf = (CGameContext *)pUserData;
704 int Victim = pResult->GetVictim();
705
706 if(Victim < 0 || Victim > MAX_CLIENTS || !pSelf->m_apPlayers[Victim])
707 return;
708
709 NETADDR Addr;
710 pSelf->Server()->GetClientAddr(ClientId: Victim, pAddr: &Addr);
711
712 for(int i = 0; i < pSelf->m_NumMutes; i++)
713 {
714 if(net_addr_comp_noport(a: &pSelf->m_aMutes[i].m_Addr, b: &Addr) == 0)
715 {
716 char aIpBuf[64];
717 char aBuf[64];
718 net_addr_str(addr: &pSelf->m_aMutes[i].m_Addr, string: aIpBuf, max_length: sizeof(aIpBuf), add_port: false);
719 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Unmuted %s", aIpBuf);
720 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes", pStr: aBuf);
721 pSelf->m_NumMutes--;
722 pSelf->m_aMutes[i] = pSelf->m_aMutes[pSelf->m_NumMutes];
723 return;
724 }
725 }
726}
727
728// list mutes
729void CGameContext::ConMutes(IConsole::IResult *pResult, void *pUserData)
730{
731 CGameContext *pSelf = (CGameContext *)pUserData;
732
733 if(pSelf->m_NumMutes <= 0)
734 {
735 // Just to make sure.
736 pSelf->m_NumMutes = 0;
737 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes",
738 pStr: "There are no active mutes.");
739 return;
740 }
741
742 char aIpBuf[64];
743 char aBuf[128];
744 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes",
745 pStr: "Active mutes:");
746 for(int i = 0; i < pSelf->m_NumMutes; i++)
747 {
748 net_addr_str(addr: &pSelf->m_aMutes[i].m_Addr, string: aIpBuf, max_length: sizeof(aIpBuf), add_port: false);
749 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%d: \"%s\", %d seconds left (%s)", i, aIpBuf,
750 (pSelf->m_aMutes[i].m_Expire - pSelf->Server()->Tick()) / pSelf->Server()->TickSpeed(), pSelf->m_aMutes[i].m_aReason);
751 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "mutes", pStr: aBuf);
752 }
753}
754
755void CGameContext::ConModerate(IConsole::IResult *pResult, void *pUserData)
756{
757 CGameContext *pSelf = (CGameContext *)pUserData;
758 if(!CheckClientId(ClientId: pResult->m_ClientId))
759 return;
760
761 bool HadModerator = pSelf->PlayerModerating();
762
763 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
764 pPlayer->m_Moderating = !pPlayer->m_Moderating;
765
766 if(!HadModerator && pPlayer->m_Moderating)
767 pSelf->SendChat(ClientId: -1, Team: CHAT_ALL, pText: "Server kick/spec votes will now be actively moderated.", SpamProtectionClientId: 0);
768
769 if(!pSelf->PlayerModerating())
770 pSelf->SendChat(ClientId: -1, Team: CHAT_ALL, pText: "Server kick/spec votes are no longer actively moderated.", SpamProtectionClientId: 0);
771
772 if(pPlayer->m_Moderating)
773 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Active moderator mode enabled for you.");
774 else
775 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Active moderator mode disabled for you.");
776}
777
778void CGameContext::ConSetDDRTeam(IConsole::IResult *pResult, void *pUserData)
779{
780 CGameContext *pSelf = (CGameContext *)pUserData;
781 auto *pController = pSelf->m_pController;
782
783 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
784 {
785 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "join",
786 pStr: "Teams are disabled");
787 return;
788 }
789
790 int Target = pResult->GetVictim();
791 int Team = pResult->GetInteger(Index: 1);
792
793 if(Team < TEAM_FLOCK || Team >= TEAM_SUPER)
794 return;
795
796 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Target);
797
798 if((pSelf->GetDDRaceTeam(ClientId: Target) && pController->Teams().GetDDRaceState(Player: pSelf->m_apPlayers[Target]) == DDRACE_STARTED) || (pChr && pController->Teams().IsPractice(Team: pChr->Team())))
799 pSelf->m_apPlayers[Target]->KillCharacter(Weapon: WEAPON_GAME);
800
801 pController->Teams().SetForceCharacterTeam(ClientId: Target, Team);
802}
803
804void CGameContext::ConUninvite(IConsole::IResult *pResult, void *pUserData)
805{
806 CGameContext *pSelf = (CGameContext *)pUserData;
807 auto *pController = pSelf->m_pController;
808
809 pController->Teams().SetClientInvited(Team: pResult->GetInteger(Index: 1), ClientId: pResult->GetVictim(), Invited: false);
810}
811
812void CGameContext::ConFreezeHammer(IConsole::IResult *pResult, void *pUserData)
813{
814 CGameContext *pSelf = (CGameContext *)pUserData;
815 int Victim = pResult->GetVictim();
816
817 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Victim);
818
819 if(!pChr)
820 return;
821
822 char aBuf[128];
823 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' got freeze hammer!",
824 pSelf->Server()->ClientName(ClientId: Victim));
825 pSelf->SendChat(ClientId: -1, Team: CHAT_ALL, pText: aBuf);
826
827 pChr->m_FreezeHammer = true;
828}
829
830void CGameContext::ConUnFreezeHammer(IConsole::IResult *pResult, void *pUserData)
831{
832 CGameContext *pSelf = (CGameContext *)pUserData;
833 int Victim = pResult->GetVictim();
834
835 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Victim);
836
837 if(!pChr)
838 return;
839
840 char aBuf[128];
841 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "'%s' lost freeze hammer!",
842 pSelf->Server()->ClientName(ClientId: Victim));
843 pSelf->SendChat(ClientId: -1, Team: CHAT_ALL, pText: aBuf);
844
845 pChr->m_FreezeHammer = false;
846}
847void CGameContext::ConVoteNo(IConsole::IResult *pResult, void *pUserData)
848{
849 CGameContext *pSelf = (CGameContext *)pUserData;
850
851 pSelf->ForceVote(EnforcerId: pResult->m_ClientId, Success: false);
852}
853
854void CGameContext::ConDrySave(IConsole::IResult *pResult, void *pUserData)
855{
856 CGameContext *pSelf = (CGameContext *)pUserData;
857
858 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
859
860 if(!pPlayer || pSelf->Server()->GetAuthedState(ClientId: pResult->m_ClientId) != AUTHED_ADMIN)
861 return;
862
863 CSaveTeam SavedTeam;
864 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
865 int Result = SavedTeam.Save(pGameServer: pSelf, Team, Dry: true);
866 if(CSaveTeam::HandleSaveError(Result, ClientId: pResult->m_ClientId, pGameContext: pSelf))
867 return;
868
869 char aTimestamp[32];
870 str_timestamp(buffer: aTimestamp, buffer_size: sizeof(aTimestamp));
871 char aBuf[64];
872 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s_%s_%s.save", pSelf->Server()->GetMapName(), aTimestamp, pSelf->Server()->GetAuthName(ClientId: pResult->m_ClientId));
873 IOHANDLE File = pSelf->Storage()->OpenFile(pFilename: aBuf, Flags: IOFLAG_WRITE, Type: IStorage::TYPE_ALL);
874 if(!File)
875 return;
876
877 int Len = str_length(str: SavedTeam.GetString());
878 io_write(io: File, buffer: SavedTeam.GetString(), size: Len);
879 io_close(io: File);
880}
881
882void CGameContext::ConDumpAntibot(IConsole::IResult *pResult, void *pUserData)
883{
884 CGameContext *pSelf = (CGameContext *)pUserData;
885 pSelf->Antibot()->ConsoleCommand(pCommand: "dump");
886}
887
888void CGameContext::ConAntibot(IConsole::IResult *pResult, void *pUserData)
889{
890 CGameContext *pSelf = (CGameContext *)pUserData;
891 pSelf->Antibot()->ConsoleCommand(pCommand: pResult->GetString(Index: 0));
892}
893
894void CGameContext::ConDumpLog(IConsole::IResult *pResult, void *pUserData)
895{
896 CGameContext *pSelf = (CGameContext *)pUserData;
897 int LimitSecs = MAX_LOG_SECONDS;
898 if(pResult->NumArguments() > 0)
899 LimitSecs = pResult->GetInteger(Index: 0);
900
901 if(LimitSecs < 0)
902 return;
903
904 int Iterator = pSelf->m_LatestLog;
905 for(int i = 0; i < MAX_LOGS; i++)
906 {
907 CLog *pEntry = &pSelf->m_aLogs[Iterator];
908 Iterator = (Iterator + 1) % MAX_LOGS;
909
910 if(!pEntry->m_Timestamp)
911 continue;
912
913 int Seconds = (time_get() - pEntry->m_Timestamp) / time_freq();
914 if(Seconds > LimitSecs)
915 continue;
916
917 char aBuf[256];
918 if(pEntry->m_FromServer)
919 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s, %d seconds ago", pEntry->m_aDescription, Seconds);
920 else
921 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s, %d seconds ago < addr=<{%s}> name='%s' client=%d",
922 pEntry->m_aDescription, Seconds, pEntry->m_aClientAddrStr, pEntry->m_aClientName, pEntry->m_ClientVersion);
923 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "log", pStr: aBuf);
924 }
925}
926
927void CGameContext::LogEvent(const char *Description, int ClientId)
928{
929 CLog *pNewEntry = &m_aLogs[m_LatestLog];
930 m_LatestLog = (m_LatestLog + 1) % MAX_LOGS;
931
932 pNewEntry->m_Timestamp = time_get();
933 str_copy(dst&: pNewEntry->m_aDescription, src: Description);
934 pNewEntry->m_FromServer = ClientId < 0;
935 if(!pNewEntry->m_FromServer)
936 {
937 pNewEntry->m_ClientVersion = Server()->GetClientVersion(ClientId);
938 Server()->GetClientAddr(ClientId, pAddrStr: pNewEntry->m_aClientAddrStr, Size: sizeof(pNewEntry->m_aClientAddrStr));
939 str_copy(dst&: pNewEntry->m_aClientName, src: Server()->ClientName(ClientId));
940 }
941}
942