Ejemplo n.º 1
0
bool C4Network2ClientList::BroadcastMsgToClients(const C4NetIOPacket &rPkt)
{
	// Send a msg to all clients, including clients that are not connected to
	// this computer (will get forwarded by host).

	C4PacketFwd Fwd; Fwd.SetListType(true);
	// lock
	pIO->BeginBroadcast(false);
	// select connections for broadcast
	for (C4Network2Client *pClient = pFirst; pClient; pClient = pClient->getNext())
		if (!pClient->isHost())
			if (pClient->isConnected())
			{
				pClient->getMsgConn()->SetBroadcastTarget(true);
				Fwd.AddClient(pClient->getID());
			}
	// broadcast
	bool fSuccess = pIO->Broadcast(rPkt);
	// unlock
	pIO->EndBroadcast();
	// clients: send forward request to host
	if (!fHost)
	{
		Fwd.SetData(rPkt);
		fSuccess &= SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd));
	}
	return fSuccess;
}
Ejemplo n.º 2
0
void C4Network2IO::SendConnPackets()
{
    CStdLock ConnListLock(&ConnListCSec);

    // exlusive conn?
    if (fExclusiveConn)
        // find a live connection
        for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
            if (pConn->isAccepted() || (!pConn->isClosed() && pConn->isConnSent()))
                // do not sent additional conn packets - no other connection should succeed
                return;

    // sent pending welcome packet(s)
    for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
        if (pConn->isOpen() && !pConn->isConnSent())
        {
            // make packet
            CStdLock LCCoreLock(&LCCoreCSec);
            C4NetIOPacket Pkt = MkC4NetIOPacket(PID_Conn, C4PacketConn(LCCore, pConn->getID(), pConn->getPassword()));
            LCCoreLock.Clear();
            // send
            if (!pConn->Send(Pkt))
                pConn->Close();
            else
            {
                // set flag
                pConn->SetConnSent();
                // only one conn packet at a time
                if (fExclusiveConn)
                    return;
            }
        }

}
Ejemplo n.º 3
0
bool C4Network2Res::FinishDerive() // by main thread
{
	// All changes have been made. Register this resource with a new ID.

	// security
	if (!isAnonymous()) return false;

	CStdLock FileLock(&FileCSec);
	// Save back data
	int32_t iDerID = Core.getDerID();
	char szName[_MAX_PATH+1]; SCopy(Core.getFileName(), szName, _MAX_PATH);
	char szFileC[_MAX_PATH+1]; SCopy(szFile, szFileC, _MAX_PATH);
	// Set by file
	if (!SetByFile(szFileC, fTempFile, getType(), pParent->nextResID(), szName))
		return false;
	// create standalone
	if (!GetStandalone(NULL, 0, true))
		return false;
	// Set ID
	Core.SetDerived(iDerID);
	// announce derive
	pParent->getIOClass()->BroadcastMsg(MkC4NetIOPacket(PID_NetResDerive, Core));
	// derivation is dirty bussines
	fDirty = true;
	// ok
	return true;
}
Ejemplo n.º 4
0
void C4Network2IO::HandleFwdReq(const C4PacketFwd &rFwd, C4Network2IOConnection *pBy)
{
    CStdLock ConnListLock(&ConnListCSec);
    // init packet
    C4PacketFwd nFwd;
    nFwd.SetListType(false);
    // find all clients the message should be forwarded to
    int iClientID;
    C4Network2IOConnection *pConn;
    for (pConn = pConnList; pConn; pConn = pConn->pNext)
        if (pConn->isAccepted())
            if ((iClientID = pConn->getClientID()) >= 0)
                if (iClientID != pBy->getClientID())
                    if (rFwd.DoFwdTo(iClientID) && !nFwd.DoFwdTo(iClientID))
                        nFwd.AddClient(iClientID);
    // check count (hardcoded: broadcast for > 2 clients)
    if (nFwd.getClientCnt() <= 2)
    {
        C4NetIOPacket Pkt(rFwd.getData(), C4NetIO::addr_t());
        for (int i = 0; i < nFwd.getClientCnt(); i++)
            if ((pConn = GetMsgConnection(nFwd.getClient(i))))
            {
                pConn->Send(Pkt);
                pConn->DelRef();
            }
    }
    else
    {
        // Temporarily unlock connection list for getting broadcast lock
        // (might lead to deathlocks otherwise, as the lock is often taken
        //  in the opposite order)
        ConnListLock.Clear();

        BeginBroadcast();
        nFwd.SetData(rFwd.getData());
        // add all clients
        CStdLock ConnListLock(&ConnListCSec);
        for (int i = 0; i < nFwd.getClientCnt(); i++)
            if ((pConn = GetMsgConnection(nFwd.getClient(i))))
            {
                pConn->SetBroadcastTarget(true);
                pConn->DelRef();
            }
        // broadcast
        Broadcast(MkC4NetIOPacket(PID_Fwd, nFwd));
        EndBroadcast();
    }
    // doing a callback here; don't lock!
    ConnListLock.Clear();
    // forward to self?
    if (rFwd.DoFwdTo(LCCore.getID()))
    {
        C4NetIOPacket Packet(rFwd.getData(), pBy->getPeerAddr());
        HandlePacket(Packet, pBy, true);
    }
}
Ejemplo n.º 5
0
void C4Network2Client::SendAddresses(C4Network2IOConnection *pConn) {
  // send all addresses
  for (int32_t i = 0; i < iAddrCnt; i++) {
    C4NetIOPacket Pkt =
        MkC4NetIOPacket(PID_Addr, C4PacketAddr(getID(), Addr[i]));
    if (pConn)
      pConn->Send(Pkt);
    else
      pParent->BroadcastMsgToConnClients(Pkt);
  }
}
Ejemplo n.º 6
0
void C4Network2Client::CloseConns(const char *szMsg) {
  C4PacketConnRe Pkt(false, false, szMsg);
  C4Network2IOConnection *pConn;
  while (pConn = pMsgConn) {
    // send packet, close
    if (pConn->isOpen()) {
      pConn->Send(MkC4NetIOPacket(PID_ConnRe, Pkt));
      pConn->Close();
    }
    // remove
    RemoveConn(pConn);
  }
}
Ejemplo n.º 7
0
bool C4Network2IO::Ping()
{
    bool fSuccess = true;
    // ping all connections
    for (C4Network2IOConnection *pConn = pConnList; pConn; pConn = pConn->pNext)
        if (pConn->isOpen())
        {
            C4PacketPing Ping(pConn->getInPacketCounter(), pConn->getOutPacketCounter());
            fSuccess &= pConn->Send(MkC4NetIOPacket(PID_Ping, Ping));
            pConn->OnPing();
        }
    return fSuccess;
}
Ejemplo n.º 8
0
bool C4Network2ClientList::SendMsgToClient(int32_t iClient, C4NetIOPacket RREF rPkt)
{
	// find client
	C4Network2Client *pClient = GetClientByID(iClient);
	if (!pClient) return false;
	// connected? send directly
	if (pClient->isConnected())
		return pClient->SendMsg(rPkt);
	// forward
	C4PacketFwd Fwd; Fwd.SetListType(false);
	Fwd.AddClient(iClient);
	Fwd.SetData(rPkt);
	return SendMsgToHost(MkC4NetIOPacket(PID_FwdReq, Fwd));
}
Ejemplo n.º 9
0
bool C4Network2ResList::SendDiscover(C4Network2IOConnection *pTo) // by both
{
	// make packet
	C4PacketResDiscover Pkt;
	// add special retrieves
	CStdShareLock ResListLock(&ResListCSec);
	for (C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
		if (!pRes->isRemoved())
			if (pRes->isLoading())
				Pkt.AddDisID(pRes->getResID());
	ResListLock.Clear();
	// empty?
	if (!Pkt.getDisIDCnt()) return false;
	// broadcast?
	if (!pTo)
	{
		// save time
		iLastDiscover = time(NULL);
		// send
		return pIO->BroadcastMsg(MkC4NetIOPacket(PID_NetResDis, Pkt));
	}
	else
		return pTo->Send(MkC4NetIOPacket(PID_NetResDis, Pkt));
}
Ejemplo n.º 10
0
void C4GameOptionsList::OptionScenarioParameter::DoDropdownSelChange(int32_t idNewSelection)
{
	// runtime change needs to be synchronized
	if (!pForDlg->IsPreGame())
	{
		// change possible?
		if (!::Control.isCtrlHost()) return;
		// Then initiate an update of the parameters on all clients
		C4GameLobby::C4PacketSetScenarioParameter pck(ParameterDef->GetID(), idNewSelection);
		::Network.Clients.BroadcastMsgToClients(MkC4NetIOPacket(PID_SetScenarioParameter, pck));
	}
	// also process on host (and standalone pre-game)
	pForDlg->GetParameters()->SetValue(ParameterDef->GetID(), idNewSelection, false);
	if (pForDlg->IsPreGame()) Update();
}
Ejemplo n.º 11
0
bool C4Network2Players::JoinLocalPlayer(const char *szLocalPlayerFilename, bool initial)
{
	// ignore in replay
	// shouldn't even come here though
	assert(!Game.C4S.Head.Replay);
	if (Game.C4S.Head.Replay) return false;
	// if observing: don't try
	if (Game.Clients.getLocal()->isObserver()) return false;
	// network only
	assert(::Network.isEnabled());
	// create join info packet
	C4ClientPlayerInfos JoinInfo(szLocalPlayerFilename, !initial);
	// league game: get authentication for players
	if (Game.Parameters.isLeague())
		for (int i = 0; i < JoinInfo.GetPlayerCount(); i++)
		{
			C4PlayerInfo *pInfo = JoinInfo.GetPlayerInfo(i);
			if (!::Network.LeaguePlrAuth(pInfo))
			{
				JoinInfo.RemoveIndexedInfo(i);
				i--;
			}
		}
	// host or client?
	if (::Network.isHost())
	{
		// error joining players? Zero players is OK for initial packet; marks host as observer
		if (!initial && !JoinInfo.GetPlayerCount()) return false;
		// handle it as a direct request
		HandlePlayerInfoUpdRequest(&JoinInfo, true);
	}
	else
	{
		// clients request initial joins at host only
		// create player info for local player joins
		C4PacketPlayerInfoUpdRequest JoinRequest(JoinInfo);
		// any players to join? Zero players is OK for initial packet; marks client as observer
		// it's also necessary to send the empty player info packet, so the host will answer
		// with infos of all other clients
		if (!initial && !JoinRequest.Info.GetPlayerCount()) return false;
		::Network.Clients.SendMsgToHost(MkC4NetIOPacket(PID_PlayerInfoUpdReq, JoinRequest));
		// request activation
		if (JoinRequest.Info.GetPlayerCount() && !Game.Clients.getLocal()->isActivated())
			::Network.RequestActivate();
	}
	// done, success
	return true;
}
Ejemplo n.º 12
0
bool C4Network2Res::SendStatus(C4Network2IOConnection *pTo)
{
	// pack status
	C4NetIOPacket Pkt = MkC4NetIOPacket(PID_NetResStat, C4PacketResStatus(Core.getID(), Chunks));
	// to one client?
	if (pTo)
		return pTo->Send(Pkt);
	else
	{
		// reset dirty flag
		fDirty = false;
		// broadcast status
		assert(pParent && pParent->getIOClass());
		return pParent->getIOClass()->BroadcastMsg(Pkt);
	}
}
Ejemplo n.º 13
0
bool C4Network2Client::AddAddr(const C4Network2Address &addr, bool fAnnounce)
{
	// checks
	if (iAddrCnt + 1 >= C4ClientMaxAddr) return false;
	if (hasAddr(addr)) return true;
	// add
	Addr[iAddrCnt] = addr; AddrAttempts[iAddrCnt] = 0;
	iAddrCnt++;
	// attempt to use this one
	if (!iNextConnAttempt) iNextConnAttempt = time(NULL);
	// announce
	if (fAnnounce)
		if (!pParent->BroadcastMsgToConnClients(MkC4NetIOPacket(PID_Addr, C4PacketAddr(getID(), addr))))
			return false;
	// done
	return true;
}
Ejemplo n.º 14
0
void C4Network2Players::RequestPlayerInfoUpdate(const class C4ClientPlayerInfos &rRequest)
{
	// network only
	assert(::Network.isEnabled());
	// host or client?
	if (::Network.isHost())
	{
		// host processes directly
		HandlePlayerInfoUpdRequest(&rRequest, true);
	}
	else
	{
		// client sends request to host
		C4PacketPlayerInfoUpdRequest UpdateRequest(rRequest);
		::Network.Clients.SendMsgToHost(MkC4NetIOPacket(PID_PlayerInfoUpdReq, UpdateRequest));
	}
}
Ejemplo n.º 15
0
void C4Network2Client::SendAddresses(C4Network2IOConnection *pConn)
{
	// send all addresses
	for (int32_t i = 0; i < iAddrCnt; i++)
	{
		if (Addr[i].getAddr().GetScopeId() && (!pConn || pConn->getPeerAddr().GetScopeId() != Addr[i].getAddr().GetScopeId()))
			continue;
		C4Network2Address addr(Addr[i]);
		addr.getAddr().SetScopeId(0);
		C4NetIOPacket Pkt = MkC4NetIOPacket(PID_Addr, C4PacketAddr(getID(), addr));
		if (pConn)
			pConn->Send(Pkt);
		else
			pParent->BroadcastMsgToConnClients(Pkt);
	}

}
Ejemplo n.º 16
0
bool C4Network2Res::SendChunk(uint32_t iChunk, int32_t iToClient)
{
	assert(pParent && pParent->getIOClass());
	if (!szStandalone[0] || iChunk >= Core.getChunkCnt()) return false;
	// find connection for given client (one of the rare uses of the data connection)
	C4Network2IOConnection *pConn = pParent->getIOClass()->GetDataConnection(iToClient);
	if (!pConn) return false;
	// save last request time
	iLastReqTime = time(NULL);
	// create packet
	CStdLock FileLock(&FileCSec);
	C4Network2ResChunk ResChunk;
	ResChunk.Set(this, iChunk);
	// send
	bool fSuccess = pConn->Send(MkC4NetIOPacket(PID_NetResData, ResChunk));
	pConn->DelRef();
	return fSuccess;
}
Ejemplo n.º 17
0
bool C4Network2Client::DoTCPSimultaneousOpen(class C4Network2IO *pIO, const C4Network2Address &addr)
{
	if (!pIO->getNetIO(P_TCP)) return false;

	// Did we already bind a socket?
	if (TcpSimOpenSocket)
	{
		LogSilentF("Network: connecting client %s on %s with TCP simultaneous open...", getName(), addr.getAddr().ToString().getData());
		return pIO->ConnectWithSocket(addr.getAddr(), addr.getProtocol(), pClient->getCore(), std::move(TcpSimOpenSocket));
	}
	else
	{
		// No - bind one, inform peer, and schedule a connection attempt.
		auto NetIOTCP = dynamic_cast<C4NetIOTCP*>(pIO->getNetIO(P_TCP));
		auto bindAddr = pParent->GetLocal()->IPv6AddrFromPuncher;
		// We need to know an address that works.
		if (bindAddr.IsNull()) return false;
		bindAddr.SetPort(0);
		TcpSimOpenSocket = NetIOTCP->Bind(bindAddr);
		auto boundAddr = TcpSimOpenSocket->GetAddress();
		LogSilentF("Network: %s TCP simultaneous open request for client %s from %s...", addr.isIPNull() ? "initiating" : "responding to",
				getName(), boundAddr.ToString().getData());
		// Send address we bound to to the client.
		if (!SendMsg(MkC4NetIOPacket(PID_TCPSimOpen, C4PacketTCPSimOpen(pParent->GetLocal()->getID(), C4Network2Address(boundAddr, P_TCP)))))
			return false;
		if (!addr.isIPNull())
		{
			// We need to delay the connection attempt a bit. Unfortunately,
			// waiting for the next tick would usually take way too much time.
			// Instead, we block the main thread for a very short time and hope
			// that noone notices...
			int ping = getMsgConn()->getLag();
			std::this_thread::sleep_for(std::chrono::milliseconds(std::min(ping / 2, 10)));
			DoTCPSimultaneousOpen(pIO, addr);
		}
		return true;
	}
}
Ejemplo n.º 18
0
bool C4Network2Res::StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Available)
{
	assert(pParent && pParent->getIOClass());
	// all slots used? ignore
	if (iLoadCnt + 1 >= C4NetResMaxLoad) return true;
	// is there already a load by this client? ignore
	for (C4Network2ResLoad *pPos = pLoads; pPos; pPos = pPos->Next())
		if (pPos->getByClient() == iFromClient)
			return true;
	// find chunk to retrieve
	int32_t iLoads[C4NetResMaxLoad]; int32_t i = 0;
	for (C4Network2ResLoad *pLoad = pLoads; pLoad; pLoad = pLoad->Next())
		iLoads[i++] = pLoad->getChunk();
	int32_t iRetrieveChunk = Chunks.GetChunkToRetrieve(Available, i, iLoads);
	// nothing? ignore
	if (iRetrieveChunk < 0 || (uint32_t)iRetrieveChunk >= Core.getChunkCnt())
		return true;
	// search message connection for client
	C4Network2IOConnection *pConn = pParent->getIOClass()->GetMsgConnection(iFromClient);
	if (!pConn) return false;
	// send request
	if (!pConn->Send(MkC4NetIOPacket(PID_NetResReq, C4PacketResRequest(Core.getID(), iRetrieveChunk))))
		{ pConn->DelRef(); return false; }
	pConn->DelRef();
#ifdef C4NET2RES_DEBUG_LOG
	// log
	Application.InteractiveThread.ThreadLogS("Network: Res: requesting chunk %d of %d:%s (%s) from client %d",
	    iRetrieveChunk, Core.getID(), Core.getFileName(), szFile, iFromClient);
#endif
	// create load class
	C4Network2ResLoad *pnLoad = new C4Network2ResLoad(iRetrieveChunk, iFromClient);
	// add to list
	pnLoad->pNext = pLoads;
	pLoads = pnLoad;
	iLoadCnt++;
	// ok
	return true;
}
Ejemplo n.º 19
0
void C4Network2IO::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
{
    // security
    if (!pConn) return;

#define GETPKT(type, name) \
    assert(pPacket); const type &name = \
     static_cast<const type &>(*pPacket);

    switch (cStatus)
    {

    case PID_Conn: // connection request
    {
        if (!pConn->isOpen()) break;
        // get packet
        GETPKT(C4PacketConn, rPkt)
        // set connection ID
        pConn->SetRemoteID(rPkt.getConnID());
        // check auto-accept
        if (doAutoAccept(rPkt.getCCore(), *pConn))
        {
            // send answer back
            C4PacketConnRe pcr(true, false, "auto accept");
            if (!pConn->Send(MkC4NetIOPacket(PID_ConnRe, pcr)))
                pConn->Close();
            // accept
            pConn->SetStatus(CS_HalfAccepted);
            pConn->SetCCore(rPkt.getCCore());
            pConn->SetAutoAccepted();
        }
        // note that this packet will get processed by C4Network2, too (main thread)
    }
    break;

    case PID_ConnRe: // connection request reply
    {
        if (!pConn->isOpen()) break;
        // conn not sent? That's fishy.
        // FIXME: Note this happens if the peer has exclusive connection mode on.
        if (!pConn->isConnSent())
        {
            pConn->Close();
            break;
        }
        // get packet
        GETPKT(C4PacketConnRe, rPkt)
        // auto accept connection
        if (rPkt.isOK())
        {
            if (pConn->isHalfAccepted() && pConn->isAutoAccepted())
                pConn->SetAccepted();
        }
    }
    break;

    case PID_Ping:
    {
        if (!pConn->isOpen()) break;
        GETPKT(C4PacketPing, rPkt)
        // pong
        C4PacketPing PktPong = rPkt;
        pConn->Send(MkC4NetIOPacket(PID_Pong, PktPong));
        // remove received packets from log
        pConn->ClearPacketLog(rPkt.getPacketCounter());
    }
    break;

    case PID_Pong:
    {
        if (!pConn->isOpen()) break;
        GETPKT(C4PacketPing, rPkt);
        // save
        pConn->SetPingTime(rPkt.getTravelTime());
    }
    break;

    case PID_FwdReq:
    {
        GETPKT(C4PacketFwd, rPkt);
        HandleFwdReq(rPkt, pConn);
    }
    break;

    case PID_Fwd:
    {
        GETPKT(C4PacketFwd, rPkt);
        // only received accidently?
        if (!rPkt.DoFwdTo(LCCore.getID())) break;
        // handle
        C4NetIOPacket Packet(rPkt.getData(), pConn->getPeerAddr());
        HandlePacket(Packet, pConn, true);
    }
    break;

    case PID_PostMortem:
    {
        GETPKT(C4PacketPostMortem, rPkt);
        // Get connection
        C4Network2IOConnection *pConn = GetConnectionByID(rPkt.getConnID());
        if (!pConn) return;
        // Handle all packets
        uint32_t iCounter;
        for (iCounter = pConn->getInPacketCounter(); ; iCounter++)
        {
            // Get packet
            const C4NetIOPacket *pPkt = rPkt.getPacket(iCounter);
            if (!pPkt) break;
            // Handle it
            HandlePacket(*pPkt, pConn, true);
        }
        // Log
        if (iCounter > pConn->getInPacketCounter())
            Application.InteractiveThread.ThreadLogS("Network: Recovered %d packets", iCounter - pConn->getInPacketCounter());
        // Remove the connection from our list
        if (!pConn->isClosed())
            pConn->Close();
        RemoveConnection(pConn);
    }
    break;

    }

#undef GETPKT
}