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 IEngineMap *pEngineMap = CreateEngineMap();
79 m_pKernel->RegisterInterface(pInterface: pEngineMap);
80 m_pKernel->RegisterInterface(pInterface: static_cast<IMap *>(pEngineMap), Destroy: false);
81
82 IEngineAntibot *pEngineAntibot = CreateEngineAntibot();
83 m_pKernel->RegisterInterface(pInterface: pEngineAntibot);
84 m_pKernel->RegisterInterface(pInterface: static_cast<IAntibot *>(pEngineAntibot), Destroy: false);
85
86 m_pGameServer = CreateGameServer();
87 m_pKernel->RegisterInterface(pInterface: m_pGameServer);
88
89 pEngine->Init();
90 pConsole->Init();
91 pConfigManager->Init();
92
93 m_pServer->RegisterCommands();
94
95 EXPECT_NE(m_pServer->LoadMap("coverage"), 0);
96
97 m_pServer->m_RunServer = CServer::RUNNING;
98
99 m_pServer->m_AuthManager.Init();
100
101 {
102 int Size = GameServer()->PersistentClientDataSize();
103 for(auto &Client : m_pServer->m_aClients)
104 {
105 Client.m_HasPersistentData = false;
106 Client.m_pPersistentData = malloc(size: Size);
107 }
108 }
109 m_pServer->m_pPersistentData = malloc(size: GameServer()->PersistentDataSize());
110 EXPECT_NE(m_pServer->LoadMap("coverage"), 0);
111
112 if(!pServer->m_Http.Init(ShutdownDelay: std::chrono::seconds{2}))
113 {
114 log_error("server", "Failed to initialize the HTTP client.");
115 }
116
117 pServer->m_NetServer.SetCallbacks(
118 pfnNewClient: CServer::NewClientCallback,
119 pfnNewClientNoAuth: CServer::NewClientNoAuthCallback,
120 pfnClientRejoin: CServer::ClientRejoinCallback,
121 pfnDelClient: CServer::DelClientCallback, pUser: pServer);
122
123 pServer->m_Econ.Init(pConfig: pServer->Config(), pConsole: pServer->Console(), pNetBan: &pServer->m_ServerBan);
124
125 pServer->m_Fifo.Init(pConsole: pServer->Console(), pFifoFile: pServer->Config()->m_SvInputFifo, Flag: CFGFLAG_SERVER);
126 m_pServer->Antibot()->Init();
127 GameServer()->OnInit(pPersistentData: nullptr);
128 pServer->ReadAnnouncementsFile();
129 pServer->InitMaplist();
130 }
131
132 ~CTestGameWorld() override
133 {
134 m_pServer->m_Econ.Shutdown();
135 m_pServer->m_Fifo.Shutdown();
136 m_pGameServer->OnShutdown(pPersistentData: nullptr);
137 m_pServer->m_pMap->Unload();
138 m_pServer->DbPool()->OnShutdown();
139 }
140};
141
142TEST_F(CTestGameWorld, ClosestCharacter)
143{
144 CNetObj_PlayerInput Input = {};
145 CCharacter *pChr1 = new(0) CCharacter(&GameServer()->m_World, Input);
146 pChr1->m_Pos = vec2(0, 0);
147 GameServer()->m_World.InsertEntity(pEntity: pChr1);
148
149 CCharacter *pChr2 = new(1) CCharacter(&GameServer()->m_World, Input);
150 pChr2->m_Pos = vec2(10, 10);
151 GameServer()->m_World.InsertEntity(pEntity: pChr2);
152
153 CCharacter *pClosest = GameServer()->m_World.ClosestCharacter(Pos: vec2(1, 1), Radius: 20, pNotThis: nullptr);
154 EXPECT_EQ(pClosest, pChr1);
155}
156
157TEST_F(CTestGameWorld, IntersectEntity)
158{
159 CNetObj_PlayerInput Input = {};
160 CCharacter *pChrLeft = new(0) CCharacter(&GameServer()->m_World, Input);
161 pChrLeft->m_Pos = vec2(15, 10);
162 GameServer()->m_World.InsertEntity(pEntity: pChrLeft);
163
164 CCharacter *pChrRight = new(1) CCharacter(&GameServer()->m_World, Input);
165 pChrRight->m_Pos = vec2(16, 10);
166 GameServer()->m_World.InsertEntity(pEntity: pChrRight);
167
168 float Radius = 5.0f;
169 vec2 IntersectAt;
170 CCharacter *pIntersectedChar;
171
172 // both tees are exactly on the line
173 // if we go intersect left to right we find the left one
174
175 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
176 Pos0: vec2(10, 10), // intersect from
177 Pos1: vec2(20, 10), // intersect to
178 Radius,
179 Type: CGameWorld::ENTTYPE_CHARACTER,
180 NewPos&: IntersectAt,
181 pNotThis: nullptr, // pNotThis
182 CollideWith: -1, // CollideWith
183 pThisOnly: nullptr /* pThisOnly */);
184 EXPECT_EQ(pIntersectedChar, pChrLeft);
185
186 // if we intersect right to left we find the right one
187
188 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
189 Pos0: vec2(20, 10), // intersect from
190 Pos1: vec2(10, 10), // intersect to
191 Radius,
192 Type: CGameWorld::ENTTYPE_CHARACTER,
193 NewPos&: IntersectAt,
194 pNotThis: nullptr, // pNotThis
195 CollideWith: -1, // CollideWith
196 pThisOnly: nullptr /* pThisOnly */);
197 EXPECT_EQ(pIntersectedChar, pChrRight);
198
199 // but not if we ignore the right one
200
201 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
202 Pos0: vec2(20, 10), // intersect from
203 Pos1: vec2(10, 10), // intersect to
204 Radius,
205 Type: CGameWorld::ENTTYPE_CHARACTER,
206 NewPos&: IntersectAt,
207 pNotThis: pChrRight, // pNotThis
208 CollideWith: -1, // CollideWith
209 pThisOnly: nullptr /* pThisOnly */);
210 EXPECT_EQ(pIntersectedChar, pChrLeft);
211
212 // or we force find the left one
213
214 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
215 Pos0: vec2(20, 10), // intersect from
216 Pos1: vec2(10, 10), // intersect to
217 Radius,
218 Type: CGameWorld::ENTTYPE_CHARACTER,
219 NewPos&: IntersectAt,
220 pNotThis: nullptr, // pNotThis
221 CollideWith: -1, // CollideWith
222 pThisOnly: pChrLeft /* pThisOnly */);
223 EXPECT_EQ(pIntersectedChar, pChrLeft);
224
225 // pNotThis == pThisOnly => nullptr
226
227 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
228 Pos0: vec2(20, 10), // intersect from
229 Pos1: vec2(10, 10), // intersect to
230 Radius,
231 Type: CGameWorld::ENTTYPE_CHARACTER,
232 NewPos&: IntersectAt,
233 pNotThis: pChrLeft, // pNotThis
234 CollideWith: -1, // CollideWith
235 pThisOnly: pChrLeft /* pThisOnly */);
236 EXPECT_EQ(pIntersectedChar, nullptr);
237
238 // the tee closer to the start of the intersection line
239 // will not be matched if it is further than Radius away
240 // from the line
241
242 vec2 CloserToFromButTooFarFromLine = vec2(11, 11 + Radius + pChrLeft->GetProximityRadius());
243 pChrLeft->SetPosition(CloserToFromButTooFarFromLine);
244 pChrLeft->m_Pos = CloserToFromButTooFarFromLine;
245
246 pIntersectedChar = (CCharacter *)GameServer()->m_World.IntersectEntity(
247 Pos0: vec2(10, 10), // intersect from
248 Pos1: vec2(20, 10), // intersect to
249 Radius,
250 Type: CGameWorld::ENTTYPE_CHARACTER,
251 NewPos&: IntersectAt,
252 pNotThis: nullptr, // pNotThis
253 CollideWith: -1, // CollideWith
254 pThisOnly: nullptr /* pThisOnly */);
255 EXPECT_EQ(pIntersectedChar, pChrRight);
256}
257
258TEST_F(CTestGameWorld, BasicTick)
259{
260 int ClientId = 0;
261 bool Afk = true;
262 int LastWhisperTo = -1;
263 const int StartTeam = GameServer()->m_pController->GetAutoTeam(NotThisId: ClientId);
264 GameServer()->CreatePlayer(ClientId, StartTeam, Afk, LastWhisperTo);
265
266 GameServer()->OnTick();
267}
268
269TEST_F(CTestGameWorld, CharacterEmote)
270{
271 int ClientId = 0;
272 bool Afk = true;
273 int LastWhisperTo = -1;
274 GameServer()->CreatePlayer(ClientId, StartTeam: TEAM_GAME, Afk, LastWhisperTo);
275 CPlayer *pPlayer = GameServer()->m_apPlayers[ClientId];
276 pPlayer->ForceSpawn(Pos: vec2(0, 0));
277 CCharacter *pChr = pPlayer->GetCharacter();
278 ASSERT_NE(pChr, nullptr);
279
280 // afk
281 pPlayer->SetAfk(true);
282 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_BLINK);
283
284 // not afk
285 pPlayer->SetAfk(false);
286 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_NORMAL);
287
288 // frozen
289 pChr->Freeze(Seconds: 10);
290 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_BLINK);
291
292 // frozen and paused
293 pPlayer->Pause(State: CPlayer::PAUSE_PAUSED, Force: true);
294 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_NORMAL);
295
296 // ninja jetpack
297 pPlayer->Pause(State: CPlayer::PAUSE_NONE, Force: true);
298 pChr->UnFreeze();
299 pPlayer->m_NinjaJetpack = true;
300 pChr->m_NinjaJetpack = true;
301 pChr->SetJetpack(true);
302 pChr->SetActiveWeapon(WEAPON_GUN);
303 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_HAPPY);
304
305 // /emote angry 3 chat command
306 pChr->SetEmote(Emote: EMOTE_ANGRY, Tick: GameServer()->Server()->Tick() + GameServer()->Server()->TickSpeed() * 3);
307 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_ANGRY);
308
309 // /emote angry 3 chat command and frozen
310 pChr->Freeze(Seconds: 10);
311 ASSERT_EQ(pChr->DetermineEyeEmote(), EMOTE_ANGRY);
312}
313