/*==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 . 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; }