openclonk/src/network/C4Client.cpp

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"));
}