ddnet/src/game/server/gameworld.cpp

383 lines
9.4 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2010-05-29 07:25:38 +00:00
#include "gameworld.h"
#include "entity.h"
#include "gamecontext.h"
2013-12-31 05:13:57 +00:00
#include <algorithm>
#include <utility>
#include <engine/shared/config.h>
//////////////////////////////////////////////////
// game world
//////////////////////////////////////////////////
2010-05-29 07:25:38 +00:00
CGameWorld::CGameWorld()
{
2010-05-29 07:25:38 +00:00
m_pGameServer = 0x0;
m_pServer = 0x0;
2010-05-29 07:25:38 +00:00
m_Paused = false;
m_ResetRequested = false;
for(int i = 0; i < NUM_ENTTYPES; i++)
2010-05-29 07:25:38 +00:00
m_apFirstEntityTypes[i] = 0;
}
2010-05-29 07:25:38 +00:00
CGameWorld::~CGameWorld()
{
// delete all entities
for(int i = 0; i < NUM_ENTTYPES; i++)
while(m_apFirstEntityTypes[i])
delete m_apFirstEntityTypes[i];
2010-05-29 07:25:38 +00:00
}
void CGameWorld::SetGameServer(CGameContext *pGameServer)
{
m_pGameServer = pGameServer;
m_pServer = m_pGameServer->Server();
}
2010-05-29 07:25:38 +00:00
CEntity *CGameWorld::FindFirst(int Type)
{
return Type < 0 || Type >= NUM_ENTTYPES ? 0 : m_apFirstEntityTypes[Type];
}
2010-05-29 07:25:38 +00:00
int CGameWorld::FindEntities(vec2 Pos, float Radius, CEntity **ppEnts, int Max, int Type)
{
if(Type < 0 || Type >= NUM_ENTTYPES)
return 0;
2010-05-29 07:25:38 +00:00
int Num = 0;
for(CEntity *pEnt = m_apFirstEntityTypes[Type]; pEnt; pEnt = pEnt->m_pNextTypeEntity)
{
2010-05-29 07:25:38 +00:00
if(distance(pEnt->m_Pos, Pos) < Radius+pEnt->m_ProximityRadius)
{
if(ppEnts)
ppEnts[Num] = pEnt;
2010-05-29 07:25:38 +00:00
Num++;
if(Num == Max)
break;
}
}
2010-05-29 07:25:38 +00:00
return Num;
}
2010-05-29 07:25:38 +00:00
void CGameWorld::InsertEntity(CEntity *pEnt)
{
#ifdef CONF_DEBUG
2011-01-20 00:35:21 +00:00
for(CEntity *pCur = m_apFirstEntityTypes[pEnt->m_ObjType]; pCur; pCur = pCur->m_pNextTypeEntity)
2010-05-29 07:25:38 +00:00
dbg_assert(pCur != pEnt, "err");
#endif
// insert it
if(m_apFirstEntityTypes[pEnt->m_ObjType])
m_apFirstEntityTypes[pEnt->m_ObjType]->m_pPrevTypeEntity = pEnt;
pEnt->m_pNextTypeEntity = m_apFirstEntityTypes[pEnt->m_ObjType];
2010-05-29 07:25:38 +00:00
pEnt->m_pPrevTypeEntity = 0x0;
m_apFirstEntityTypes[pEnt->m_ObjType] = pEnt;
}
2010-05-29 07:25:38 +00:00
void CGameWorld::DestroyEntity(CEntity *pEnt)
{
2010-05-29 07:25:38 +00:00
pEnt->m_MarkedForDestroy = true;
}
2010-05-29 07:25:38 +00:00
void CGameWorld::RemoveEntity(CEntity *pEnt)
{
// not in the list
if(!pEnt->m_pNextTypeEntity && !pEnt->m_pPrevTypeEntity && m_apFirstEntityTypes[pEnt->m_ObjType] != pEnt)
return;
// remove
2010-05-29 07:25:38 +00:00
if(pEnt->m_pPrevTypeEntity)
pEnt->m_pPrevTypeEntity->m_pNextTypeEntity = pEnt->m_pNextTypeEntity;
else
m_apFirstEntityTypes[pEnt->m_ObjType] = pEnt->m_pNextTypeEntity;
2010-05-29 07:25:38 +00:00
if(pEnt->m_pNextTypeEntity)
pEnt->m_pNextTypeEntity->m_pPrevTypeEntity = pEnt->m_pPrevTypeEntity;
// keep list traversing valid
if(m_pNextTraverseEntity == pEnt)
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
2010-05-29 07:25:38 +00:00
pEnt->m_pNextTypeEntity = 0;
pEnt->m_pPrevTypeEntity = 0;
}
//
2010-05-29 07:25:38 +00:00
void CGameWorld::Snap(int SnappingClient)
{
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
pEnt->Snap(SnappingClient);
pEnt = m_pNextTraverseEntity;
}
}
2010-05-29 07:25:38 +00:00
void CGameWorld::Reset()
{
// reset all entities
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
pEnt->Reset();
pEnt = m_pNextTraverseEntity;
}
2010-05-29 07:25:38 +00:00
RemoveEntities();
2010-05-29 07:25:38 +00:00
GameServer()->m_pController->PostReset();
RemoveEntities();
2010-05-29 07:25:38 +00:00
m_ResetRequested = false;
}
2010-05-29 07:25:38 +00:00
void CGameWorld::RemoveEntities()
{
// destroy objects marked for destruction
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
if(pEnt->m_MarkedForDestroy)
{
RemoveEntity(pEnt);
pEnt->Destroy();
}
pEnt = m_pNextTraverseEntity;
}
}
2013-12-31 05:13:57 +00:00
bool distCompare(std::pair<float,int> a, std::pair<float,int> b)
{
return (a.first < b.first);
}
void CGameWorld::UpdatePlayerMaps()
{
if (Server()->Tick() % g_Config.m_SvMapUpdateRate != 0) return;
std::pair<float,int> dist[MAX_CLIENTS];
for (int i = 0; i < MAX_CLIENTS; i++)
{
2014-01-02 01:23:09 +00:00
if (!Server()->ClientIngame(i)) continue;
2013-12-31 05:13:57 +00:00
int* map = Server()->GetIdMap(i);
// compute distances
for (int j = 0; j < MAX_CLIENTS; j++)
{
dist[j].second = j;
if (!Server()->ClientIngame(j))
2014-01-02 01:47:06 +00:00
{
dist[j].first = 1e10;
2013-12-31 05:13:57 +00:00
continue;
2014-01-02 01:47:06 +00:00
}
2013-12-31 05:13:57 +00:00
CCharacter* ch = GameServer()->m_apPlayers[j]->GetCharacter();
if (!ch)
2014-01-02 01:47:06 +00:00
{
dist[j].first = 1e9;
2013-12-31 05:13:57 +00:00
continue;
2014-01-02 01:47:06 +00:00
}
2013-12-31 05:13:57 +00:00
// copypasted chunk from character.cpp Snap() follows
int SnappingClient = i;
CCharacter* SnapChar = GameServer()->GetPlayerChar(SnappingClient);
if(SnapChar && !SnapChar->m_Super &&
GameServer()->m_apPlayers[SnappingClient]->GetTeam() != -1 &&
!ch->CanCollide(SnappingClient) &&
2014-01-30 15:54:58 +00:00
(!GameServer()->m_apPlayers[SnappingClient] ||
GameServer()->m_apPlayers[SnappingClient]->m_ClientVersion == VERSION_VANILLA ||
(GameServer()->m_apPlayers[SnappingClient]->m_ClientVersion >= VERSION_DDRACE &&
2013-12-31 05:13:57 +00:00
!GameServer()->m_apPlayers[SnappingClient]->m_ShowOthers
2014-01-02 01:47:06 +00:00
)
2013-12-31 05:13:57 +00:00
)
2014-01-02 01:47:06 +00:00
)
dist[j].first = 1e8;
2014-01-11 22:38:13 +00:00
else
dist[j].first = 0;
2013-12-31 05:13:57 +00:00
2014-01-11 22:38:13 +00:00
dist[j].first += distance(GameServer()->m_apPlayers[i]->m_ViewPos, GameServer()->m_apPlayers[j]->GetCharacter()->m_Pos);
2013-12-31 05:13:57 +00:00
}
// always send the player himself
dist[i].first = 0;
// compute reverse map
int rMap[MAX_CLIENTS];
for (int j = 0; j < MAX_CLIENTS; j++)
{
rMap[j] = -1;
}
for (int j = 0; j < VANILLA_MAX_CLIENTS; j++)
{
if (map[j] == -1) continue;
if (dist[map[j]].first > 5e9) map[j] = -1;
2013-12-31 05:13:57 +00:00
else rMap[map[j]] = j;
}
std::nth_element(&dist[0], &dist[VANILLA_MAX_CLIENTS - 1], &dist[MAX_CLIENTS], distCompare);
int mapc = 0;
int demand = 0;
for (int j = 0; j < VANILLA_MAX_CLIENTS - 1; j++)
{
int k = dist[j].second;
if (rMap[k] != -1 || dist[j].first > 5e9) continue;
2013-12-31 05:13:57 +00:00
while (mapc < VANILLA_MAX_CLIENTS && map[mapc] != -1) mapc++;
if (mapc < VANILLA_MAX_CLIENTS - 1)
map[mapc] = k;
2014-01-02 01:24:49 +00:00
else
2014-01-11 22:38:13 +00:00
demand++;
2013-12-31 05:13:57 +00:00
}
for (int j = MAX_CLIENTS - 1; j > VANILLA_MAX_CLIENTS - 2; j--)
{
int k = dist[j].second;
if (rMap[k] != -1 && demand-- > 0)
map[rMap[k]] = -1;
}
map[VANILLA_MAX_CLIENTS - 1] = -1; // player with empty name to say chat msgs
}
}
2010-05-29 07:25:38 +00:00
void CGameWorld::Tick()
{
2010-05-29 07:25:38 +00:00
if(m_ResetRequested)
Reset();
2010-05-29 07:25:38 +00:00
if(!m_Paused)
{
2010-05-29 07:25:38 +00:00
if(GameServer()->m_pController->IsForceBalanced())
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, "Teams have been balanced");
// update all objects
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
pEnt->Tick();
pEnt = m_pNextTraverseEntity;
}
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
pEnt->TickDefered();
pEnt = m_pNextTraverseEntity;
}
}
2012-01-09 23:49:31 +00:00
else
{
// update all objects
for(int i = 0; i < NUM_ENTTYPES; i++)
for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
{
m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
pEnt->TickPaused();
pEnt = m_pNextTraverseEntity;
}
}
2010-05-29 07:25:38 +00:00
RemoveEntities();
2013-12-31 05:13:57 +00:00
UpdatePlayerMaps();
}
// TODO: should be more general
//CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2& NewPos, CEntity *pNotThis)
CCharacter *CGameWorld::IntersectCharacter(vec2 Pos0, vec2 Pos1, float Radius, vec2& NewPos, CCharacter *pNotThis, int CollideWith, class CCharacter *pThisOnly)
{
// Find other players
2010-05-29 07:25:38 +00:00
float ClosestLen = distance(Pos0, Pos1) * 100.0f;
CCharacter *pClosest = 0;
CCharacter *p = (CCharacter *)FindFirst(ENTTYPE_CHARACTER);
2010-05-29 07:25:38 +00:00
for(; p; p = (CCharacter *)p->TypeNext())
{
2010-05-29 07:25:38 +00:00
if(p == pNotThis)
continue;
if(CollideWith != -1 && !p->CanCollide(CollideWith))
continue;
2010-05-29 07:25:38 +00:00
vec2 IntersectPos = closest_point_on_line(Pos0, Pos1, p->m_Pos);
float Len = distance(p->m_Pos, IntersectPos);
if(Len < p->m_ProximityRadius+Radius)
{
Len = distance(Pos0, IntersectPos);
2010-05-29 07:25:38 +00:00
if(Len < ClosestLen)
{
2010-05-29 07:25:38 +00:00
NewPos = IntersectPos;
ClosestLen = Len;
pClosest = p;
}
}
}
2010-05-29 07:25:38 +00:00
return pClosest;
}
2011-01-06 03:46:10 +00:00
2010-05-29 07:25:38 +00:00
CCharacter *CGameWorld::ClosestCharacter(vec2 Pos, float Radius, CEntity *pNotThis)
{
// Find other players
2010-05-29 07:25:38 +00:00
float ClosestRange = Radius*2;
CCharacter *pClosest = 0;
CCharacter *p = (CCharacter *)GameServer()->m_World.FindFirst(ENTTYPE_CHARACTER);
2010-05-29 07:25:38 +00:00
for(; p; p = (CCharacter *)p->TypeNext())
{
2010-05-29 07:25:38 +00:00
if(p == pNotThis)
continue;
2010-05-29 07:25:38 +00:00
float Len = distance(Pos, p->m_Pos);
if(Len < p->m_ProximityRadius+Radius)
{
2010-05-29 07:25:38 +00:00
if(Len < ClosestRange)
{
2010-05-29 07:25:38 +00:00
ClosestRange = Len;
pClosest = p;
}
}
}
2010-05-29 07:25:38 +00:00
return pClosest;
}
std::list<class CCharacter *> CGameWorld::IntersectedCharacters(vec2 Pos0, vec2 Pos1, float Radius, class CEntity *pNotThis)
{
std::list< CCharacter * > listOfChars;
2011-01-29 00:59:50 +00:00
CCharacter *pChr = (CCharacter *)FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; pChr; pChr = (CCharacter *)pChr->TypeNext())
{
2011-01-29 00:59:50 +00:00
if(pChr == pNotThis)
continue;
2011-01-29 00:59:50 +00:00
vec2 IntersectPos = closest_point_on_line(Pos0, Pos1, pChr->m_Pos);
float Len = distance(pChr->m_Pos, IntersectPos);
if(Len < pChr->m_ProximityRadius+Radius)
{
2011-01-29 00:59:50 +00:00
pChr->m_Intersection = IntersectPos;
listOfChars.push_back(pChr);
}
}
return listOfChars;
}
2010-09-11 09:42:35 +00:00
void CGameWorld::ReleaseHooked(int ClientID)
{
2011-01-29 00:59:50 +00:00
CCharacter *pChr = (CCharacter *)CGameWorld::FindFirst(CGameWorld::ENTTYPE_CHARACTER);
for(; pChr; pChr = (CCharacter *)pChr->TypeNext())
{
CCharacterCore* Core = pChr->Core();
if(Core->m_HookedPlayer == ClientID && !pChr->m_Super)
{
2011-01-29 00:59:50 +00:00
Core->m_HookedPlayer = -1;
Core->m_HookState = HOOK_RETRACTED;
Core->m_TriggeredEvents |= COREEVENT_HOOK_RETRACT;
Core->m_HookState = HOOK_RETRACTED;
}
2011-01-29 00:59:50 +00:00
}
}