You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

591 lines
16 KiB

/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
/*****************************************************************************
*
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/pnAceIo.cpp
*
***/
#include "Pch.h"
#pragma hdrstop
/****************************************************************************
*
* ISocketConnHash
*
***/
// socket notification procedures
// connection data format:
// byte connType;
// dword buildId; [optional]
// dword branchId; [optional]
// dword buildType; [optional]
// Uuid productId; [optional]
const unsigned kConnHashFlagsIgnore = 0x01;
const unsigned kConnHashFlagsExactMatch = 0x02;
struct ISocketConnHash {
unsigned connType;
unsigned buildId;
unsigned buildType;
unsigned branchId;
Uuid productId;
unsigned flags;
unsigned GetHash () const;
bool operator== (const ISocketConnHash & rhs) const;
};
struct ISocketConnType : ISocketConnHash {
HASHLINK(ISocketConnType) hashlink;
FAsyncNotifySocketProc notifyProc;
};
static CLock s_notifyProcLock;
static HASHTABLEDECL(
ISocketConnType,
ISocketConnHash,
hashlink
) s_notifyProcs;
//===========================================================================
unsigned ISocketConnHash::GetHash () const {
CHashValue hash;
hash.Hash32(connType);
/*
if (buildId)
hash.Hash32(buildId);
if (buildType)
hash.Hash32(buildType);
if (branchId)
hash.Hash32(branchId);
if (productId != kNilGuid)
hash.Hash(&productId, sizeof(productId));
*/
return hash.GetHash();
}
//===========================================================================
bool ISocketConnHash::operator== (const ISocketConnHash & rhs) const {
ASSERT(flags & kConnHashFlagsIgnore);
for (;;) {
// Check connType
if (connType != rhs.connType)
break;
// Check buildId
if (buildId != rhs.buildId) {
if (rhs.flags & kConnHashFlagsExactMatch)
break;
if (buildId)
break;
}
// Check buildType
if (buildType != rhs.buildType) {
if (rhs.flags & kConnHashFlagsExactMatch)
break;
if (buildType)
break;
}
// Check branchId
if (branchId != rhs.branchId) {
if (rhs.flags & kConnHashFlagsExactMatch)
break;
if (branchId)
break;
}
// Check productId
if (productId != rhs.productId) {
if (rhs.flags & kConnHashFlagsExactMatch)
break;
if (productId != kNilGuid)
break;
}
// Success!
return true;
}
// Failed!
return false;
}
//===========================================================================
static unsigned GetConnHash (
ISocketConnHash * hash,
const byte buffer[],
unsigned bytes
) {
if (!bytes)
return 0;
if (IS_TEXT_CONNTYPE(buffer[0])) {
hash->connType = buffer[0];
hash->buildId = 0;
hash->buildType = 0;
hash->branchId = 0;
hash->productId = 0;
hash->flags = 0;
// one byte consumed
return 1;
}
else {
if (bytes < sizeof(AsyncSocketConnectPacket))
return 0;
const AsyncSocketConnectPacket & connect = * (const AsyncSocketConnectPacket *) buffer;
if (connect.hdrBytes < sizeof(connect))
return 0;
hash->connType = connect.connType;
hash->buildId = connect.buildId;
hash->buildType = connect.buildType;
hash->branchId = connect.branchId;
hash->productId = connect.productId;
hash->flags = 0;
return connect.hdrBytes;
}
}
/****************************************************************************
*
* Public exports
*
***/
//===========================================================================
EFileError AsyncGetLastFileError () {
const unsigned error = GetLastError();
switch (error) {
case NO_ERROR:
return kFileSuccess;
case ERROR_FILE_NOT_FOUND:
return kFileErrorFileNotFound;
case ERROR_ACCESS_DENIED:
case ERROR_FILE_EXISTS:
case ERROR_ALREADY_EXISTS:
return kFileErrorAccessDenied;
case ERROR_SHARING_VIOLATION:
return kFileErrorSharingViolation;
case ERROR_BAD_NETPATH:
case ERROR_PATH_NOT_FOUND:
case ERROR_INVALID_NAME:
case ERROR_BAD_NET_NAME:
case ERROR_CANT_ACCESS_DOMAIN_INFO:
case ERROR_NETWORK_UNREACHABLE:
case ERROR_HOST_UNREACHABLE:
return kFileErrorPathNotFound;
}
LogMsg(kLogPerf, "Unexpected Win32 error [%#x]", error);
return kFileErrorPathNotFound;
}
//============================================================================
const wchar * FileErrorToString (EFileError error) {
static wchar * s_fileErrorStrings[] = {
L"FileSuccess",
L"FileErrorInvalidParameter",
L"FileErrorFileNotFound",
L"FileErrorPathNotFound",
L"FileErrorAccessDenied",
L"FileErrorSharingViolation",
};
COMPILER_ASSERT(kNumFileErrors == arrsize(s_fileErrorStrings));
return s_fileErrorStrings[error];
}
//============================================================================
AsyncFile AsyncFileOpen (
const wchar fullPath[],
FAsyncNotifyFileProc notifyProc,
EFileError * error,
unsigned desiredAccess,
unsigned openMode,
unsigned shareModeFlags,
void * userState,
qword * fileSize,
qword * fileLastWriteTime
) {
ASSERT(g_api.fileOpen);
return g_api.fileOpen(
fullPath,
notifyProc,
error,
desiredAccess,
openMode,
shareModeFlags,
userState,
fileSize,
fileLastWriteTime
);
}
//============================================================================
void AsyncFileClose (
AsyncFile file,
qword truncateSize
) {
ASSERT(g_api.fileClose);
g_api.fileClose(file, truncateSize);
}
//============================================================================
void AsyncFileSetLastWriteTime (
AsyncFile file,
qword lastWriteTime
) {
ASSERT(g_api.fileSetLastWriteTime);
g_api.fileSetLastWriteTime(file, lastWriteTime);
}
//============================================================================
qword AsyncFileGetLastWriteTime (
const wchar fileName[]
) {
ASSERT(g_api.fileGetLastWriteTime);
return g_api.fileGetLastWriteTime(fileName);
}
//============================================================================
AsyncId AsyncFileFlushBuffers (
AsyncFile file,
qword truncateSize,
bool notify,
void * param
) {
ASSERT(g_api.fileFlushBuffers);
return g_api.fileFlushBuffers(file, truncateSize, notify, param);
}
//============================================================================
AsyncId AsyncFileRead (
AsyncFile file,
qword offset,
void * buffer,
unsigned bytes,
unsigned flags,
void * param
) {
ASSERT(g_api.fileRead);
return g_api.fileRead(
file,
offset,
buffer,
bytes,
flags,
param
);
}
//============================================================================
AsyncId AsyncFileWrite (
AsyncFile file,
qword offset,
const void * buffer,
unsigned bytes,
unsigned flags,
void * param
) {
ASSERT(g_api.fileWrite);
return g_api.fileWrite(
file,
offset,
buffer,
bytes,
flags,
param
);
}
//============================================================================
AsyncId AsyncFileCreateSequence (
AsyncFile file,
bool notify,
void * param
) {
ASSERT(g_api.fileCreateSequence);
return g_api.fileCreateSequence(file, notify, param);
}
//============================================================================
bool AsyncFileSeek (
AsyncFile file,
qword distance,
EFileSeekFrom seekFrom
) {
ASSERT(g_api.fileSeek);
return g_api.fileSeek(file, distance, seekFrom);
}
//===========================================================================
void AsyncSocketConnect (
AsyncCancelId * cancelId,
const NetAddress & netAddr,
FAsyncNotifySocketProc notifyProc,
void * param,
const void * sendData,
unsigned sendBytes,
unsigned connectMs,
unsigned localPort
) {
ASSERT(g_api.socketConnect);
g_api.socketConnect(
cancelId,
netAddr,
notifyProc,
param,
sendData,
sendBytes,
connectMs,
localPort
);
}
//===========================================================================
void AsyncSocketConnectCancel (
FAsyncNotifySocketProc notifyProc,
AsyncCancelId cancelId
) {
ASSERT(g_api.socketConnectCancel);
g_api.socketConnectCancel(notifyProc, cancelId);
}
//===========================================================================
void AsyncSocketDisconnect (
AsyncSocket sock,
bool hardClose
) {
ASSERT(g_api.socketDisconnect);
g_api.socketDisconnect(sock, hardClose);
}
//===========================================================================
void AsyncSocketDelete (AsyncSocket sock) {
ASSERT(g_api.socketDelete);
g_api.socketDelete(sock);
}
//===========================================================================
bool AsyncSocketSend (
AsyncSocket sock,
const void * data,
unsigned bytes
) {
ASSERT(g_api.socketSend);
return g_api.socketSend(sock, data, bytes);
}
//===========================================================================
bool AsyncSocketWrite (
AsyncSocket sock,
const void * buffer,
unsigned bytes,
void * param
) {
ASSERT(g_api.socketWrite);
return g_api.socketWrite(sock, buffer, bytes, param);
}
//===========================================================================
void AsyncSocketSetNotifyProc (
AsyncSocket sock,
FAsyncNotifySocketProc notifyProc
) {
ASSERT(g_api.socketSetNotifyProc);
g_api.socketSetNotifyProc(sock, notifyProc);
}
//===========================================================================
void AsyncSocketSetBacklogAlloc (
AsyncSocket sock,
unsigned bufferSize
) {
ASSERT(g_api.socketSetBacklogAlloc);
g_api.socketSetBacklogAlloc(sock, bufferSize);
}
//===========================================================================
unsigned AsyncSocketStartListening (
const NetAddress & listenAddr,
FAsyncNotifySocketProc notifyProc
) {
ASSERT(g_api.socketStartListening);
return g_api.socketStartListening(listenAddr, notifyProc);
}
//===========================================================================
void AsyncSocketStopListening (
const NetAddress & listenAddr,
FAsyncNotifySocketProc notifyProc
) {
ASSERT(g_api.socketStopListening);
g_api.socketStopListening(listenAddr, notifyProc);
}
//============================================================================
void AsyncSocketEnableNagling (
AsyncSocket sock,
bool enable
) {
ASSERT(g_api.socketEnableNagling);
g_api.socketEnableNagling(sock, enable);
}
//===========================================================================
void AsyncSocketRegisterNotifyProc (
byte connType,
FAsyncNotifySocketProc notifyProc,
unsigned buildId,
unsigned buildType,
unsigned branchId,
const Uuid & productId
) {
ASSERT(connType != kConnTypeNil);
ASSERT(notifyProc);
// Perform memory allocation outside lock
ISocketConnType * ct = NEW(ISocketConnType);
ct->notifyProc = notifyProc;
ct->connType = connType;
ct->buildId = buildId;
ct->buildType = buildType;
ct->branchId = branchId;
ct->productId = productId;
ct->flags = kConnHashFlagsIgnore;
s_notifyProcLock.EnterWrite();
{
s_notifyProcs.Add(ct);
}
s_notifyProcLock.LeaveWrite();
}
//===========================================================================
void AsyncSocketUnregisterNotifyProc (
byte connType,
FAsyncNotifySocketProc notifyProc,
unsigned buildId,
unsigned buildType,
unsigned branchId,
const Uuid & productId
) {
ISocketConnHash hash;
hash.connType = connType;
hash.buildId = buildId;
hash.buildType = buildType;
hash.branchId = branchId;
hash.productId = productId;
hash.flags = kConnHashFlagsExactMatch;
ISocketConnType * scan;
s_notifyProcLock.EnterWrite();
{
scan = s_notifyProcs.Find(hash);
for (; scan; scan = s_notifyProcs.FindNext(hash, scan)) {
if (scan->notifyProc != notifyProc)
continue;
// Unlink the object so it can be deleted outside the lock
s_notifyProcs.Unlink(scan);
break;
}
}
s_notifyProcLock.LeaveWrite();
// perform memory deallocation outside the lock
DEL(scan);
}
//===========================================================================
FAsyncNotifySocketProc AsyncSocketFindNotifyProc (
const byte buffer[],
unsigned bytes,
unsigned * bytesProcessed,
unsigned * connType,
unsigned * buildId,
unsigned * buildType,
unsigned * branchId,
Uuid * productId
) {
for (;;) {
// Get the connType
ISocketConnHash hash;
*bytesProcessed = GetConnHash(&hash, buffer, bytes);
if (!*bytesProcessed)
break;
// Lookup notifyProc based on connType
FAsyncNotifySocketProc proc;
s_notifyProcLock.EnterRead();
if (const ISocketConnType * scan = s_notifyProcs.Find(hash))
proc = scan->notifyProc;
else
proc = nil;
s_notifyProcLock.LeaveRead();
if (!proc)
break;
// Success!
*connType = hash.connType;
*buildId = hash.buildId;
*buildType = hash.buildType;
*branchId = hash.branchId;
*productId = hash.productId;
return proc;
}
// Failure!
PerfAddCounter(kAsyncPerfSocketDisconnectInvalidConnType, 1);
*bytesProcessed = 0;
*connType = 0;
*buildId = 0;
*buildType = 0;
*branchId = 0;
*productId = 0;
return nil;
}