1#include "test.h"
2
3#include <base/logger.h>
4#include <base/system.h>
5#include <base/types.h>
6
7#include <engine/engine.h>
8#include <engine/kernel.h>
9#include <engine/server/databases/connection.h>
10#include <engine/server/databases/connection_pool.h>
11#include <engine/server/register.h>
12#include <engine/server/server.h>
13#include <engine/server/server_logger.h>
14#include <engine/shared/assertion_logger.h>
15#include <engine/shared/config.h>
16
17#include <generated/protocol.h>
18
19#include <game/server/entities/character.h>
20#include <game/server/gamecontext.h>
21#include <game/server/gamecontroller.h>
22#include <game/server/gameworld.h>
23#include <game/server/player.h>
24#include <game/version.h>
25
26#include <gtest/gtest.h>
27
28#include <memory>
29#include <thread>
30
31bool IsInterrupted()
32{
33 return false;
34}
35
36std::vector<std::string> FakeQueue;
37std::vector<std::string> FetchAndroidServerCommandQueue()
38{
39 return FakeQueue;
40}
41
42class CTestGameWorld : public ::testing::Test
43{
44public:
45 IGameServer *m_pGameServer = nullptr;
46 CServer *m_pServer = nullptr;
47 std::unique_ptr<IKernel> m_pKernel;
48 CTestInfo m_TestInfo;
49 std::unique_ptr<IStorage> m_pStorage;
50
51 CGameContext *GameServer()
52 {
53 return (CGameContext *)m_pGameServer;
54 }
55
56 CTestGameWorld()
57 {
58 CServer *pServer = CreateServer();
59 m_pServer = pServer;
60
61 m_pKernel = std::unique_ptr<IKernel>(IKernel::Create());
62 m_pKernel->RegisterInterface(pInterface: m_pServer);
63
64 IEngine *pEngine = CreateTestEngine(GAME_NAME);
65 m_pKernel->RegisterInterface(pInterface: pEngine);
66
67 m_TestInfo.m_DeleteTestStorageFilesOnSuccess = true;
68 m_pStorage = m_TestInfo.CreateTestStorage();
69 EXPECT_NE(m_pStorage, nullptr);
70 m_pKernel->RegisterInterface(pInterface: m_pStorage.get(), Destroy: false);
71
72 IConsole *pConsole = CreateConsole(FlagMask: CFGFLAG_SERVER | CFGFLAG_ECON).release();
73 m_pKernel->RegisterInterface(pInterface: pConsole);
74
75 IConfigManager *pConfigManager = CreateConfigManager();
76 m_pKernel->RegisterInterface(pInterface: pConfigManager);
77
78 IEngineAntibot *pEngineAntibot = CreateEngineAntibot();
79 m_pKernel->RegisterInterface(pInterface: pEngineAntibot);
80 m_pKernel->RegisterInterface(pInterface: static_cast<IAntibot *>(pEngineAntibot), Destroy: false);
81
82 m_pGameServer = CreateGameServer();
83 m_pKernel->RegisterInterface(pInterface: m_pGameServer);
84
85 pEngine->Init();
86 pConsole->Init();
87 pConfigManager->Init();
88
89 m_pServer->RegisterCommands();
90
91 EXPECT_NE(m_pServer->LoadMap("coverage"), 0);
92
93 m_pServer->m_RunServer = CServer::RUNNING;
94
95 m_pServer->m_AuthManager.Init();
96
97 {
98 int Size = GameServer()->PersistentClientDataSize();
99 for(auto &Client : m_pServer->m_aClients)
100 {
101 Client.m_HasPersistentData = false;
102 Client.m_pPersistentData = malloc(size: Size);
103 }
104 }
105 m_pServer->m_pPersistentData = malloc(size: GameServer()->PersistentDataSize());
106 EXPECT_NE(m_pServer->LoadMap("coverage"), 0);
107
108 if(!pServer->m_Http.Init(ShutdownDelay: std::chrono::seconds{2}))
109 {
110 log_error("server", "Failed to initialize the HTTP client.");
111 }
112
113 pServer->m_NetServer.SetCallbacks(
114 pfnNewClient: CServer::NewClientCallback,
115 pfnNewClientNoAuth: CServer::NewClientNoAuthCallback,
116 pfnClientRejoin: CServer::ClientRejoinCallback,
117 pfnDelClient: CServer::DelClientCallback, pUser: pServer);
118
119 pServer->m_Econ.Init(pConfig: pServer->Config(), pConsole: pServer->Console(), pNetBan: &pServer->m_ServerBan);
120
121 pServer->m_Fifo.Init(pConsole: pServer->Console(), pFifoFile: pServer->Config()->m_SvInputFifo, Flag: CFGFLAG_SERVER);
122 m_pServer->Antibot()->Init();
123 GameServer()->OnInit(pPersistentData: nullptr);
124 pServer->ReadAnnouncementsFile();
125 pServer->InitMaplist();
126 }
127
128 ~CTestGameWorld() override
129 {
130 m_pServer->m_Econ.Shutdown();
131 m_pServer->m_Fifo.Shutdown();
132 m_pGameServer->OnShutdown(pPersistentData: nullptr);
133 m_pServer->DbPool()->OnShutdown();
134 }
135};
136
137TEST_F(CTestGameWorld, ClosestCharacter)
138{
139 CNetObj_PlayerInput Input = {};
140 CCharacter *pChr1 = new(0) CCharacter(&GameServer()->m_World, Input);
141 pChr1->m_Pos = vec2(0, 0);
142 GameServer()->m_World.InsertEntity(pEntity: pChr1);
143
144 CCharacter *pChr2 = new(1) CCharacter(&GameServer()->m_World, Input);
145 pChr2->m_Pos = vec2(10, 10);
146 GameServer()->m_World.InsertEntity(pEntity: pChr2);
147
148 CCharacter *pClosest = GameServer()->m_World.ClosestCharacter(Pos: vec2(1, 1), Radius: 20, pNotThis: nullptr);
149 EXPECT_EQ(pClosest, pChr1);
150}
151
152TEST_F(CTestGameWorld, IntersectEntity)
153{
154 CNetObj_PlayerInput Input = {};
155 CCharacter *pChrLeft = new(0) CCharacter(&GameServer()->m_World, Input);
156 pChrLeft->m_Pos = vec2(15, 10);
157 GameServer()->m_World.InsertEntity(pEntity: pChrLeft);
158
159 CCharacter *pChrRight = new(1) CCharacter(&GameServer()->m_World, Input);
160 pChrRight->m_Pos = vec2(16, 10);
161 GameServer()->m_World.InsertEntity(pEntity: pChrRight);
162
163 float Radius = 5.0f;
164 vec2 IntersectAt;
165 CCharacter *pIntersectedChar;
166
167 // both tees are exactly on the line
168 // if we go intersect left to right we find the left one
169
170 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
171 Pos0: vec2(10, 10), // intersect from
172 Pos1: vec2(20, 10), // intersect to
173 Radius,
174 Type: CGameWorld::ENTTYPE_CHARACTER,
175 NewPos&: IntersectAt,
176 pNotThis: nullptr, // pNotThis
177 CollideWith: -1, // CollideWith
178 pThisOnly: nullptr /* pThisOnly */);
179 EXPECT_EQ(pIntersectedChar, pChrLeft);
180
181 // if we intersect right to left we find the right one
182
183 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
184 Pos0: vec2(20, 10), // intersect from
185 Pos1: vec2(10, 10), // intersect to
186 Radius,
187 Type: CGameWorld::ENTTYPE_CHARACTER,
188 NewPos&: IntersectAt,
189 pNotThis: nullptr, // pNotThis
190 CollideWith: -1, // CollideWith
191 pThisOnly: nullptr /* pThisOnly */);
192 EXPECT_EQ(pIntersectedChar, pChrRight);
193
194 // but not if we ignore the right one
195
196 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
197 Pos0: vec2(20, 10), // intersect from
198 Pos1: vec2(10, 10), // intersect to
199 Radius,
200 Type: CGameWorld::ENTTYPE_CHARACTER,
201 NewPos&: IntersectAt,
202 pNotThis: pChrRight, // pNotThis
203 CollideWith: -1, // CollideWith
204 pThisOnly: nullptr /* pThisOnly */);
205 EXPECT_EQ(pIntersectedChar, pChrLeft);
206
207 // or we force find the left one
208
209 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
210 Pos0: vec2(20, 10), // intersect from
211 Pos1: vec2(10, 10), // intersect to
212 Radius,
213 Type: CGameWorld::ENTTYPE_CHARACTER,
214 NewPos&: IntersectAt,
215 pNotThis: nullptr, // pNotThis
216 CollideWith: -1, // CollideWith
217 pThisOnly: pChrLeft /* pThisOnly */);
218 EXPECT_EQ(pIntersectedChar, pChrLeft);
219
220 // pNotThis == pThisOnly => nullptr
221
222 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
223 Pos0: vec2(20, 10), // intersect from
224 Pos1: vec2(10, 10), // intersect to
225 Radius,
226 Type: CGameWorld::ENTTYPE_CHARACTER,
227 NewPos&: IntersectAt,
228 pNotThis: pChrLeft, // pNotThis
229 CollideWith: -1, // CollideWith
230 pThisOnly: pChrLeft /* pThisOnly */);
231 EXPECT_EQ(pIntersectedChar, nullptr);
232
233 // the tee closer to the start of the intersection line
234 // will not be matched if it is further than Radius away
235 // from the line
236
237 vec2 CloserToFromButTooFarFromLine = vec2(11, 11 + Radius + pChrLeft->GetProximityRadius());
238 pChrLeft->SetPosition(CloserToFromButTooFarFromLine);
239 pChrLeft->m_Pos = CloserToFromButTooFarFromLine;
240
241 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
242 Pos0: vec2(10, 10), // intersect from
243 Pos1: vec2(20, 10), // intersect to
244 Radius,
245 Type: CGameWorld::ENTTYPE_CHARACTER,
246 NewPos&: IntersectAt,
247 pNotThis: nullptr, // pNotThis
248 CollideWith: -1, // CollideWith
249 pThisOnly: nullptr /* pThisOnly */);
250 EXPECT_EQ(pIntersectedChar, pChrRight);
251}
252
253TEST_F(CTestGameWorld, BasicTick)
254{
255 int ClientId = 0;
256 bool Afk = true;
257 int LastWhisperTo = -1;
258 const int StartTeam = GameServer()->m_pController->GetAutoTeam(NotThisId: ClientId);
259 GameServer()->CreatePlayer(ClientId, StartTeam, Afk, LastWhisperTo);
260
261 GameServer()->OnTick();
262}
263
264TEST_F(CTestGameWorld, CharacterEmote)
265{
266 int ClientId = 0;
267 bool Afk = true;
268 int LastWhisperTo = -1;
269 GameServer()->CreatePlayer(ClientId, StartTeam: TEAM_GAME, Afk, LastWhisperTo);
270 CPlayer *pPlayer = GameServer()->m_apPlayers[ClientId];
271 pPlayer->ForceSpawn(Pos: vec2(0, 0));
272 CCharacter *pChr = pPlayer->GetCharacter();
273 ASSERT_NE(pChr, nullptr);
274
275 // afk
276 pPlayer->SetAfk(true);
277 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_BLINK);
278
279 // not afk
280 pPlayer->SetAfk(false);
281 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_NORMAL);
282
283 // frozen
284 pChr->Freeze(Seconds: 10);
285 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_BLINK);
286
287 // frozen and paused
288 pPlayer->Pause(State: CPlayer::PAUSE_PAUSED, Force: true);
289 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_NORMAL);
290
291 // ninja jetpack
292 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: true);
293 pChr->Unfreeze();
294 pPlayer->m_NinjaJetpack = true;
295 pChr->m_NinjaJetpack = true;
296 pChr->SetJetpack(true);
297 pChr->SetActiveWeapon(WEAPON_GUN);
298 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_HAPPY);
299
300 // /emote angry 3 chat command
301 pChr->SetEmote(Emote: EMOTE_ANGRY, Tick: GameServer()->Server()->Tick() + GameServer()->Server()->TickSpeed() * 3);
302 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_ANGRY);
303
304 // /emote angry 3 chat command and frozen
305 pChr->Freeze(Seconds: 10);
306 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_ANGRY);
307}
308