forked from Mirrors/openclonk
413 lines
9.8 KiB
C++
413 lines
9.8 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
|
* Copyright (c) 2009-2016, The OpenClonk Team and contributors
|
|
*
|
|
* Distributed under the terms of the ISC license; see accompanying file
|
|
* "COPYING" for details.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
|
|
* See accompanying file "TRADEMARK" for details.
|
|
*
|
|
* To redistribute this file separately, substitute the full license texts
|
|
* for the above references.
|
|
*/
|
|
#include "C4Include.h"
|
|
#include "network/C4Client.h"
|
|
|
|
#include "control/C4GameControl.h"
|
|
#include "game/C4Application.h"
|
|
#include "network/C4Network2Client.h"
|
|
#include "player/C4PlayerList.h"
|
|
|
|
#ifndef HAVE_WINSOCK
|
|
#include <netdb.h>
|
|
#include <sys/socket.h> /* for AF_INET */
|
|
#endif
|
|
|
|
// *** C4ClientCore
|
|
|
|
C4ClientCore::C4ClientCore()
|
|
{
|
|
Name.Ref(""); CUID.Ref(""); Nick.Ref("");
|
|
}
|
|
|
|
C4ClientCore::~C4ClientCore() = default;
|
|
|
|
void C4ClientCore::SetLocal(int32_t inID, bool fnActivated, bool fnObserver)
|
|
{
|
|
// status
|
|
iID = inID;
|
|
fActivated = fnActivated;
|
|
fObserver = fnObserver;
|
|
// misc
|
|
CUID.CopyValidated(Config.GetRegistrationData("Cuid"));
|
|
Revision.CopyValidated(Application.GetRevision());
|
|
if (Config.Network.Nick.getLength())
|
|
{
|
|
Name.CopyValidated(Config.Network.Nick);
|
|
Nick.Copy(Name);
|
|
}
|
|
else
|
|
{
|
|
const char* position = std::strchr(Config.General.Participants, ';');
|
|
if (!position)
|
|
position = Config.General.Participants + std::strlen(Config.General.Participants);
|
|
if (Config.General.Participants + 4 <= position && std::strncmp(position - 4, ".ocp", 4) == 0)
|
|
Name.CopyValidated(std::string(Config.General.Participants, position - Config.General.Participants - 4).c_str());
|
|
else
|
|
Name.Copy("empty");
|
|
Nick.Copy(Name);
|
|
}
|
|
}
|
|
|
|
void C4ClientCore::SetUnknown(int32_t inID)
|
|
{
|
|
// save id
|
|
iID = inID;
|
|
// fill everything else with default values
|
|
Name.Ref("unknown"); CUID.Ref("unknown"); Nick.Ref("unknown");
|
|
Revision.Ref("unknown");
|
|
fActivated = fObserver = false;
|
|
}
|
|
|
|
int32_t C4ClientCore::getDiffLevel(const C4ClientCore &CCore2) const
|
|
{
|
|
// nick & cuid won't ever be changed
|
|
if (CUID != CCore2.getCUID() || Nick != CCore2.getNick())
|
|
return C4ClientCoreDL_Different;
|
|
// identification changed?
|
|
if (iID != CCore2.getID() || Name != CCore2.getName())
|
|
return C4ClientCoreDL_IDChange;
|
|
// status change?
|
|
if (fActivated != CCore2.isActivated() || fObserver != CCore2.isObserver() || fLobbyReady != CCore2.isLobbyReady())
|
|
return C4ClientCoreDL_IDMatch;
|
|
// otherwise: identical
|
|
return C4ClientCoreDL_None;
|
|
}
|
|
|
|
// C4PacketBase virtuals
|
|
|
|
void C4ClientCore::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
pComp->Value(mkNamingAdapt(iID, "ID", C4ClientIDUnknown));
|
|
pComp->Value(mkNamingAdapt(fActivated, "Activated", false));
|
|
pComp->Value(mkNamingAdapt(fObserver, "Observer", false));
|
|
pComp->Value(mkNamingAdapt(fLobbyReady, "Ready", false));
|
|
pComp->Value(mkNamingAdapt(Name, "Name", ""));
|
|
pComp->Value(mkNamingAdapt(CUID, "CUID", ""));
|
|
pComp->Value(mkNamingAdapt(Nick, "Nick", ""));
|
|
pComp->Value(mkNamingAdapt(Revision, "Revision", ""));
|
|
}
|
|
|
|
// *** C4Client
|
|
|
|
C4Client::C4Client() = default;
|
|
|
|
C4Client::C4Client(const C4ClientCore &Core)
|
|
: Core(Core), pNext(nullptr)
|
|
{
|
|
|
|
}
|
|
|
|
C4Client::~C4Client()
|
|
{
|
|
// network client bind must be removed before
|
|
assert(!pNetClient);
|
|
}
|
|
|
|
void C4Client::SetActivated(bool fnActivated)
|
|
{
|
|
Core.SetActivated(fnActivated);
|
|
// activity
|
|
if (fnActivated && pNetClient)
|
|
pNetClient->SetLastActivity(Game.FrameCounter);
|
|
}
|
|
|
|
void C4Client::SetLobbyReady(bool fnLobbyReady, time_t *time_since_last_change)
|
|
{
|
|
// Change state
|
|
Core.SetLobbyReady(fnLobbyReady);
|
|
// Keep track of times
|
|
if (time_since_last_change)
|
|
{
|
|
time_t now = time(nullptr);
|
|
*time_since_last_change = now - last_lobby_ready_change;
|
|
last_lobby_ready_change = now;
|
|
}
|
|
}
|
|
|
|
void C4Client::SetLocal()
|
|
{
|
|
// set flag
|
|
fLocal = true;
|
|
}
|
|
|
|
void C4Client::Remove()
|
|
{
|
|
// remove players for this client
|
|
::Players.RemoveAtClient(getID(), true);
|
|
}
|
|
|
|
void C4Client::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
pComp->Value(Core);
|
|
// Assume this is a non-local client
|
|
if (pComp->isDeserializer())
|
|
fLocal = false;
|
|
}
|
|
|
|
// *** C4ClientList
|
|
|
|
C4ClientList::C4ClientList() = default;
|
|
|
|
C4ClientList::~C4ClientList()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void C4ClientList::Clear()
|
|
{
|
|
ClearNetwork();
|
|
while (pFirst)
|
|
{
|
|
C4Client *pClient = pFirst;
|
|
Remove(pClient, true);
|
|
delete pClient;
|
|
}
|
|
}
|
|
|
|
void C4ClientList::Add(C4Client *pClient)
|
|
{
|
|
// local client?
|
|
if (pClient->isLocal()) pLocal = pClient;
|
|
// already in list?
|
|
assert(!getClientByID(pClient->getID()));
|
|
// find insert position
|
|
C4Client *pPos = pFirst, *pPrev = nullptr;
|
|
for (; pPos; pPrev = pPos, pPos = pPos->pNext)
|
|
if (pPos->getID() > pClient->getID())
|
|
break;
|
|
// add to list
|
|
pClient->pNext = pPos;
|
|
(pPrev ? pPrev->pNext : pFirst) = pClient;
|
|
// register to network
|
|
if (pNetClients)
|
|
pClient->pNetClient = pNetClients->RegClient(pClient);
|
|
}
|
|
|
|
C4Client *C4ClientList::getClientByID(int32_t iID) const
|
|
{
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
if (pClient->getID() == iID)
|
|
return pClient;
|
|
return nullptr;
|
|
}
|
|
|
|
C4Client *C4ClientList::getNextClientByID(int32_t iAfterID) const
|
|
{
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
if (pClient->getID() > iAfterID)
|
|
return pClient;
|
|
return nullptr;
|
|
}
|
|
|
|
C4Client *C4ClientList::getClientByName(const char *szName) const
|
|
{
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
if (SEqual(pClient->getName(), szName))
|
|
return pClient;
|
|
return nullptr;
|
|
}
|
|
|
|
int32_t C4ClientList::getClientCnt() const
|
|
{
|
|
int32_t iCnt = 0;
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
iCnt++;
|
|
return iCnt;
|
|
}
|
|
|
|
StdStrBuf C4ClientList::GetAllClientNames() const
|
|
{
|
|
StdStrBuf result;
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
{
|
|
if (result.getSize()) result.Append(", ");
|
|
result.Append(pClient->getName());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool C4ClientList::Init(int32_t iLocalClientID)
|
|
{
|
|
Clear();
|
|
// Add local client (activated, not observing)
|
|
AddLocal(iLocalClientID, true, false);
|
|
return true;
|
|
}
|
|
|
|
void C4ClientList::InitNetwork(C4Network2ClientList *pnNetClients)
|
|
{
|
|
// clear list before
|
|
pnNetClients->Clear();
|
|
// set
|
|
pNetClients = pnNetClients;
|
|
// copy client list, create links
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
pClient->pNetClient = pNetClients->RegClient(pClient);
|
|
}
|
|
|
|
void C4ClientList::ClearNetwork()
|
|
{
|
|
// clear the list (should remove links)
|
|
C4Network2ClientList *pOldNetClients = pNetClients;
|
|
pNetClients = nullptr;
|
|
if (pOldNetClients) pOldNetClients->Clear();
|
|
}
|
|
|
|
bool C4ClientList::Remove(C4Client *pClient, bool fTemporary)
|
|
{
|
|
// first client in list?
|
|
if (pClient == pFirst)
|
|
pFirst = pClient->pNext;
|
|
else
|
|
{
|
|
// find previous
|
|
C4Client *pPrev;
|
|
for (pPrev = pFirst; pPrev && pPrev->pNext != pClient; pPrev = pPrev->pNext) {}
|
|
// not found?
|
|
if (!pPrev) return false;
|
|
// unlink
|
|
pPrev->pNext = pClient->pNext;
|
|
}
|
|
if (pClient == pLocal) pLocal = nullptr;
|
|
if (!fTemporary)
|
|
{
|
|
pClient->Remove();
|
|
// remove network client
|
|
if (pNetClients && pClient->getNetClient())
|
|
pNetClients->DeleteClient(pClient->getNetClient());
|
|
}
|
|
// done
|
|
return true;
|
|
}
|
|
|
|
C4Client *C4ClientList::Add(const C4ClientCore &Core)
|
|
{
|
|
// client with same ID in list?
|
|
if (getClientByID(Core.getID()))
|
|
{ Log("ClientList: Duplicated client ID!"); return nullptr; }
|
|
// create, add
|
|
C4Client *pClient = new C4Client(Core);
|
|
Add(pClient);
|
|
return pClient;
|
|
}
|
|
|
|
C4Client *C4ClientList::AddLocal(int32_t iID, bool fActivated, bool fObserver)
|
|
{
|
|
// only one local client allowed
|
|
assert(!pLocal);
|
|
// create core
|
|
C4ClientCore LocalCore;
|
|
LocalCore.SetLocal(iID, fActivated, fObserver);
|
|
// create client
|
|
C4Client *pClient = new C4Client(LocalCore);
|
|
pClient->SetLocal();
|
|
// add
|
|
Add(pClient);
|
|
assert(pLocal);
|
|
return pClient;
|
|
}
|
|
|
|
void C4ClientList::SetLocalID(int32_t iID)
|
|
{
|
|
if (!pLocal) return;
|
|
pLocal->SetID(iID);
|
|
// resort local client
|
|
C4Client *pSavedLocal = pLocal;
|
|
Remove(pLocal, true); Add(pSavedLocal);
|
|
}
|
|
|
|
void C4ClientList::CtrlRemove(const C4Client *pClient, const char *szReason)
|
|
{
|
|
// host only
|
|
if (!pLocal || !pLocal->isHost() || !pClient) return;
|
|
// Net client? flag
|
|
if (pClient->getNetClient())
|
|
pClient->getNetClient()->SetStatus(NCS_Remove);
|
|
// add control
|
|
::Control.DoInput(CID_ClientRemove,
|
|
new C4ControlClientRemove(pClient->getID(), szReason),
|
|
CDT_Sync);
|
|
}
|
|
|
|
void C4ClientList::RemoveRemote()
|
|
{
|
|
// remove all remote clients
|
|
for (C4Client *pClient = pFirst, *pNext; pClient; pClient = pNext)
|
|
{
|
|
pNext = pClient->pNext;
|
|
// remove all clients except the local client
|
|
if (!pClient->isLocal())
|
|
{
|
|
Remove(pClient);
|
|
delete pClient;
|
|
}
|
|
}
|
|
}
|
|
|
|
C4ClientList &C4ClientList::operator =(const C4ClientList &List)
|
|
{
|
|
// remove clients that are not in the list
|
|
C4Client *pClient, *pNext;
|
|
for (pClient = pFirst; pClient; pClient = pNext)
|
|
{
|
|
pNext = pClient->pNext;
|
|
if (!List.getClientByID(pClient->getID()))
|
|
{
|
|
Remove(pClient);
|
|
delete pClient;
|
|
}
|
|
}
|
|
// add all clients
|
|
for (pClient = List.pFirst; pClient; pClient = pClient->pNext)
|
|
{
|
|
// already in list?
|
|
C4Client *pNewClient = getClientByID(pClient->getID());
|
|
if (pNewClient)
|
|
{
|
|
// core change
|
|
pNewClient->SetCore(pClient->getCore());
|
|
}
|
|
else
|
|
{
|
|
// add
|
|
pNewClient = Add(pClient->getCore());
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
void C4ClientList::CompileFunc(StdCompiler *pComp)
|
|
{
|
|
// Clear existing data
|
|
bool deserializing = pComp->isDeserializer();
|
|
if (deserializing) Clear();
|
|
// Client count
|
|
uint32_t iClientCnt = getClientCnt();
|
|
pComp->Value(mkNamingCountAdapt(iClientCnt, "Client"));
|
|
// Compile all clients
|
|
if (pComp->isDeserializer())
|
|
for (uint32_t i = 0; i < iClientCnt; i++)
|
|
{
|
|
C4Client *pClient = new C4Client();
|
|
pComp->Value(mkNamingAdapt(*pClient, "Client"));
|
|
Add(pClient);
|
|
}
|
|
else
|
|
for (C4Client *pClient = pFirst; pClient; pClient = pClient->pNext)
|
|
pComp->Value(mkNamingAdapt(*pClient, "Client"));
|
|
}
|