engine/platformMacCarb/macCarbNet.cc
2024-01-07 04:36:33 +00:00

983 lines
27 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
//!!!!!!!!! NOTE !!!!!!!!!
// Since the platform layers are currently separate, the Mac implementation takes
// extreme advantage of knowing that all values are already in 'network order', and
// that a byte array of an ip address is the same byte order as a U32 ip address.
// dc - need to inlucde OT stuff first, as V12 does some nasty stuff with redeclaring
// base new() implementation to take filename/linenum, and screws up the OT hdrs.
#include <OpenTransport.h>
#if __APPLE__
#include <OpenTransportProtocol.h>
#include <OpenTransportProviders.h>
#define IP_BROADCAST kIP_BROADCAST
#define IP_REUSEADDR kIP_REUSEADDR
#else
#include <OpenTptInternet.h>
#endif
#include "PlatformMacCarb/platformMacCarb.h"
#include "Platform/platform.h"
#include "Platform/event.h"
#include "console/console.h"
#include "Platform/gameInterface.h"
#include "Core/fileStream.h"
struct Connection
{
NetSocket socket;
S32 state;
S32 prevState;
bool listenConnection;
Connection *nextConnection;
Connection *nextInTable;
// HANDLE connectThreadHandle;
U32 tag;
};
struct Status
{
bool mSignal;
OSErr mErr;
void *mData;
Status()
{
mSignal = false;
mErr = kOTNoError;
mData = NULL;
}
};
// main control vars.
static Net::Error getLastError();
static S32 defaultPort = 28000;
static S32 netPort = 0;
static bool sendPackets = true;
static U32 nextConnectionId = 1;
static U32 nextAcceptId = 1000000000;
static Connection *connectionList = NULL;
enum {
ConnectionTableSize = 256,
ConnectionTableMask = 0xFF
};
static Connection *connectionTable[ConnectionTableSize] = { 0, };
// OpenTransport init controls.
static U32 OTReferenceCount = 0;
//======================================================================
// NEW MAC GLOBALS
//======================================================================
// structures we want/need to hang around.
static EndpointRef mainEndpoint = NULL;
static InetAddress mainPortAddr, destPortAddr;
static TBind tbRequest, tbBound;
static TUnitData tSend, tRecv;
static OTFlags tFlags = 0L;
// error holders.
static OSErr macErr = kOTNoError;
static const OSStatus kStatusFine = 0L;
static OSStatus macStatus = kStatusFine;
static OTResult macResult = kOTNoError;
#define HOSTFROMBYTES(a) (*((InetHost*)(a)))
static const InetHost broadcastIP = 0xFFFFFFFF;
static const InetHost loopbackIP = 0x7F000001; // 127.0.0.1
#if V12_DEBUG
static InetHost testloop = 0;
static U8 test2[4];
#endif
//======================================================================
static pascal void EventProc( void *_pSession, OTEventCode event, OTResult result, void *cookie )
{
Status *status = (Status *) _pSession;
switch ( event )
{
case T_DATA:
//pSess->m_dataToRead = 1;
break;
case T_BINDCOMPLETE:
status->mErr = (OSErr) result;
status->mSignal = true;
break;
case T_OPENCOMPLETE:
status->mErr = (OSErr) result;
status->mData = cookie; // the EndPoint
status->mSignal = true;
break;
}
}
/*
//======================================================================
void Net::setJournaling(bool jrn)
{
sendPackets = !jrn;
}
*/
//======================================================================
bool Net::init()
{
if ( OTReferenceCount == 0 )
if (InitOpenTransport() != kOTNoError)
return false;
#if V12_DEBUG // this just validates the byte ordering of host ips in a byte array.
OTInetStringToHost("127.0.2.1", &testloop);
OTInetStringToHost("127.0.2.1", (InetHost*)test2);
#endif
// successfully opened Open Transport, inncrement the reference count
OTReferenceCount++;
return true;
}
//======================================================================
void Net::shutdown()
{
while(connectionList)
Net::closeConnectTo(connectionList->tag);
// make sure that we shut down the main open port.
closePort();
if ( OTReferenceCount <= 0 )
return;
// close Open Transport if there are no more references to it
OTReferenceCount--;
if ( OTReferenceCount == 0 )
CloseOpenTransport();
}
//======================================================================
static void netToIPSocketAddress(const NetAddress *address, InetAddress *inAddr)
{
dMemset(inAddr, 0, sizeof(InetAddress));
inAddr->fAddressType = AF_INET;
inAddr->fHost = HOSTFROMBYTES(address->netNum);
inAddr->fPort = address->port;
}
//======================================================================
static void IPSocketToNetAddress(const InetAddress *inAddr, NetAddress *address)
{
address->type = NetAddress::IPAddress;
address->port = inAddr->fPort;
HOSTFROMBYTES(address->netNum) = inAddr->fHost;
}
//======================================================================
bool Net::openPort(S32 port)
{
// check if we have an endpoint already.
if (mainEndpoint != NULL) // just unbind it - no need to close & recreate...
OTUnbind(mainEndpoint);
else // need to create a new endpoint.
{
mainEndpoint = OTOpenEndpoint(OTCreateConfiguration(kUDPName), 0L, NULL, &macStatus);
if (mainEndpoint == NULL)
{
// !!!!!!TBD add error case.
// macStatus may need to be converted...
return false;
}
}
// then, bind the endpoint to the specified address.
// first, clear the bind value struct.
tbBound.qlen = 0;
tbBound.addr.maxlen = 0;
tbBound.addr.len = 0;
tbBound.addr.buf = NULL;
// then, setup the bind request struct to point to a port addr struct.
tbRequest.qlen = 4;
tbRequest.addr.maxlen = sizeof(InetAddress);
tbRequest.addr.len = sizeof(InetAddress);
tbRequest.addr.buf = (U8*)(&mainPortAddr);
// then, fill the port address structure for the given port.
// this will bind to whichever net device it first finds. !!!!!TBD????
OTInitInetAddress(&mainPortAddr, port, kOTAnyInetAddress);
// then do the bind. this is sync, obv.
macStatus = OTBind(mainEndpoint, &tbRequest, &tbBound);
if (macStatus != kStatusFine)
{
return false;
}
// now that we're bound, need to 'negotiate' for capabilities.
U8 omBuffer[kOTFourByteOptionSize];
TOptMgmt omRequest;
TOptMgmt omReply;
// alias the buffer for cleaner structure access.
TOption *omOpts = (TOption*)omBuffer;
// set up the request and reply structures.
omRequest.flags = T_NEGOTIATE;
omRequest.opt.len = kOTFourByteOptionSize;
omRequest.opt.buf = omBuffer;
omReply.opt.maxlen = kOTFourByteOptionSize;
omReply.opt.buf = omBuffer;
// main thing we need is BROADCAST cap.
omOpts->len = kOTFourByteOptionSize;
omOpts->status = 0;
omOpts->level = INET_IP; // we're dealing with IP protocols.
omOpts->name = IP_BROADCAST; // we want BROADCAST.
*(U32*)(omOpts->value) = TRUE; // we want broadcast to be ENABLED.
macStatus = OTOptionManagement(mainEndpoint, &omRequest, &omReply);
if(macStatus != kStatusFine)
{
// need to look deeper to see what really happened.
if(omOpts->status != T_SUCCESS)
{
// !!!!!TBD convert/handle the error!
}
return false;
}
// we also want REUSE_ADDR
omOpts->len = kOTFourByteOptionSize;
omOpts->status = 0;
omOpts->level = INET_IP; // we're dealing with IP protocols.
omOpts->name = IP_REUSEADDR; // we want REUSE_ADDR.
*(U32*)(omOpts->value) = TRUE; // we want broadcast to be ENABLED.
macStatus = OTOptionManagement(mainEndpoint, &omRequest, &omReply);
if(macStatus != kStatusFine)
{
// need to look deeper to see what really happened.
if(omOpts->status != T_SUCCESS)
{
// !!!!!TBD convert/handle the error!
}
return false;
}
// we're ready. hold onto assigned portnum in a global...
netPort = port;
return true;
}
//======================================================================
void Net::closePort()
{
// check if we have an endpoint.
if (mainEndpoint != NULL)
{
// for now, unbind it
OTUnbind(mainEndpoint);
netPort = 0;
// we'll try this for nicer cleanup...
OTCloseProvider(mainEndpoint);
mainEndpoint = NULL;
}
}
//======================================================================
Net::Error Net::sendto(const NetAddress *address, const U8 *buffer, S32 bufferSize)
{
if (!sendPackets)
return NoError;
if (mainEndpoint == NULL)
return NoError; // !!!!!TBD????
// clear any send struct fields that should be clean...
tSend.opt.len = 0;
// setup the send buffer.
tSend.udata.maxlen = bufferSize;
tSend.udata.len = bufferSize;
tSend.udata.buf = (U8*)buffer;
// setup the dest addr struct.
tSend.addr.maxlen = sizeof(InetAddress);
tSend.addr.len = sizeof(InetAddress);
tSend.addr.buf = (U8*)(&mainPortAddr);
// fill in the dest address.
netToIPSocketAddress(address, &mainPortAddr);
// then send it.
macResult = OTSndUData(mainEndpoint, &tSend);
if(macResult < 0)
{
//!!!!!!TBD handle error.
return UnknownError;
}
return NoError;
}
//======================================================================
static PacketReceiveEvent receiveEvent; // why create on stack each time...
//======================================================================
void Net::process()
{
S32 bytesRead;
if (mainEndpoint==NULL)
return;
macResult = kOTNoError;
while(1)
{
bytesRead = 0L;
// clear any send struct fields that should be clean...
tRecv.opt.len = 0;
// setup the recv buffer to point to the receive event packet..
tRecv.udata.maxlen = MaxPacketDataSize;
tRecv.udata.len = MaxPacketDataSize;
tRecv.udata.buf = receiveEvent.data;
// setup the dest addr struct.
tRecv.addr.maxlen = sizeof(InetAddress);
tRecv.addr.len = sizeof(InetAddress);
tRecv.addr.buf = (U8*)(&destPortAddr);
tFlags = 0L;
macResult = OTRcvUData(mainEndpoint, &tRecv, &tFlags);
if (macResult == kOTNoError)
bytesRead = tRecv.udata.len;
else
if (macResult < 0)
{
if (macResult == kOTNoDataErr)
break; // clean return.
else
{
// !!!!TBD error handling.
break;
}
}
// else // !!!!TBD????? what to do with positive error?
// {
// }
IPSocketToNetAddress(&destPortAddr, &receiveEvent.sourceAddress);
// check for loopback.
NetAddress &na = receiveEvent.sourceAddress;
if (na.type == NetAddress::IPAddress &&
na.netNum[0] == 127 &&
na.netNum[1] == 0 &&
na.netNum[2] == 0 &&
na.netNum[3] == 1 &&
na.port == netPort)
continue;
// validate that we read something, else loop again.
if (bytesRead <= 0)
continue;
// post the received data.
receiveEvent.size = PacketReceiveEventHeaderSize + bytesRead;
Game->postEvent(receiveEvent);
}
// !!!!!!TBD
// may need to convert macResult into another variable
// for referencing error later on...
}
// THE NEXT SET OF METHODS ARE FOR HANDLING TCP-STREAM PORTS.
//======================================================================
//DWORD WINAPI connectThreadFunction(LPVOID param)
//{
// Connection *con = (Connection *) param;
// con;
// return 0;
//}
//======================================================================
static Connection *newConnection(U32 id)
{
Connection *conn = new Connection;
conn->nextConnection = connectionList;
conn->tag = id;
connectionList = conn;
conn->nextInTable = connectionTable[conn->tag & ConnectionTableMask];
connectionTable[conn->tag & ConnectionTableMask] = conn;
return conn;
}
//======================================================================
NetSocket Net::openListenPort(U16 port)
{
nextConnectionId++;
if(!sendPackets)
return nextConnectionId;
Connection *conn = newConnection(nextConnectionId);
conn->listenConnection = true;
conn->socket = openSocket();
bind(conn->socket, port);
listen(conn->socket, 4);
setBlocking(conn->socket, false);
return conn->tag;
}
//======================================================================
NetSocket Net::openConnectTo(const char *stringAddress)
{
nextConnectionId++;
if(!sendPackets)
return nextConnectionId;
Connection *conn = newConnection(nextConnectionId);
conn->listenConnection = false;
conn->prevState = ConnectedNotifyEvent::DNSResolved;
conn->state = ConnectedNotifyEvent::DNSResolved;
conn->socket = openSocket();
NetAddress netaddr;
connect(conn->socket, &netaddr);
setBlocking(conn->socket, false);
//threadHandle = CreateThread(NULL, 0, connectThreadFunction, (LPVOID) conn, 0, &threadId);
//CloseHandle(threadHandle);
conn->state = ConnectedNotifyEvent::Connected;
return conn->tag;
}
#if later
//======================================================================
void Net::processConnected()
{
Connection **walk = &connectionList;
Connection *con;
while((con = *walk) != NULL)
{
bool del = false;
if(con->listenConnection)
{
NetSocket newSocket;
ConnectedAcceptEvent event;
newSocket = accept(con->socket, &event.address);
if(newSocket != InvalidSocket)
{
Connection *nc = newConnection(nextAcceptId++);
nc->listenConnection = false;
nc->prevState = ConnectedNotifyEvent::Connected;
nc->state = ConnectedNotifyEvent::Connected;
nc->socket = newSocket;
setBlocking(nc->socket, false);
event.portTag = con->tag;
event.connectionTag = nc->tag;
Game->postEvent(event);
}
walk = &con->nextConnection;
}
else
{
if(con->state != con->prevState)
{
ConnectedNotifyEvent event;
event.tag = con->tag;
event.state = con->state;
Game->postEvent(event);
con->prevState = con->state;
}
if(con->state == ConnectedNotifyEvent::Connected)
{
ConnectedReceiveEvent event;
Net::Error err;
S32 bytesRead;
event.tag = con->tag;
do {
// err = recv(con->socket, event.data, MaxPacketDataSize, &bytesRead);
if(err == NoError && bytesRead != 0)
{
event.size = ConnectedReceiveEventHeaderSize + bytesRead;
Game->postEvent(event);
}
else if(err != WouldBlock)
{
// bad news... this disconnected
ConnectedNotifyEvent event;
event.tag = con->tag;
event.state = ConnectedNotifyEvent::Disconnected;
Game->postEvent(event);
del = true;
}
}
while(err == NoError && bytesRead != 0);
}
if(del)
{
*walk = con->nextConnection;
closeSocket(con->socket);
for(Connection **tbWalk = &connectionTable[con->tag & ConnectionTableMask]; *tbWalk != NULL; tbWalk = &(*tbWalk)->nextInTable)
{
Connection *dc = *tbWalk;
if(dc->tag == con->tag)
{
*tbWalk = dc->nextInTable;
break;
}
}
delete con;
}
else
walk = &con->nextConnection;
}
}
}
#endif
//======================================================================
void Net::closeConnectTo(NetSocket sock)
{
if(Game->isJournalReading())
return;
Connection **walk;
for(walk = &connectionList; *walk != NULL; walk = &(*walk)->nextConnection)
{
Connection *con = *walk;
if(con->tag == sock)
{
*walk = con->nextConnection;
closeSocket(con->socket);
break;
}
}
for(walk = &connectionTable[sock & ConnectionTableMask]; *walk != NULL; walk = &(*walk)->nextInTable)
{
Connection *con = *walk;
if(con->tag == sock)
{
*walk = con->nextInTable;
delete con;
return;
}
}
}
//======================================================================
Net::Error Net::sendtoSocket(NetSocket socket, const U8 *buffer, int bufferSize)
{
if(Game->isJournalReading())
{
U32 e;
Game->journalRead(&e);
return (Net::Error) e;
}
Net::Error e = send(socket, buffer, bufferSize);
if(Game->isJournalWriting())
Game->journalWrite(U32(e));
return e;
}
//======================================================================
NetSocket Net::openSocket()
{
return InvalidSocket;
#if later
SOCKET retSocket;
retSocket = socket(AF_INET, SOCK_STREAM, 0);
if(retSocket == INVALID_SOCKET)
return InvalidSocket;
else
return retSocket;
#endif
}
//======================================================================
Net::Error Net::closeSocket(NetSocket socket)
{
if(socket != InvalidSocket)
{
#if later
if(!closesocket(socket))
return NoError;
else
#endif
return getLastError();
}
else
return NotASocket;
}
//======================================================================
Net::Error Net::connect(NetSocket socket, const NetAddress *address)
{
if(address->type != NetAddress::IPAddress)
return WrongProtocolType;
#if later
SOCKADDR_IN socketAddress;
netToIPSocketAddress(address, &socketAddress);
if(!::connect(socket, (PSOCKADDR) &socketAddress, sizeof(socketAddress)))
return NoError;
#endif
return getLastError();
}
Net::Error Net::listen(NetSocket socket, S32 backlog)
{
#if later
if(!::listen(socket, backlog))
return NoError;
#endif
return getLastError();
}
//======================================================================
NetSocket Net::accept(NetSocket acceptSocket, NetAddress *remoteAddress)
{
#if later
SOCKADDR_IN socketAddress;
S32 addrLen = sizeof(socketAddress);
SOCKET retVal = ::accept(acceptSocket, (PSOCKADDR) &socketAddress, &addrLen);
if(retVal != INVALID_SOCKET)
{
IPSocketToNetAddress(&socketAddress, remoteAddress);
return retVal;
}
#endif
return InvalidSocket;
}
//======================================================================
Net::Error Net::bind(NetSocket socket, U16 port)
{
#if later
S32 error;
SOCKADDR_IN socketAddress;
dMemset((char *)&socketAddress, 0, sizeof(socketAddress));
socketAddress.sin_family = AF_INET;
// It's entirely possible that there are two NIC cards.
// We let the user specify which one the server runs on.
// thanks to [TPG]P1aGu3 for the name
const char* serverIP = Con::getVariable( "Host::BindAddress" );
// serverIP is guaranteed to be non-0.
AssertFatal( serverIP, "serverIP is NULL!" );
if( serverIP[0] != '\0' ) {
// we're not empty
socketAddress.sin_addr.s_addr = inet_addr( serverIP );
if( socketAddress.sin_addr.s_addr != INADDR_NONE ) {
Con::printf( "Binding server port to %s", serverIP );
} else {
Con::warnf( ConsoleLogEntry::General,
"inet_addr() failed for %s while binding!",
serverIP );
socketAddress.sin_addr.s_addr = INADDR_ANY;
}
} else {
Con::printf( "Binding server port to default IP" );
socketAddress.sin_addr.s_addr = INADDR_ANY;
}
socketAddress.sin_port = htons(port);
error = ::bind(socket, (PSOCKADDR) &socketAddress, sizeof(socketAddress));
if(!error)
return NoError;
#endif
return getLastError();
}
//======================================================================
Net::Error Net::setBufferSize(NetSocket socket, S32 bufferSize)
{
socket, bufferSize;
// S32 error;
// error = setsockopt(socket, SOL_SOCKET, SO_RCVBUF, (char *) &bufferSize, sizeof(bufferSize));
// if(!error)
// error = setsockopt(socket, SOL_SOCKET, SO_SNDBUF, (char *) &bufferSize, sizeof(bufferSize));
// if(!error)
// return NoError;
return getLastError();
}
//======================================================================
Net::Error Net::setBroadcast(NetSocket socket, bool broadcast)
{
socket, broadcast;
// S32 bc = broadcast;
// S32 error = setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (char*)&bc, sizeof(bc));
// if(!error)
// return NoError;
return getLastError();
}
//======================================================================
Net::Error Net::setBlocking(NetSocket socket, bool blockingIO)
{
socket, blockingIO;
// DWORD notblock = !blockingIO;
// S32 error = ioctlsocket(socket, FIONBIO, &notblock);
// if(!error)
// return NoError;
return getLastError();
}
//======================================================================
Net::Error Net::send(NetSocket socket, const U8 *buffer, S32 bufferSize)
{
Net::Error e;
// e = ::send(socket, (const char*)buffer, bufferSize, 0);
return getLastError();
// return e;
}
//======================================================================
Net::Error Net::recv(NetSocket socket, U8 *buffer, S32 bufferSize, S32 *bytesRead)
{
socket, buffer, bufferSize, bytesRead;
// *bytesRead = ::recv(socket, (char*)buffer, bufferSize, 0);
// if(*bytesRead == SOCKET_ERROR)
// return getLastError();
return NoError;
}
//======================================================================
bool Net::compareAddresses(const NetAddress *a1, const NetAddress *a2)
{
if(a1->type != a2->type)
return false;
if(*((U32 *)a1->netNum) != *((U32 *)a2->netNum))
return false;
/* this was for IPX only. !!!!tbd
if(a1->type == NetAddress::IPAddress)
return true;
for(S32 i = 0; i < 6; i++)
if(a1->nodeNum[i] != a2->nodeNum[i])
return false;
*/
return true;
}
//======================================================================
bool Net::stringToAddress(const char *addressString, NetAddress *address)
{
char sRemote[256]; // temporary space so we can muck with the inString.
char *sPort;
// clear destination.
dMemset((void*)address, 0, sizeof(NetAddress));
// assume IP if it doesn't have ipx: at the front.
if (dStrnicmp(addressString, "ipx:", 4))
{
if (!dStrnicmp(addressString, "ip:", 3))
addressString += 3; // eat off the ip:
if (dStrlen(addressString) > 255) // waaay too long.
return false;
dStrcpy(sRemote, addressString);
sPort = dStrchr(sRemote, ':');
if (sPort) // if non-null...
*sPort++ = 0; // null the : and set ahead past the :
// first, look for broadcast.
if(!dStricmp(sRemote, "broadcast"))
{
*((InetHost*)address->netNum) = broadcastIP;
}
else
{
// try to do a simple string convert, in hopes it's a dotted-numeral address.
OTInetStringToHost(sRemote, ((InetHost*)(address->netNum)));
// if host numbers are all zero, safe bet that we need to do DNS lookup...
if(*((InetHost*)address->netNum) == 0)
{
/* THIS WAS OLD CODE... NEED TO VERIFY OR REWRITE!!!!TBD
U8 dnsBuffer[1024];
TLookupRequest dnsRequest;
TLookupReply dnsReply;
MapperRef dns;
// create a mapper for DNS lookup
dns = OTOpenMapper(OTCreateConfiguration(kDNRName), 0, &macStatus);
// set the mapper to be sync and blocking, so we don't need callbacks
// !!!!TBD change to async handling of this!
if (macStatus == kOTNoError)
oterr = OTSetSynchronous(dns);
if (macStatus == kOTNoError)
oterr = OTSetBlocking(dns);
// then, if we haven't yet hit an error, do the DNS lookup.
if (macStatus == kOTNoError)
{
dMemset((void*)&dnsRequest, 0, sizeof(TLookupRequest));
dnsRequest.maxcnt = 1; // just looking for a single IP
dnsRequest.timeout = 5000; // # ms before we timeout.
dnsRequest.name.buf = sRemote;
dnsRequest.name.len = dStrlen(sRemote);
dMemset((char *)&dnsReply, 0, sizeof(TLookupReply));
dnsReply.names.maxlen = 1024;
dnsReply.names.buf = dnsBuffer;
macStatus = OTLookupName(dns, &dnsRequest, &dnsReply);
if (macStatus == kOTNoError)
{
if (!dnsReply.rspcount)
{
// !!!!TBD handle error of not found. hopefully, macStatus was set already.
}
else // copy over the result.
{
// address->netNumIP = ((InetAddress*)(((TLookupBuffer*)dnsBuffer)->fAddressBuffer));
}
}
}
// HERE we handle ALL drop through error cases.
// this way, it's handled in one place, and cleanup
// is simpler as well...
if (macStatus != kOTNoError)
{
//!!!!TBD -- handle the error by setting global lookup val.
}
// close down dns if valid.
if (dns != kOTInvalidMapperRef)
{
macStatus = OTCloseProvider(dns);
if (macStatus != kOTNoError)
{
// !!!! NOT SURE. we don't want to necessarily blow away
// the failure above with this one.
//!!!!TBD -- handle the error by setting global lookup val.
}
}
*/
// if((hp = gethostbyname(remoteAddr)) == NULL)
return false;
}
}
if (sPort)
address->port = dAtoi(sPort);
else
address->port = defaultPort;
address->type = NetAddress::IPAddress;
return true;
}
return false;
}
//======================================================================
void Net::addressToString(const NetAddress *address, char addressString[256])
{
addressString[0] = 0; // clear string...
if(address->type == NetAddress::IPAddress)
{
if (HOSTFROMBYTES(address->netNum) == broadcastIP)
dSprintf(addressString, 256, "IP:Broadcast:%d", address->port);
else
dSprintf(addressString, 256, "IP:%d.%d.%d.%d:%d",
address->netNum[0], address->netNum[1], address->netNum[2], address->netNum[3],
address->port);
}
}
//======================================================================
Net::Error getLastError()
{
return Net::UnknownError;
// S32 err = WSAGetLastError();
// switch(err)
// {
// case WSAEWOULDBLOCK:
// return Net::WouldBlock;
// default:
// return Net::UnknownError;
// }
}