forked from Mirrors/openclonk
Extend the netpuncher: clients can ask hosts for a connection initiation to circumvent port restricted NATs
parent
078ac47780
commit
e61dcbe4c3
|
@ -991,8 +991,12 @@ src/lib/StdBuf.h
|
|||
src/lib/StdCompiler.cpp
|
||||
src/lib/StdCompiler.h
|
||||
src/lib/StdResStr2.cpp
|
||||
src/netpuncher/C4PuncherPacket.cpp
|
||||
src/netpuncher/C4PuncherPacket.h
|
||||
src/network/C4NetIO.cpp
|
||||
src/network/C4NetIO.h
|
||||
src/network/C4Network2Address.cpp
|
||||
src/network/C4Network2Address.h
|
||||
src/platform/StdFile.cpp
|
||||
src/platform/StdFile.h
|
||||
src/platform/StdRegistry.cpp
|
||||
|
@ -1235,6 +1239,7 @@ target_link_libraries(c4group
|
|||
)
|
||||
|
||||
add_executable(netpuncher EXCLUDE_FROM_ALL
|
||||
src/netpuncher/C4PuncherHash.h
|
||||
src/netpuncher/main.cpp
|
||||
)
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ void C4ConfigNetwork::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(mkNamingAdapt(PacketLogging, "PacketLogging", 0 ));
|
||||
|
||||
|
||||
pComp->Value(mkNamingAdapt(s(PuncherAddress), "PuncherAddress", "netpuncher.openclonk.org:11115")); // maybe store default for this one?
|
||||
pComp->Value(mkNamingAdapt(s(PuncherAddress), "PuncherAddress", "netpuncher.openclonk.org:11115"));
|
||||
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueServer, StdCompiler::RCT_All), "LastLeagueServer", "" ));
|
||||
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeaguePlayerName, StdCompiler::RCT_All), "LastLeaguePlayerName", "" ));
|
||||
pComp->Value(mkNamingAdapt(mkParAdapt(LastLeagueAccount, StdCompiler::RCT_All), "LastLeagueAccount", "" ));
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#ifndef C4PuncherHash_H
|
||||
#define C4PuncherHash_H
|
||||
|
||||
#include <functional>
|
||||
#include "network/C4NetIO.h"
|
||||
|
||||
namespace {
|
||||
|
||||
size_t hash_combiner(size_t left, size_t right) //replacable
|
||||
{ return left*2793419347^right; }
|
||||
|
||||
template<int index, class...types>
|
||||
struct hash_impl {
|
||||
size_t operator()(size_t a, const std::tuple<types...>& t) const {
|
||||
typedef typename std::tuple_element<index-1, std::tuple<types...>>::type nexttype;
|
||||
hash_impl<index-1, types...> next;
|
||||
size_t b = std::hash<nexttype>()(std::get<index-1>(t));
|
||||
return next(hash_combiner(a, b), t);
|
||||
}
|
||||
};
|
||||
template<class...types>
|
||||
struct hash_impl<1, types...> {
|
||||
size_t operator()(size_t a, const std::tuple<types...>& t) const {
|
||||
typedef typename std::tuple_element<0, std::tuple<types...>>::type nexttype;
|
||||
size_t b = std::hash<nexttype>()(std::get<0>(t));
|
||||
return hash_combiner(a, b);
|
||||
}
|
||||
};
|
||||
template<class...types>
|
||||
struct hash_impl<0, types...> {
|
||||
size_t operator()(size_t a, const std::tuple<types...>& t) const {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace std {
|
||||
// Remove this when C++ gets proper tuple hashes. For now, http://stackoverflow.com/questions/7110301/generic-hash-for-tuples-in-unordered-map-unordered-set will do
|
||||
template<class...types>
|
||||
struct hash<std::tuple<types...>> {
|
||||
size_t operator()(const std::tuple<types...>& t) const {
|
||||
const size_t begin = std::tuple_size<std::tuple<types...>>::value;
|
||||
return hash_impl<begin, types...>()(19739, t);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct hash<::sockaddr_in> {
|
||||
size_t operator()(const ::sockaddr_in& addr) const {
|
||||
auto unpack = make_tuple(addr.sin_family, addr.sin_addr.s_addr, addr.sin_port);
|
||||
return hash<decltype(unpack)>()(unpack);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 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 "netpuncher/C4PuncherPacket.h"
|
||||
#include "network/C4Network2Address.h"
|
||||
#include <sstream>
|
||||
|
||||
std::unique_ptr<C4NetpuncherPacket> C4NetpuncherPacket::Construct(const C4NetIOPacket& rpack) {
|
||||
if (!rpack.getPData()) return nullptr;
|
||||
try {
|
||||
switch (rpack.getStatus())
|
||||
{
|
||||
case PID_Puncher_AssID: return uptr(new C4NetpuncherPacketAssID(rpack));
|
||||
case PID_Puncher_SReq: return uptr(new C4NetpuncherPacketSReq(rpack));
|
||||
case PID_Puncher_CReq: return uptr(new C4NetpuncherPacketCReq(rpack));
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
catch (StdCompiler::Exception *e) { delete e; return nullptr; }
|
||||
catch (...) { return nullptr; }
|
||||
}
|
||||
C4NetIOPacket C4NetpuncherPacket::PackTo(const C4NetIO::addr_t& addr) const {
|
||||
C4NetIOPacket pkt;
|
||||
pkt.SetAddr(addr);
|
||||
StdBuf content(PackInto());
|
||||
char type = GetType();
|
||||
pkt.New(sizeof(type) + content.getSize());
|
||||
pkt.Write(&type, sizeof(type));
|
||||
pkt.Write(content, /*offset*/sizeof(type));
|
||||
return pkt;
|
||||
}
|
||||
|
||||
C4NetpuncherPacketCReq::C4NetpuncherPacketCReq(const C4NetIOPacket& rpack) {
|
||||
C4Network2Address parse_addr;
|
||||
CompileFromBuf<StdCompilerBinRead>(parse_addr, rpack.getPBuf());
|
||||
if (parse_addr.getProtocol() != P_UDP) throw P_UDP;
|
||||
addr.sin_addr.s_addr = parse_addr.getAddr().sin_addr.s_addr,
|
||||
addr.sin_family = parse_addr.getAddr().sin_family,
|
||||
addr.sin_port = parse_addr.getAddr().sin_port;
|
||||
}
|
||||
|
||||
StdBuf C4NetpuncherPacketCReq::PackInto() const {
|
||||
C4Network2Address ser_addr;
|
||||
ser_addr.SetAddr(addr);
|
||||
ser_addr.SetProtocol(P_UDP);
|
||||
return DecompileToBuf<StdCompilerBinWrite>(ser_addr);
|
||||
}
|
||||
|
||||
template<C4NetpuncherPacketType TYPE>
|
||||
C4NetpuncherPacketID<TYPE>::C4NetpuncherPacketID(const C4NetIOPacket& rpack) {
|
||||
std::istringstream iss(std::string(rpack.getPData(), rpack.getPSize()));
|
||||
iss >> id;
|
||||
}
|
||||
|
||||
template<C4NetpuncherPacketType TYPE>
|
||||
StdBuf C4NetpuncherPacketID<TYPE>::PackInto() const {
|
||||
std::ostringstream oss;
|
||||
oss << GetID();
|
||||
std::string s = oss.str();
|
||||
return StdCopyBuf(s.c_str(), s.length());
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 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.
|
||||
*/
|
||||
|
||||
#ifndef C4PuncherPacket_H
|
||||
#define C4PuncherPacket_H
|
||||
|
||||
/* I'm cooking up a seperate solution for the netpuncher because using C4Packet2.cpp would require introducing half a ton of stubs, and it seems like overkill */
|
||||
|
||||
#include "network/C4NetIO.h"
|
||||
|
||||
enum C4NetpuncherPacketType {
|
||||
PID_Puncher_AssID = 0x51, // Puncher announcing ID to client
|
||||
PID_Puncher_SReq = 0x52, // Client requesting to be served with punching (for an ID)
|
||||
PID_Puncher_CReq = 0x53, // Puncher requesting clients to punch (towards an address)
|
||||
// extend this with exchanging ICE parameters, some day?
|
||||
};
|
||||
|
||||
typedef uint32_t C4NetpuncherID_t;
|
||||
|
||||
class C4NetpuncherPacket {
|
||||
public:
|
||||
typedef std::unique_ptr<C4NetpuncherPacket> uptr;
|
||||
static std::unique_ptr<C4NetpuncherPacket> Construct(const C4NetIOPacket& rpack);
|
||||
virtual ~C4NetpuncherPacket() {};
|
||||
virtual C4NetpuncherPacketType GetType() const = 0;
|
||||
C4NetIOPacket PackTo(const C4NetIO::addr_t&) const;
|
||||
protected:
|
||||
virtual StdBuf PackInto() const = 0;
|
||||
typedef C4NetpuncherID_t CID;
|
||||
};
|
||||
|
||||
template<C4NetpuncherPacketType TYPE>
|
||||
class C4NetpuncherPacketID : public C4NetpuncherPacket {
|
||||
private:
|
||||
CID id;
|
||||
StdBuf PackInto() const override;
|
||||
protected:
|
||||
C4NetpuncherPacketID(const C4NetIOPacket& rpack);
|
||||
C4NetpuncherPacketID(CID id) : id(id) {};
|
||||
public:
|
||||
C4NetpuncherPacketType GetType() const final { return TYPE; }
|
||||
CID GetID() const { return id; }
|
||||
virtual ~C4NetpuncherPacketID() {};
|
||||
};
|
||||
|
||||
#define PIDC(n) \
|
||||
class C4NetpuncherPacket##n : public C4NetpuncherPacketID<PID_Puncher_##n> { \
|
||||
public: explicit C4NetpuncherPacket##n(const C4NetIOPacket& p) : C4NetpuncherPacketID<PID_Puncher_##n>(p) {}; \
|
||||
public: explicit C4NetpuncherPacket##n(CID id) : C4NetpuncherPacketID<PID_Puncher_##n>(id) {}; \
|
||||
}
|
||||
PIDC(AssID); PIDC(SReq);
|
||||
#undef PIDC
|
||||
|
||||
class C4NetpuncherPacketCReq : public C4NetpuncherPacket {
|
||||
private:
|
||||
C4NetIO::addr_t addr;
|
||||
StdBuf PackInto() const override;
|
||||
public:
|
||||
C4NetpuncherPacketType GetType() const final { return PID_Puncher_CReq; }
|
||||
explicit C4NetpuncherPacketCReq(const C4NetIOPacket& rpack);
|
||||
explicit C4NetpuncherPacketCReq(const C4NetIO::addr_t& addr) : addr(addr) {};
|
||||
const C4NetIO::addr_t& GetAddr() { return addr; }
|
||||
};
|
||||
|
||||
#endif
|
|
@ -2,7 +2,7 @@
|
|||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2010-2013, The OpenClonk Team and contributors
|
||||
* Copyright (c) 2010-2016, The OpenClonk Team and contributors
|
||||
*
|
||||
* Distributed under the terms of the ISC license; see accompanying file
|
||||
* "COPYING" for details.
|
||||
|
@ -15,25 +15,61 @@
|
|||
*/
|
||||
|
||||
#include "C4Include.h"
|
||||
#include "netpuncher/C4PuncherHash.h"
|
||||
#include "network/C4Network2.h"
|
||||
#include "netpuncher/C4PuncherPacket.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <functional>
|
||||
#include <random>
|
||||
#include <stdexcept>
|
||||
|
||||
class C4PuncherServer : public C4NetIOUDP, private C4NetIO::CBClass
|
||||
{
|
||||
public:
|
||||
C4PuncherServer() { C4NetIOUDP::SetCallback(this); }
|
||||
typedef C4NetpuncherID_t CID;
|
||||
C4PuncherServer() {
|
||||
C4NetIOUDP::SetCallback(this);
|
||||
rng = std::bind(std::uniform_int_distribution<CID>(1/*, max*/), std::ref(random_device));
|
||||
}
|
||||
private:
|
||||
std::random_device random_device;
|
||||
std::function<CID()> rng;
|
||||
std::unordered_map<addr_t, CID> peer_ids;
|
||||
std::unordered_map<CID, addr_t> peer_addrs;
|
||||
// Event handlers
|
||||
virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *OwnAddr, C4NetIO *pNetIO)
|
||||
{
|
||||
printf("Punched back at %s:%d...\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port));
|
||||
virtual bool OnConn(const addr_t &AddrPeer, const addr_t &AddrConnect, const addr_t *OwnAddr, C4NetIO *pNetIO) {
|
||||
CID nid;
|
||||
do {
|
||||
nid = rng();
|
||||
} while(peer_addrs.count(nid) && !nid);
|
||||
peer_ids.emplace(AddrPeer, nid);
|
||||
peer_addrs.emplace(nid, AddrPeer);
|
||||
Send(C4NetpuncherPacketAssID(nid).PackTo(AddrPeer));
|
||||
printf("Punched %s:%d... #%u\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), nid);
|
||||
return true;
|
||||
}
|
||||
virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
|
||||
{
|
||||
// Unused
|
||||
virtual void OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO) {
|
||||
auto& addr = rPacket.getAddr();
|
||||
auto unpack = C4NetpuncherPacket::Construct(rPacket);
|
||||
if (!unpack || unpack->GetType() != PID_Puncher_SReq) { Close(addr); return; }
|
||||
auto other_it = peer_addrs.find(dynamic_cast<C4NetpuncherPacketSReq*>(unpack.get())->GetID());
|
||||
if (other_it == peer_addrs.end()) return; // Might be nice to return some kind of error, for purposes of debugging.
|
||||
Send(C4NetpuncherPacketCReq(other_it->second).PackTo(addr));
|
||||
Send(C4NetpuncherPacketCReq(addr).PackTo(other_it->second));
|
||||
}
|
||||
virtual void OnDisconn(const addr_t &AddrPeer, C4NetIO *pNetIO, const char *szReason) {
|
||||
auto it = peer_ids.find(AddrPeer);
|
||||
if (it == peer_ids.end()) {
|
||||
printf("ERROR: closing connection for %s:%d: (%s) but no connection is known\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), szReason);
|
||||
return;
|
||||
}
|
||||
peer_addrs.erase(it->second);
|
||||
peer_ids.erase(it);
|
||||
printf("Stopped punching %s:%d: %s...\n", inet_ntoa(AddrPeer.sin_addr), htons(AddrPeer.sin_port), szReason);
|
||||
};
|
||||
} Puncher;
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
|
|
|
@ -451,6 +451,7 @@ public:
|
|||
virtual bool Close(const addr_t &addr);
|
||||
|
||||
virtual bool Send(const C4NetIOPacket &rPacket);
|
||||
bool SendDirect(C4NetIOPacket &&rPacket); // (mt-safe)
|
||||
virtual bool Broadcast(const C4NetIOPacket &rPacket);
|
||||
virtual bool SetBroadcast(const addr_t &addr, bool fSet = true);
|
||||
|
||||
|
@ -739,7 +740,6 @@ protected:
|
|||
|
||||
// sending
|
||||
bool BroadcastDirect(const Packet &rPacket, unsigned int iNr = ~0u); // (mt-safe)
|
||||
bool SendDirect(C4NetIOPacket &&rPacket); // (mt-safe)
|
||||
|
||||
// multicast related
|
||||
bool DoLoopbackTest();
|
||||
|
|
|
@ -147,7 +147,8 @@ C4Network2::C4Network2()
|
|||
pVoteDialog(NULL),
|
||||
fPausedForVote(false),
|
||||
iLastOwnVoting(0),
|
||||
fStreaming(false)
|
||||
fStreaming(false),
|
||||
NetpuncherGameID(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -168,6 +169,8 @@ bool C4Network2::InitHost(bool fLobby)
|
|||
fChasing = false;
|
||||
fAllowJoin = false;
|
||||
iNextClientID = C4ClientIDStart;
|
||||
NetpuncherGameID = 0;
|
||||
NetpuncherAddr = ::Config.Network.PuncherAddress;
|
||||
// initialize client list
|
||||
Clients.Init(&Game.Clients, true);
|
||||
// initialize resource list
|
||||
|
@ -228,6 +231,8 @@ C4Network2::InitResult C4Network2::InitClient(const C4Network2Reference &Ref, bo
|
|||
}
|
||||
// repeat if wrong password
|
||||
fWrongPassword = Ref.isPasswordNeeded();
|
||||
NetpuncherGameID = Ref.getNetpuncherGameID();
|
||||
NetpuncherAddr = Ref.getNetpuncherAddr();
|
||||
StdStrBuf Password;
|
||||
for (;;)
|
||||
{
|
||||
|
@ -295,6 +300,8 @@ C4Network2::InitResult C4Network2::InitClient(const class C4Network2Address *pAd
|
|||
pControl = &::Control.Network;
|
||||
// set exclusive connection mode
|
||||
NetIO.SetExclusiveConnMode(true);
|
||||
// warm up netpuncher
|
||||
InitPuncher();
|
||||
// try to connect host
|
||||
StdStrBuf strAddresses; int iSuccesses = 0;
|
||||
for (int i = 0; i < iAddrCount; i++)
|
||||
|
@ -663,6 +670,7 @@ void C4Network2::Clear()
|
|||
delete pVoteDialog; pVoteDialog = NULL;
|
||||
fPausedForVote = false;
|
||||
iLastOwnVoting = 0;
|
||||
NetpuncherGameID = 0;
|
||||
Votes.Clear();
|
||||
// don't clear fPasswordNeeded here, it's needed by InitClient
|
||||
}
|
||||
|
@ -880,6 +888,49 @@ void C4Network2::HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C
|
|||
if (pLobby) pLobby->HandlePacket(cStatus, pBasePkt, pClient);
|
||||
}
|
||||
|
||||
bool C4Network2::HandlePuncherPacket(C4NetpuncherPacket::uptr pkt)
|
||||
{
|
||||
// TODO: is this all thread-safe?
|
||||
assert(pkt);
|
||||
#define GETPKT(c) dynamic_cast<C4NetpuncherPacket##c*>(pkt.get())
|
||||
switch (pkt->GetType())
|
||||
{
|
||||
case PID_Puncher_CReq:
|
||||
if (isHost())
|
||||
{
|
||||
NetIO.Punch(GETPKT(CReq)->GetAddr());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The IP/Port should be already in the masterserver list, so just keep trying.
|
||||
return Status.getState() == GS_Init;
|
||||
}
|
||||
case PID_Puncher_AssID:
|
||||
if (isHost())
|
||||
{
|
||||
NetpuncherGameID = GETPKT(AssID)->GetID();
|
||||
InvalidateReference();
|
||||
}
|
||||
else
|
||||
{
|
||||
// While we don't need the ID as a client, this nicely serves as the signal that we can start using the netpuncher
|
||||
if (Status.getState() == GS_Init && getNetpuncherGameID())
|
||||
NetIO.SendPuncherPacket(C4NetpuncherPacketSReq(getNetpuncherGameID()));
|
||||
}
|
||||
return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
void C4Network2::InitPuncher()
|
||||
{
|
||||
// We have an internet connection, so let's punch the puncher server here in order to open an udp port
|
||||
C4NetIO::addr_t PuncherAddr;
|
||||
if (ResolveAddress(getNetpuncherAddr().getData(), &PuncherAddr, C4NetStdPortPuncher))
|
||||
NetIO.InitPuncher(PuncherAddr);
|
||||
}
|
||||
|
||||
void C4Network2::OnGameSynchronized()
|
||||
{
|
||||
// savegame needed?
|
||||
|
@ -1978,10 +2029,7 @@ bool C4Network2::LeagueStart(bool *pCancel)
|
|||
return false;
|
||||
}
|
||||
|
||||
// We have an internet connection, so let's punch the master server here in order to open an udp port
|
||||
C4NetIO::addr_t PuncherAddr;
|
||||
if (ResolveAddress(Config.Network.PuncherAddress, &PuncherAddr, C4NetStdPortPuncher))
|
||||
NetIO.Punch(PuncherAddr);
|
||||
InitPuncher();
|
||||
|
||||
// Let's wait for response
|
||||
StdStrBuf Message = FormatString(LoadResStr("IDS_NET_LEAGUE_REGGAME"), pLeagueClient->getServerName());
|
||||
|
|
|
@ -193,6 +193,10 @@ protected:
|
|||
class C4Network2HTTPClient *pStreamer;
|
||||
unsigned int iCurrentStreamAmount, iCurrentStreamPosition;
|
||||
|
||||
// puncher
|
||||
C4NetpuncherID_t NetpuncherGameID;
|
||||
StdCopyStrBuf NetpuncherAddr;
|
||||
|
||||
public:
|
||||
|
||||
// data access
|
||||
|
@ -253,6 +257,7 @@ public:
|
|||
void OnDisconn(C4Network2IOConnection *pConn);
|
||||
void HandlePacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
|
||||
void HandleLobbyPacket(char cStatus, const C4PacketBase *pBasePkt, C4Network2IOConnection *pConn);
|
||||
bool HandlePuncherPacket(C4NetpuncherPacket::uptr);
|
||||
|
||||
// runtime join stuff
|
||||
void OnGameSynchronized();
|
||||
|
@ -297,6 +302,10 @@ public:
|
|||
bool FinishStreaming();
|
||||
bool StopStreaming();
|
||||
|
||||
// netpuncher
|
||||
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
|
||||
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
|
||||
|
||||
protected:
|
||||
|
||||
using C4ApplicationSec1Timer::Execute; // to avoid "virtual ... is hidden" warning
|
||||
|
@ -350,6 +359,9 @@ protected:
|
|||
// streaming
|
||||
bool StreamIn(bool fFinish);
|
||||
bool StreamOut();
|
||||
|
||||
// puncher
|
||||
void InitPuncher();
|
||||
};
|
||||
|
||||
extern C4Network2 Network;
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2010-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"
|
||||
#ifndef _WIN32
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
#include "network/C4Network2Address.h"
|
||||
|
||||
// *** C4Network2Address
|
||||
|
||||
void C4Network2Address::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
// Clear
|
||||
if (pComp->isCompiler())
|
||||
{
|
||||
ZeroMem(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
}
|
||||
|
||||
// Write protocol
|
||||
StdEnumEntry<C4Network2IOProtocol> Protocols[] =
|
||||
{
|
||||
{ "UDP", P_UDP },
|
||||
{ "TCP", P_TCP },
|
||||
|
||||
{ NULL, P_NONE },
|
||||
};
|
||||
pComp->Value(mkEnumAdaptT<uint8_t>(eProtocol, Protocols));
|
||||
pComp->Separator(StdCompiler::SEP_PART2); // ':'
|
||||
|
||||
// Write IP (no IP = 0.0.0.0)
|
||||
in_addr zero; zero.s_addr = INADDR_ANY;
|
||||
pComp->Value(mkDefaultAdapt(addr.sin_addr, zero));
|
||||
pComp->Separator(StdCompiler::SEP_PART2); // ':'
|
||||
|
||||
// Write port
|
||||
uint16_t iPort = htons(addr.sin_port);
|
||||
pComp->Value(iPort);
|
||||
addr.sin_port = htons(iPort);
|
||||
}
|
||||
|
||||
StdStrBuf C4Network2Address::toString() const
|
||||
{
|
||||
switch (eProtocol)
|
||||
{
|
||||
case P_UDP: return FormatString("UDP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
|
||||
case P_TCP: return FormatString("TCP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
|
||||
default: return StdStrBuf("INVALID");
|
||||
}
|
||||
}
|
||||
|
||||
bool C4Network2Address::operator == (const C4Network2Address &addr2) const
|
||||
{
|
||||
return eProtocol == addr2.getProtocol() && AddrEqual(addr, addr2.getAddr());
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* OpenClonk, http://www.openclonk.org
|
||||
*
|
||||
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
|
||||
* Copyright (c) 2013-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.
|
||||
*/
|
||||
|
||||
#ifndef INC_C4Network2Address
|
||||
#define INC_C4Network2Address
|
||||
|
||||
#include "network/C4Network2IO.h"
|
||||
|
||||
class C4Network2Address
|
||||
{
|
||||
public:
|
||||
C4Network2Address()
|
||||
: eProtocol(P_NONE)
|
||||
{ ZeroMem(&addr, sizeof(addr)); }
|
||||
|
||||
C4Network2Address(C4NetIO::addr_t addr, C4Network2IOProtocol eProtocol)
|
||||
: addr(addr), eProtocol(eProtocol)
|
||||
{ }
|
||||
|
||||
C4Network2Address(const C4Network2Address &addr)
|
||||
: addr(addr.getAddr()), eProtocol(addr.getProtocol())
|
||||
{ }
|
||||
|
||||
void operator = (const C4Network2Address &addr)
|
||||
{ SetAddr(addr.getAddr()); SetProtocol(addr.getProtocol()); }
|
||||
|
||||
bool operator == (const C4Network2Address &addr) const;
|
||||
|
||||
protected:
|
||||
C4NetIO::addr_t addr;
|
||||
C4Network2IOProtocol eProtocol;
|
||||
|
||||
public:
|
||||
const C4NetIO::addr_t &getAddr() const { return addr; }
|
||||
in_addr getIPAddr() const { return addr.sin_addr; }
|
||||
bool isIPNull() const { return !addr.sin_addr.s_addr; }
|
||||
uint16_t getPort() const { return htons(addr.sin_port); }
|
||||
C4Network2IOProtocol getProtocol() const { return eProtocol; }
|
||||
|
||||
StdStrBuf toString() const;
|
||||
|
||||
void SetAddr(C4NetIO::addr_t naddr) { addr = naddr; }
|
||||
void SetIP(in_addr ip) { addr.sin_addr = ip; }
|
||||
void SetPort(uint16_t iPort) { addr.sin_port = htons(iPort); }
|
||||
void SetProtocol(C4Network2IOProtocol enProtocol) { eProtocol = enProtocol; }
|
||||
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -32,55 +32,6 @@
|
|||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
// *** C4Network2Address
|
||||
|
||||
void C4Network2Address::CompileFunc(StdCompiler *pComp)
|
||||
{
|
||||
// Clear
|
||||
if (pComp->isCompiler())
|
||||
{
|
||||
ZeroMem(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
}
|
||||
|
||||
// Write protocol
|
||||
StdEnumEntry<C4Network2IOProtocol> Protocols[] =
|
||||
{
|
||||
{ "UDP", P_UDP },
|
||||
{ "TCP", P_TCP },
|
||||
|
||||
{ NULL, P_NONE },
|
||||
};
|
||||
pComp->Value(mkEnumAdaptT<uint8_t>(eProtocol, Protocols));
|
||||
pComp->Separator(StdCompiler::SEP_PART2); // ':'
|
||||
|
||||
// Write IP (no IP = 0.0.0.0)
|
||||
in_addr zero; zero.s_addr = INADDR_ANY;
|
||||
pComp->Value(mkDefaultAdapt(addr.sin_addr, zero));
|
||||
pComp->Separator(StdCompiler::SEP_PART2); // ':'
|
||||
|
||||
// Write port
|
||||
uint16_t iPort = htons(addr.sin_port);
|
||||
pComp->Value(iPort);
|
||||
addr.sin_port = htons(iPort);
|
||||
}
|
||||
|
||||
StdStrBuf C4Network2Address::toString() const
|
||||
{
|
||||
switch (eProtocol)
|
||||
{
|
||||
case P_UDP: return FormatString("UDP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
|
||||
case P_TCP: return FormatString("TCP:%s:%d", inet_ntoa(addr.sin_addr), htons(addr.sin_port));
|
||||
default: return StdStrBuf("INVALID");
|
||||
}
|
||||
}
|
||||
|
||||
bool C4Network2Address::operator == (const C4Network2Address &addr2) const
|
||||
{
|
||||
return eProtocol == addr2.getProtocol() && AddrEqual(addr, addr2.getAddr());
|
||||
}
|
||||
|
||||
|
||||
// *** C4Network2Client
|
||||
|
||||
C4Network2Client::C4Network2Client(C4Client *pClient)
|
||||
|
@ -201,6 +152,7 @@ bool C4Network2Client::DoConnectAttempt(C4Network2IO *pIO)
|
|||
|
||||
bool C4Network2Client::hasAddr(const C4Network2Address &addr) const
|
||||
{
|
||||
// Note that the host only knows its own address as 0.0.0.0, so if the real address is being added, that can't be sorted out.
|
||||
for (int32_t i = 0; i < iAddrCnt; i++)
|
||||
if (Addr[i] == addr)
|
||||
return true;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "network/C4Network2IO.h"
|
||||
#include "network/C4PacketBase.h"
|
||||
#include "network/C4Client.h"
|
||||
#include "network/C4Network2Address.h"
|
||||
|
||||
class C4Network2; class C4Network2IOConnection;
|
||||
|
||||
|
@ -40,47 +41,6 @@ enum C4Network2ClientStatus
|
|||
NCS_Remove // client is to be removed
|
||||
};
|
||||
|
||||
class C4Network2Address
|
||||
{
|
||||
public:
|
||||
C4Network2Address()
|
||||
: eProtocol(P_NONE)
|
||||
{ ZeroMem(&addr, sizeof(addr)); }
|
||||
|
||||
C4Network2Address(C4NetIO::addr_t addr, C4Network2IOProtocol eProtocol)
|
||||
: addr(addr), eProtocol(eProtocol)
|
||||
{ }
|
||||
|
||||
C4Network2Address(const C4Network2Address &addr)
|
||||
: addr(addr.getAddr()), eProtocol(addr.getProtocol())
|
||||
{ }
|
||||
|
||||
void operator = (const C4Network2Address &addr)
|
||||
{ SetAddr(addr.getAddr()); SetProtocol(addr.getProtocol()); }
|
||||
|
||||
bool operator == (const C4Network2Address &addr) const;
|
||||
|
||||
protected:
|
||||
C4NetIO::addr_t addr;
|
||||
C4Network2IOProtocol eProtocol;
|
||||
|
||||
public:
|
||||
const C4NetIO::addr_t &getAddr() const { return addr; }
|
||||
in_addr getIPAddr() const { return addr.sin_addr; }
|
||||
bool isIPNull() const { return !addr.sin_addr.s_addr; }
|
||||
uint16_t getPort() const { return htons(addr.sin_port); }
|
||||
C4Network2IOProtocol getProtocol() const { return eProtocol; }
|
||||
|
||||
StdStrBuf toString() const;
|
||||
|
||||
void SetAddr(C4NetIO::addr_t naddr) { addr = naddr; }
|
||||
void SetIP(in_addr ip) { addr.sin_addr = ip; }
|
||||
void SetPort(uint16_t iPort) { addr.sin_port = htons(iPort); }
|
||||
void SetProtocol(C4Network2IOProtocol enProtocol) { eProtocol = enProtocol; }
|
||||
|
||||
void CompileFunc(StdCompiler *pComp);
|
||||
};
|
||||
|
||||
class C4Network2Client
|
||||
{
|
||||
friend class C4Network2ClientList;
|
||||
|
@ -244,4 +204,3 @@ public:
|
|||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ struct C4Network2IO::NetEvPacketData
|
|||
};
|
||||
|
||||
// compile options
|
||||
#define C4NET2IO_DUMP_LEVEL 1
|
||||
#define C4NET2IO_DUMP_LEVEL 3
|
||||
|
||||
// *** C4Network2IO
|
||||
|
||||
|
@ -455,7 +455,7 @@ bool C4Network2IO::BroadcastMsg(const C4NetIOPacket &rPkt) // by both
|
|||
return fSuccess;
|
||||
}
|
||||
|
||||
bool C4Network2IO::Punch(C4NetIO::addr_t nPuncherAddr)
|
||||
bool C4Network2IO::InitPuncher(C4NetIO::addr_t nPuncherAddr)
|
||||
{
|
||||
// UDP must be initialized
|
||||
if (!pNetIO_UDP)
|
||||
|
@ -466,19 +466,30 @@ bool C4Network2IO::Punch(C4NetIO::addr_t nPuncherAddr)
|
|||
return pNetIO_UDP->Connect(PuncherAddr);
|
||||
}
|
||||
|
||||
void C4Network2IO::Punch(const C4NetIO::addr_t &punchee_addr) {
|
||||
if (!pNetIO_UDP)
|
||||
return;
|
||||
C4PacketPing PktPeng;
|
||||
dynamic_cast<C4NetIOUDP*>(pNetIO_UDP)->SendDirect(MkC4NetIOPacket(PID_Pong, PktPeng, punchee_addr));
|
||||
}
|
||||
|
||||
void C4Network2IO::SendPuncherPacket(const C4NetpuncherPacket& p) {
|
||||
if (!pNetIO_UDP || !PuncherAddr.sin_addr.s_addr) return;
|
||||
pNetIO_UDP->Send(p.PackTo(PuncherAddr));
|
||||
}
|
||||
|
||||
// C4NetIO interface
|
||||
bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t &ConnectAddr, const C4NetIO::addr_t *pOwnAddr, C4NetIO *pNetIO)
|
||||
{
|
||||
// puncher answer? We just make sure here a connection /can/ be established, so close it instantly.
|
||||
if (pNetIO == pNetIO_UDP)
|
||||
if (PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, ConnectAddr))
|
||||
{
|
||||
// got an address?
|
||||
if (pOwnAddr)
|
||||
OnPunch(*pOwnAddr);
|
||||
// this is only a test connection - close it instantly
|
||||
return false;
|
||||
}
|
||||
// puncher answer?
|
||||
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, ConnectAddr))
|
||||
{
|
||||
// got an address?
|
||||
if (pOwnAddr)
|
||||
OnPuncherConnect(*pOwnAddr);
|
||||
return true;
|
||||
}
|
||||
|
||||
#if(C4NET2IO_DUMP_LEVEL > 1)
|
||||
Application.InteractiveThread.ThreadLogS("OnConn: %s %s",
|
||||
C4TimeMilliseconds::Now().AsString().getData(),
|
||||
|
@ -527,12 +538,11 @@ bool C4Network2IO::OnConn(const C4NetIO::addr_t &PeerAddr, const C4NetIO::addr_t
|
|||
void C4Network2IO::OnDisconn(const C4NetIO::addr_t &addr, C4NetIO *pNetIO, const char *szReason)
|
||||
{
|
||||
// punch?
|
||||
if (pNetIO == pNetIO_UDP)
|
||||
if (PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, addr))
|
||||
{
|
||||
ZeroMem(&PuncherAddr, sizeof(PuncherAddr));
|
||||
return;
|
||||
}
|
||||
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, addr))
|
||||
{
|
||||
ZeroMem(&PuncherAddr, sizeof(PuncherAddr));
|
||||
return;
|
||||
}
|
||||
#if(C4NET2IO_DUMP_LEVEL > 1)
|
||||
Application.InteractiveThread.ThreadLogS("OnDisconn: %s %s",
|
||||
C4TimeMilliseconds::Now().AsString().getData(),
|
||||
|
@ -570,6 +580,11 @@ void C4Network2IO::OnPacket(const class C4NetIOPacket &rPacket, C4NetIO *pNetIO)
|
|||
C4TimeMilliseconds::Now().AsString().getData(),
|
||||
rPacket.getStatus(), getNetIOName(pNetIO));
|
||||
#endif
|
||||
if (pNetIO == pNetIO_UDP && PuncherAddr.sin_addr.s_addr && AddrEqual(PuncherAddr, rPacket.getAddr()))
|
||||
{
|
||||
HandlePuncherPacket(rPacket);
|
||||
return;
|
||||
}
|
||||
if (!rPacket.getSize()) return;
|
||||
// find connection
|
||||
C4Network2IOConnection *pConn = GetConnection(rPacket.getAddr(), pNetIO);
|
||||
|
@ -1120,6 +1135,17 @@ void C4Network2IO::HandleFwdReq(const C4PacketFwd &rFwd, C4Network2IOConnection
|
|||
}
|
||||
}
|
||||
|
||||
void C4Network2IO::HandlePuncherPacket(const C4NetIOPacket& rPacket)
|
||||
{
|
||||
auto pkt = C4NetpuncherPacket::Construct(rPacket);
|
||||
if (pkt && ::Network.HandlePuncherPacket(move(pkt)));
|
||||
else
|
||||
{
|
||||
assert(pNetIO_UDP);
|
||||
pNetIO_UDP->Close(rPacket.getAddr());
|
||||
}
|
||||
}
|
||||
|
||||
bool C4Network2IO::Ping()
|
||||
{
|
||||
bool fSuccess = true;
|
||||
|
@ -1237,7 +1263,7 @@ void C4Network2IO::SendConnPackets()
|
|||
|
||||
}
|
||||
|
||||
void C4Network2IO::OnPunch(C4NetIO::addr_t addr)
|
||||
void C4Network2IO::OnPuncherConnect(C4NetIO::addr_t addr)
|
||||
{
|
||||
// Sanity check
|
||||
assert (addr.sin_family == AF_INET);
|
||||
|
@ -1250,8 +1276,8 @@ void C4Network2IO::OnPunch(C4NetIO::addr_t addr)
|
|||
// Add for local client
|
||||
C4Network2Client *pLocal = ::Network.Clients.GetLocal();
|
||||
if (pLocal)
|
||||
if (pLocal->AddAddr(C4Network2Address(addr, P_UDP), true))
|
||||
::Network.InvalidateReference();
|
||||
pLocal->AddAddr(C4Network2Address(addr, P_UDP), true);
|
||||
// Do not ::Network.InvalidateReference(); yet, we're expecting an ID from the netpuncher
|
||||
}
|
||||
|
||||
// *** C4Network2IOConnection
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "network/C4NetIO.h"
|
||||
#include "network/C4Client.h"
|
||||
#include "network/C4InteractiveThread.h"
|
||||
#include "netpuncher/C4PuncherPacket.h"
|
||||
|
||||
class C4Network2IOConnection;
|
||||
|
||||
|
@ -135,7 +136,9 @@ public:
|
|||
bool BroadcastMsg(const C4NetIOPacket &rPkt); // by both
|
||||
|
||||
// punch
|
||||
bool Punch(C4NetIO::addr_t PuncherAddr); // by main thread
|
||||
bool InitPuncher(C4NetIO::addr_t PuncherAddr); // by main thread
|
||||
void SendPuncherPacket(const C4NetpuncherPacket&);
|
||||
void Punch(const C4NetIO::addr_t&); // sends a ping packet
|
||||
|
||||
// stuff
|
||||
C4NetIO *getNetIO(C4Network2IOProtocol eProt); // by both
|
||||
|
@ -185,6 +188,7 @@ protected:
|
|||
// packet handling (some are really handled here)
|
||||
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn);
|
||||
void HandleFwdReq(const class C4PacketFwd &rFwd, C4Network2IOConnection *pBy);
|
||||
void HandlePuncherPacket(const C4NetIOPacket &rPacket);
|
||||
|
||||
// misc
|
||||
bool Ping();
|
||||
|
@ -193,8 +197,7 @@ protected:
|
|||
void SendConnPackets();
|
||||
|
||||
// puncher
|
||||
void OnPunch(C4NetIO::addr_t addr);
|
||||
|
||||
void OnPuncherConnect(C4NetIO::addr_t addr);
|
||||
};
|
||||
|
||||
enum C4Network2IOConnStatus
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
C4Network2Reference::C4Network2Reference()
|
||||
: Icon(0), GameMode(), Time(0), Frame(0), StartTime(0), LeaguePerformance(0),
|
||||
JoinAllowed(true), ObservingAllowed(true), PasswordNeeded(false), OfficialServer(false),
|
||||
IsEditor(false), iAddrCnt(0)
|
||||
IsEditor(false), iAddrCnt(0), NetpuncherGameID(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ void C4Network2Reference::InitLocal()
|
|||
ObservingAllowed = ::Network.isObservingAllowed();
|
||||
PasswordNeeded = ::Network.isPassworded();
|
||||
IsEditor = !!::Application.isEditor;
|
||||
NetpuncherGameID = ::Network.getNetpuncherGameID();
|
||||
NetpuncherAddr = ::Network.getNetpuncherAddr();
|
||||
Game.Set();
|
||||
|
||||
// Addresses
|
||||
|
@ -125,6 +127,8 @@ void C4Network2Reference::CompileFunc(StdCompiler *pComp)
|
|||
pComp->Value(mkNamingAdapt(Game.sEngineName, "Game", "None"));
|
||||
pComp->Value(mkNamingAdapt(mkArrayAdaptDM(Game.iVer,0),"Version" ));
|
||||
pComp->Value(mkNamingAdapt(OfficialServer, "OfficialServer", false));
|
||||
pComp->Value(mkNamingAdapt(NetpuncherGameID, "NetpuncherID", 0, false, false));
|
||||
pComp->Value(mkNamingAdapt(NetpuncherAddr, "NetpuncherAddr", "", false, false));
|
||||
|
||||
pComp->Value(Parameters);
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@ private:
|
|||
bool PasswordNeeded;
|
||||
bool OfficialServer;
|
||||
bool IsEditor;
|
||||
C4NetpuncherID_t NetpuncherGameID;
|
||||
StdCopyStrBuf NetpuncherAddr;
|
||||
|
||||
// Engine information
|
||||
C4GameVersion Game;
|
||||
|
@ -75,6 +77,8 @@ public:
|
|||
int32_t getStartTime() const { return StartTime; }
|
||||
StdStrBuf getGameGoalString() const;
|
||||
bool isEditor() const { return IsEditor; }
|
||||
C4NetpuncherID_t getNetpuncherGameID() const { return NetpuncherGameID; }
|
||||
StdStrBuf getNetpuncherAddr() const { return NetpuncherAddr; }
|
||||
|
||||
void SetSourceIP(in_addr ip);
|
||||
|
||||
|
|
|
@ -101,7 +101,6 @@ const C4PktHandlingData PktHandlingData[] =
|
|||
{ PID_ControlPkt, PC_Network, "Control Paket", false, false, PH_C4GameControlNetwork, PKT_UNPACK(C4PacketControlPkt) },
|
||||
{ PID_ExecSyncCtrl, PC_Network, "Execute Sync Control", false, false, PH_C4GameControlNetwork, PKT_UNPACK(C4PacketExecSyncCtrl)},
|
||||
|
||||
|
||||
// Control (Isn't send over network, handled only as part of a control list)
|
||||
{ CID_ClientJoin, PC_Control, "Client Join", false, true, 0, PKT_UNPACK(C4ControlClientJoin) },
|
||||
{ CID_ClientUpdate, PC_Control, "Client Update", false, true, 0, PKT_UNPACK(C4ControlClientUpdate)},
|
||||
|
|
|
@ -169,6 +169,9 @@ enum C4PacketType
|
|||
|
||||
CID_DebugRec = CID_First | 0x40,
|
||||
CID_MenuCommand = CID_First | 0x41,
|
||||
|
||||
// Note: There are some more packet types in src/netpuncher/C4PuncherPacket.h
|
||||
// They have been picked to be distinct from these for safety, not for necessary.
|
||||
};
|
||||
|
||||
// packet classes
|
||||
|
@ -187,7 +190,7 @@ enum C4PacketHandlerID
|
|||
PH_C4Network2ClientList = 1 << 3, // client list class
|
||||
PH_C4Network2Players = 1 << 4, // player list class
|
||||
PH_C4Network2ResList = 1 << 5, // resource list class
|
||||
PH_C4GameControlNetwork = 1 << 6 // network control class
|
||||
PH_C4GameControlNetwork = 1 << 6, // network control class
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue