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 <base/io.h>
5#include <base/log.h>
6#include <base/time.h>
7
8#include <engine/antibot.h>
9#include <engine/shared/config.h>
10
11#include <game/mapitems.h>
12#include <game/server/entities/character.h>
13#include <game/server/gamemodes/ddnet.h>
14#include <game/server/player.h>
15#include <game/server/save.h>
16#include <game/server/teams.h>
17
18void CGameContext::ConGoLeft(IConsole::IResult *pResult, void *pUserData)
19{
20 CGameContext *pSelf = (CGameContext *)pUserData;
21 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
22
23 if(!CheckClientId(ClientId: pResult->m_ClientId))
24 return;
25 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: -1 * Tiles, Y: 0);
26}
27
28void CGameContext::ConGoRight(IConsole::IResult *pResult, void *pUserData)
29{
30 CGameContext *pSelf = (CGameContext *)pUserData;
31 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
32
33 if(!CheckClientId(ClientId: pResult->m_ClientId))
34 return;
35 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: Tiles, Y: 0);
36}
37
38void CGameContext::ConGoDown(IConsole::IResult *pResult, void *pUserData)
39{
40 CGameContext *pSelf = (CGameContext *)pUserData;
41 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
42
43 if(!CheckClientId(ClientId: pResult->m_ClientId))
44 return;
45 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: 0, Y: Tiles);
46}
47
48void CGameContext::ConGoUp(IConsole::IResult *pResult, void *pUserData)
49{
50 CGameContext *pSelf = (CGameContext *)pUserData;
51 int Tiles = pResult->NumArguments() == 1 ? pResult->GetInteger(Index: 0) : 1;
52
53 if(!CheckClientId(ClientId: pResult->m_ClientId))
54 return;
55 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: 0, Y: -1 * Tiles);
56}
57
58void CGameContext::ConMove(IConsole::IResult *pResult, void *pUserData)
59{
60 CGameContext *pSelf = (CGameContext *)pUserData;
61 if(!CheckClientId(ClientId: pResult->m_ClientId))
62 return;
63 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: pResult->GetInteger(Index: 0),
64 Y: pResult->GetInteger(Index: 1));
65}
66
67void CGameContext::ConMoveRaw(IConsole::IResult *pResult, void *pUserData)
68{
69 CGameContext *pSelf = (CGameContext *)pUserData;
70 if(!CheckClientId(ClientId: pResult->m_ClientId))
71 return;
72 pSelf->MoveCharacter(ClientId: pResult->m_ClientId, X: pResult->GetInteger(Index: 0),
73 Y: pResult->GetInteger(Index: 1), Raw: true);
74}
75
76void CGameContext::MoveCharacter(int ClientId, int X, int Y, bool Raw)
77{
78 CCharacter *pChr = GetPlayerChar(ClientId);
79
80 if(!pChr)
81 return;
82
83 pChr->Move(RelPos: vec2((Raw ? 1 : 32) * X, (Raw ? 1 : 32) * Y));
84 pChr->ResetVelocity();
85 pChr->m_DDRaceState = ERaceState::CHEATED;
86}
87
88void CGameContext::ConKillPlayer(IConsole::IResult *pResult, void *pUserData)
89{
90 CGameContext *pSelf = (CGameContext *)pUserData;
91 if(!CheckClientId(ClientId: pResult->m_ClientId))
92 return;
93 int Victim = pResult->GetVictim();
94
95 if(pSelf->m_apPlayers[Victim])
96 {
97 pSelf->m_apPlayers[Victim]->KillCharacter(Weapon: WEAPON_GAME);
98 char aBuf[512];
99 if(pResult->NumArguments() == 2)
100 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s was killed by authorized player (%s)",
101 pSelf->Server()->ClientName(ClientId: Victim),
102 pResult->GetString(Index: 1));
103 else
104 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s was killed by authorized player",
105 pSelf->Server()->ClientName(ClientId: Victim));
106 pSelf->SendChat(ClientId: -1, Team: TEAM_ALL, pText: aBuf);
107 }
108}
109
110void CGameContext::ConNinja(IConsole::IResult *pResult, void *pUserData)
111{
112 CGameContext *pSelf = (CGameContext *)pUserData;
113 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_NINJA, Remove: false);
114}
115
116void CGameContext::ConUnNinja(IConsole::IResult *pResult, void *pUserData)
117{
118 CGameContext *pSelf = (CGameContext *)pUserData;
119 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_NINJA, Remove: true);
120}
121
122void CGameContext::ConEndlessHook(IConsole::IResult *pResult, void *pUserData)
123{
124 CGameContext *pSelf = (CGameContext *)pUserData;
125 if(!CheckClientId(ClientId: pResult->m_ClientId))
126 return;
127 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
128 if(pChr)
129 {
130 pChr->SetEndlessHook(true);
131 }
132}
133
134void CGameContext::ConUnEndlessHook(IConsole::IResult *pResult, void *pUserData)
135{
136 CGameContext *pSelf = (CGameContext *)pUserData;
137 if(!CheckClientId(ClientId: pResult->m_ClientId))
138 return;
139 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
140 if(pChr)
141 {
142 pChr->SetEndlessHook(false);
143 }
144}
145
146void CGameContext::ConSuper(IConsole::IResult *pResult, void *pUserData)
147{
148 CGameContext *pSelf = (CGameContext *)pUserData;
149 if(!CheckClientId(ClientId: pResult->m_ClientId))
150 return;
151 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
152 if(pChr && !pChr->IsSuper())
153 {
154 pChr->SetSuper(true);
155 pChr->Unfreeze();
156 }
157}
158
159void CGameContext::ConUnSuper(IConsole::IResult *pResult, void *pUserData)
160{
161 CGameContext *pSelf = (CGameContext *)pUserData;
162 if(!CheckClientId(ClientId: pResult->m_ClientId))
163 return;
164 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
165 if(pChr && pChr->IsSuper())
166 {
167 pChr->SetSuper(false);
168 }
169}
170
171void CGameContext::ConToggleInvincible(IConsole::IResult *pResult, void *pUserData)
172{
173 CGameContext *pSelf = (CGameContext *)pUserData;
174 if(!CheckClientId(ClientId: pResult->m_ClientId))
175 return;
176 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
177 if(pChr)
178 pChr->SetInvincible(pResult->NumArguments() == 0 ? !pChr->Core()->m_Invincible : pResult->GetInteger(Index: 0));
179}
180
181void CGameContext::ConSolo(IConsole::IResult *pResult, void *pUserData)
182{
183 CGameContext *pSelf = (CGameContext *)pUserData;
184 if(!CheckClientId(ClientId: pResult->m_ClientId))
185 return;
186 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
187 if(pChr)
188 pChr->SetSolo(true);
189}
190
191void CGameContext::ConUnSolo(IConsole::IResult *pResult, void *pUserData)
192{
193 CGameContext *pSelf = (CGameContext *)pUserData;
194 if(!CheckClientId(ClientId: pResult->m_ClientId))
195 return;
196 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
197 if(pChr)
198 pChr->SetSolo(false);
199}
200
201void CGameContext::ConFreeze(IConsole::IResult *pResult, void *pUserData)
202{
203 CGameContext *pSelf = (CGameContext *)pUserData;
204 if(!CheckClientId(ClientId: pResult->m_ClientId))
205 return;
206 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
207 if(pChr)
208 pChr->Freeze();
209}
210
211void CGameContext::ConUnfreeze(IConsole::IResult *pResult, void *pUserData)
212{
213 CGameContext *pSelf = (CGameContext *)pUserData;
214 if(!CheckClientId(ClientId: pResult->m_ClientId))
215 return;
216 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
217 if(pChr)
218 pChr->Unfreeze();
219}
220
221void CGameContext::ConDeep(IConsole::IResult *pResult, void *pUserData)
222{
223 CGameContext *pSelf = (CGameContext *)pUserData;
224 if(!CheckClientId(ClientId: pResult->m_ClientId))
225 return;
226 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
227 if(pChr)
228 pChr->SetDeepFrozen(true);
229}
230
231void CGameContext::ConUnDeep(IConsole::IResult *pResult, void *pUserData)
232{
233 CGameContext *pSelf = (CGameContext *)pUserData;
234 if(!CheckClientId(ClientId: pResult->m_ClientId))
235 return;
236 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
237 if(pChr)
238 {
239 pChr->SetDeepFrozen(false);
240 pChr->Unfreeze();
241 }
242}
243
244void CGameContext::ConLiveFreeze(IConsole::IResult *pResult, void *pUserData)
245{
246 CGameContext *pSelf = (CGameContext *)pUserData;
247 if(!CheckClientId(ClientId: pResult->m_ClientId))
248 return;
249 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
250 if(pChr)
251 pChr->SetLiveFrozen(true);
252}
253
254void CGameContext::ConUnLiveFreeze(IConsole::IResult *pResult, void *pUserData)
255{
256 CGameContext *pSelf = (CGameContext *)pUserData;
257 if(!CheckClientId(ClientId: pResult->m_ClientId))
258 return;
259 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
260 if(pChr)
261 pChr->SetLiveFrozen(false);
262}
263
264void CGameContext::ConShotgun(IConsole::IResult *pResult, void *pUserData)
265{
266 CGameContext *pSelf = (CGameContext *)pUserData;
267 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_SHOTGUN, Remove: false);
268}
269
270void CGameContext::ConGrenade(IConsole::IResult *pResult, void *pUserData)
271{
272 CGameContext *pSelf = (CGameContext *)pUserData;
273 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_GRENADE, Remove: false);
274}
275
276void CGameContext::ConLaser(IConsole::IResult *pResult, void *pUserData)
277{
278 CGameContext *pSelf = (CGameContext *)pUserData;
279 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_LASER, Remove: false);
280}
281
282void CGameContext::ConJetpack(IConsole::IResult *pResult, void *pUserData)
283{
284 CGameContext *pSelf = (CGameContext *)pUserData;
285 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
286 if(pChr)
287 pChr->SetJetpack(true);
288}
289
290void CGameContext::ConEndlessJump(IConsole::IResult *pResult, void *pUserData)
291{
292 CGameContext *pSelf = (CGameContext *)pUserData;
293 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
294 if(pChr)
295 pChr->SetEndlessJump(true);
296}
297
298void CGameContext::ConSetJumps(IConsole::IResult *pResult, void *pUserData)
299{
300 CGameContext *pSelf = (CGameContext *)pUserData;
301 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
302 if(pChr)
303 pChr->SetJumps(pResult->GetInteger(Index: 0));
304}
305
306void CGameContext::ConWeapons(IConsole::IResult *pResult, void *pUserData)
307{
308 CGameContext *pSelf = (CGameContext *)pUserData;
309 pSelf->ModifyWeapons(pResult, pUserData, Weapon: -1, Remove: false);
310}
311
312void CGameContext::ConUnShotgun(IConsole::IResult *pResult, void *pUserData)
313{
314 CGameContext *pSelf = (CGameContext *)pUserData;
315 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_SHOTGUN, Remove: true);
316}
317
318void CGameContext::ConUnGrenade(IConsole::IResult *pResult, void *pUserData)
319{
320 CGameContext *pSelf = (CGameContext *)pUserData;
321 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_GRENADE, Remove: true);
322}
323
324void CGameContext::ConUnLaser(IConsole::IResult *pResult, void *pUserData)
325{
326 CGameContext *pSelf = (CGameContext *)pUserData;
327 pSelf->ModifyWeapons(pResult, pUserData, Weapon: WEAPON_LASER, Remove: true);
328}
329
330void CGameContext::ConUnJetpack(IConsole::IResult *pResult, void *pUserData)
331{
332 CGameContext *pSelf = (CGameContext *)pUserData;
333 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
334 if(pChr)
335 pChr->SetJetpack(false);
336}
337
338void CGameContext::ConUnEndlessJump(IConsole::IResult *pResult, void *pUserData)
339{
340 CGameContext *pSelf = (CGameContext *)pUserData;
341 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
342 if(pChr)
343 pChr->SetEndlessJump(false);
344}
345
346void CGameContext::ConSetSwitch(IConsole::IResult *pResult, void *pUserData)
347{
348 CGameContext *pSelf = (CGameContext *)pUserData;
349 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
350 if(!pChr)
351 {
352 log_info("chatresp", "You can't set switch while you are dead/a spectator.");
353 return;
354 }
355 const int Team = pChr->Team();
356 const int Switch = pResult->GetInteger(Index: 0);
357 if(!in_range(a: Switch, upper: (int)pSelf->Switchers().size() - 1))
358 {
359 log_info("chatresp", "Invalid switch ID");
360 return;
361 }
362 const bool State = pResult->NumArguments() == 1 ? !pSelf->Switchers()[Switch].m_aStatus[Team] : pResult->GetInteger(Index: 1) != 0;
363 const int EndTick = pResult->NumArguments() == 3 ? pSelf->Server()->Tick() + 1 + pResult->GetInteger(Index: 2) * pSelf->Server()->TickSpeed() : 0;
364 pSelf->Switchers()[Switch].m_aStatus[Team] = State;
365 pSelf->Switchers()[Switch].m_aEndTick[Team] = EndTick;
366 if(State)
367 pSelf->Switchers()[Switch].m_aType[Team] = EndTick ? TILE_SWITCHTIMEDOPEN : TILE_SWITCHOPEN;
368 else
369 pSelf->Switchers()[Switch].m_aType[Team] = EndTick ? TILE_SWITCHTIMEDCLOSE : TILE_SWITCHCLOSE;
370}
371
372void CGameContext::ConUnWeapons(IConsole::IResult *pResult, void *pUserData)
373{
374 CGameContext *pSelf = (CGameContext *)pUserData;
375 pSelf->ModifyWeapons(pResult, pUserData, Weapon: -1, Remove: true);
376}
377
378void CGameContext::ConAddWeapon(IConsole::IResult *pResult, void *pUserData)
379{
380 CGameContext *pSelf = (CGameContext *)pUserData;
381 pSelf->ModifyWeapons(pResult, pUserData, Weapon: pResult->GetInteger(Index: 0), Remove: false);
382}
383
384void CGameContext::ConRemoveWeapon(IConsole::IResult *pResult, void *pUserData)
385{
386 CGameContext *pSelf = (CGameContext *)pUserData;
387 pSelf->ModifyWeapons(pResult, pUserData, Weapon: pResult->GetInteger(Index: 0), Remove: true);
388}
389
390void CGameContext::ModifyWeapons(IConsole::IResult *pResult, void *pUserData,
391 int Weapon, bool Remove)
392{
393 CGameContext *pSelf = (CGameContext *)pUserData;
394 CCharacter *pChr = GetPlayerChar(ClientId: pResult->m_ClientId);
395 if(!pChr)
396 return;
397
398 if(std::clamp(val: Weapon, lo: -1, hi: NUM_WEAPONS - 1) != Weapon)
399 {
400 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "info",
401 pStr: "invalid weapon id");
402 return;
403 }
404
405 if(Weapon == -1)
406 {
407 pChr->GiveWeapon(Weapon: WEAPON_SHOTGUN, Remove);
408 pChr->GiveWeapon(Weapon: WEAPON_GRENADE, Remove);
409 pChr->GiveWeapon(Weapon: WEAPON_LASER, Remove);
410 }
411 else
412 {
413 pChr->GiveWeapon(Weapon, Remove);
414 }
415
416 pChr->m_DDRaceState = ERaceState::CHEATED;
417}
418
419void CGameContext::Teleport(CCharacter *pChr, vec2 Pos)
420{
421 pChr->SetPosition(Pos);
422 pChr->m_Pos = Pos;
423 pChr->m_PrevPos = Pos;
424 pChr->m_DDRaceState = ERaceState::CHEATED;
425}
426
427void CGameContext::ConToTeleporter(IConsole::IResult *pResult, void *pUserData)
428{
429 CGameContext *pSelf = (CGameContext *)pUserData;
430 unsigned int TeleTo = pResult->GetInteger(Index: 0);
431
432 if(!pSelf->Collision()->TeleOuts(Number: TeleTo - 1).empty())
433 {
434 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
435 if(pChr)
436 {
437 int TeleOut = pSelf->m_World.m_Core.RandomOr0(BelowThis: pSelf->Collision()->TeleOuts(Number: TeleTo - 1).size());
438 pSelf->Teleport(pChr, Pos: pSelf->Collision()->TeleOuts(Number: TeleTo - 1)[TeleOut]);
439 }
440 }
441}
442
443void CGameContext::ConToCheckTeleporter(IConsole::IResult *pResult, void *pUserData)
444{
445 CGameContext *pSelf = (CGameContext *)pUserData;
446 unsigned int TeleTo = pResult->GetInteger(Index: 0);
447
448 if(!pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1).empty())
449 {
450 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: pResult->m_ClientId);
451 if(pChr)
452 {
453 int TeleOut = pSelf->m_World.m_Core.RandomOr0(BelowThis: pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1).size());
454 pSelf->Teleport(pChr, Pos: pSelf->Collision()->TeleCheckOuts(Number: TeleTo - 1)[TeleOut]);
455 pChr->m_TeleCheckpoint = TeleTo;
456 }
457 }
458}
459
460void CGameContext::ConTeleport(IConsole::IResult *pResult, void *pUserData)
461{
462 CGameContext *pSelf = (CGameContext *)pUserData;
463 if(!CheckClientId(ClientId: pResult->m_ClientId))
464 return;
465 int Tele = pResult->NumArguments() == 2 ? pResult->GetInteger(Index: 0) : pResult->m_ClientId;
466 int TeleTo = pResult->NumArguments() ? pResult->GetInteger(Index: pResult->NumArguments() - 1) : pResult->m_ClientId;
467 int AuthLevel = pSelf->Server()->GetAuthedState(ClientId: pResult->m_ClientId);
468
469 if(Tele != pResult->m_ClientId && AuthLevel < g_Config.m_SvTeleOthersAuthLevel)
470 {
471 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "tele", pStr: "you aren't allowed to tele others");
472 return;
473 }
474
475 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Tele);
476 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
477
478 if(pChr && pPlayer && pSelf->GetPlayerChar(ClientId: TeleTo))
479 {
480 // default to view pos when character is not available
481 vec2 Pos = pPlayer->m_ViewPos;
482 if(pResult->NumArguments() == 0 && !pPlayer->IsPaused() && pChr->IsAlive())
483 {
484 vec2 Target = vec2(pChr->Core()->m_Input.m_TargetX, pChr->Core()->m_Input.m_TargetY);
485 Pos = pPlayer->m_CameraInfo.ConvertTargetToWorld(Position: pChr->GetPos(), Target);
486 }
487 pSelf->Teleport(pChr, Pos);
488 pChr->ResetJumps();
489 pChr->Unfreeze();
490 pChr->SetVelocity(vec2(0, 0));
491 }
492}
493
494void CGameContext::ConKill(IConsole::IResult *pResult, void *pUserData)
495{
496 CGameContext *pSelf = (CGameContext *)pUserData;
497 if(!CheckClientId(ClientId: pResult->m_ClientId))
498 return;
499 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
500
501 if(!pPlayer || (pPlayer->m_LastKill && pPlayer->m_LastKill + pSelf->Server()->TickSpeed() * g_Config.m_SvKillDelay > pSelf->Server()->Tick()))
502 return;
503
504 pPlayer->m_LastKill = pSelf->Server()->Tick();
505 pPlayer->KillCharacter(Weapon: WEAPON_SELF);
506}
507
508void CGameContext::ConForcePause(IConsole::IResult *pResult, void *pUserData)
509{
510 CGameContext *pSelf = (CGameContext *)pUserData;
511 int Victim = pResult->GetVictim();
512 int Seconds = 0;
513 if(pResult->NumArguments() > 1)
514 Seconds = std::clamp(val: pResult->GetInteger(Index: 1), lo: 0, hi: 360);
515
516 CPlayer *pPlayer = pSelf->m_apPlayers[Victim];
517 if(!pPlayer)
518 return;
519
520 pPlayer->ForcePause(Time: Seconds);
521}
522
523void CGameContext::ConModerate(IConsole::IResult *pResult, void *pUserData)
524{
525 CGameContext *pSelf = (CGameContext *)pUserData;
526 if(!CheckClientId(ClientId: pResult->m_ClientId))
527 return;
528
529 bool HadModerator = pSelf->PlayerModerating();
530
531 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
532 pPlayer->m_Moderating = !pPlayer->m_Moderating;
533
534 if(!HadModerator && pPlayer->m_Moderating)
535 pSelf->SendChat(ClientId: -1, Team: TEAM_ALL, pText: "Server kick/spec votes will now be actively moderated.", SpamProtectionClientId: 0);
536
537 if(!pSelf->PlayerModerating())
538 pSelf->SendChat(ClientId: -1, Team: TEAM_ALL, pText: "Server kick/spec votes are no longer actively moderated.", SpamProtectionClientId: 0);
539
540 if(pPlayer->m_Moderating)
541 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Active moderator mode enabled for you.");
542 else
543 pSelf->SendChatTarget(To: pResult->m_ClientId, pText: "Active moderator mode disabled for you.");
544}
545
546void CGameContext::ConSetDDRTeam(IConsole::IResult *pResult, void *pUserData)
547{
548 CGameContext *pSelf = (CGameContext *)pUserData;
549 auto *pController = pSelf->m_pController;
550
551 if(g_Config.m_SvTeam == SV_TEAM_FORBIDDEN || g_Config.m_SvTeam == SV_TEAM_FORCED_SOLO)
552 {
553 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "join",
554 pStr: "Teams are disabled");
555 return;
556 }
557
558 const int Target = pResult->GetVictim();
559 CPlayer *pPlayer = pSelf->m_apPlayers[Target];
560 if(!pPlayer)
561 return;
562
563 const int Team = pResult->GetInteger(Index: 1);
564 if(!pController->Teams().IsValidTeamNumber(Team))
565 return;
566
567 CCharacter *pChr = pSelf->GetPlayerChar(ClientId: Target);
568
569 if((pSelf->GetDDRaceTeam(ClientId: Target) && pController->Teams().GetDDRaceState(Player: pPlayer) == ERaceState::STARTED) || (pChr && pController->Teams().IsPractice(Team: pChr->Team())))
570 pPlayer->KillCharacter(Weapon: WEAPON_GAME);
571
572 pController->Teams().SetForceCharacterTeam(ClientId: Target, Team);
573 pController->Teams().SetTeamLock(Team, Lock: true);
574}
575
576void CGameContext::ConUninvite(IConsole::IResult *pResult, void *pUserData)
577{
578 CGameContext *pSelf = (CGameContext *)pUserData;
579 auto *pController = pSelf->m_pController;
580
581 const int Target = pResult->GetVictim();
582 if(!pSelf->m_apPlayers[Target])
583 return;
584
585 pController->Teams().SetClientInvited(Team: pResult->GetInteger(Index: 1), ClientId: Target, Invited: false);
586}
587
588void CGameContext::ConVoteNo(IConsole::IResult *pResult, void *pUserData)
589{
590 CGameContext *pSelf = (CGameContext *)pUserData;
591
592 pSelf->ForceVote(Success: false);
593}
594
595void CGameContext::ConDrySave(IConsole::IResult *pResult, void *pUserData)
596{
597 CGameContext *pSelf = (CGameContext *)pUserData;
598 if(!CheckClientId(ClientId: pResult->m_ClientId))
599 return;
600 CPlayer *pPlayer = pSelf->m_apPlayers[pResult->m_ClientId];
601 if(!pPlayer || !pSelf->Server()->IsRconAuthedAdmin(ClientId: pResult->m_ClientId))
602 return;
603
604 CSaveTeam SavedTeam;
605 int Team = pSelf->GetDDRaceTeam(ClientId: pResult->m_ClientId);
606 ESaveResult Result = SavedTeam.Save(pGameServer: pSelf, Team, Dry: true);
607 if(CSaveTeam::HandleSaveError(Result, ClientId: pResult->m_ClientId, pGameContext: pSelf))
608 return;
609
610 char aTimestamp[32];
611 str_timestamp(buffer: aTimestamp, buffer_size: sizeof(aTimestamp));
612 char aBuf[64];
613 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s_%s_%s.save", pSelf->Map()->BaseName(), aTimestamp, pSelf->Server()->GetAuthName(ClientId: pResult->m_ClientId));
614 IOHANDLE File = pSelf->Storage()->OpenFile(pFilename: aBuf, Flags: IOFLAG_WRITE, Type: IStorage::TYPE_SAVE);
615 if(!File)
616 return;
617
618 int Len = str_length(str: SavedTeam.GetString());
619 io_write(io: File, buffer: SavedTeam.GetString(), size: Len);
620 io_close(io: File);
621}
622
623void CGameContext::ConReloadCensorlist(IConsole::IResult *pResult, void *pUserData)
624{
625 CGameContext *pSelf = (CGameContext *)pUserData;
626 pSelf->ReadCensorList();
627}
628
629void CGameContext::ConDumpAntibot(IConsole::IResult *pResult, void *pUserData)
630{
631 CGameContext *pSelf = (CGameContext *)pUserData;
632 pSelf->Antibot()->ConsoleCommand(pCommand: "dump");
633}
634
635void CGameContext::ConAntibot(IConsole::IResult *pResult, void *pUserData)
636{
637 CGameContext *pSelf = (CGameContext *)pUserData;
638 pSelf->Antibot()->ConsoleCommand(pCommand: pResult->GetString(Index: 0));
639}
640
641void CGameContext::ConDumpLog(IConsole::IResult *pResult, void *pUserData)
642{
643 CGameContext *pSelf = (CGameContext *)pUserData;
644 int LimitSecs = MAX_LOG_SECONDS;
645 if(pResult->NumArguments() > 0)
646 LimitSecs = pResult->GetInteger(Index: 0);
647
648 if(LimitSecs < 0)
649 return;
650
651 int Iterator = pSelf->m_LatestLog;
652 for(int i = 0; i < MAX_LOGS; i++)
653 {
654 CLog *pEntry = &pSelf->m_aLogs[Iterator];
655 Iterator = (Iterator + 1) % MAX_LOGS;
656
657 if(!pEntry->m_Timestamp)
658 continue;
659
660 int Seconds = (time_get() - pEntry->m_Timestamp) / time_freq();
661 if(Seconds > LimitSecs)
662 continue;
663
664 char aBuf[sizeof(pEntry->m_aDescription) + 128];
665 if(pEntry->m_FromServer)
666 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s, %d seconds ago", pEntry->m_aDescription, Seconds);
667 else
668 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "%s, %d seconds ago < addr=<{%s}> name='%s' client=%d",
669 pEntry->m_aDescription, Seconds, pEntry->m_aClientAddrStr, pEntry->m_aClientName, pEntry->m_ClientVersion);
670 pSelf->Console()->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: "log", pStr: aBuf);
671 }
672}
673
674void CGameContext::LogEvent(const char *Description, int ClientId)
675{
676 CLog *pNewEntry = &m_aLogs[m_LatestLog];
677 m_LatestLog = (m_LatestLog + 1) % MAX_LOGS;
678
679 pNewEntry->m_Timestamp = time_get();
680 str_copy(dst&: pNewEntry->m_aDescription, src: Description);
681 pNewEntry->m_FromServer = ClientId < 0;
682 if(!pNewEntry->m_FromServer)
683 {
684 pNewEntry->m_ClientVersion = Server()->GetClientVersion(ClientId);
685 str_copy(dst&: pNewEntry->m_aClientAddrStr, src: Server()->ClientAddrString(ClientId, IncludePort: false));
686 str_copy(dst&: pNewEntry->m_aClientName, src: Server()->ClientName(ClientId));
687 }
688}
689