void Network_Manager::Initialize() { logWrite("Initializing networking..."); Available = true; #ifdef _WIN32 WSADATA wsaData; WORD wVersionRequested = MAKEWORD(2, 0); int result = WSAStartup(wVersionRequested, &wsaData); if (result != 0) { logWrite("Win32 networking failed (error %d)",result); Available = false; } else { logWritem("network: %s",wsaData.szDescription); } #else logWritem("network: unix sockets"); #endif Connections.Preallocate(256); maxMessageSize = 1024; //Convars cvMaxMessageSize = Convar.Create("net_maxsize",cvMaxMessageSize_Read,cvMaxMessageSize_Write); cvMaxConnections = Convar.Create("net_maxconnections",cvMaxConnections_Read,cvMaxConnections_Write); cvSendQueueSize = Convar.Create("net_send_queue",128); cvTimeRate = Convar.Create("net_timerate",0.1f); cvFakeLag = Convar.Create("net_fakelag",0.0f); remainingTimeSyncMsgs = 0; prevTimeSyncMessageTime = 0; IsServer = false; IsConnected = false; networkCallback[NETMESSAGE_SERVERINFO] = netCallback_SERVERINFO; networkCallback[NETMESSAGE_TIMESYNC] = netCallback_TIMESYNC; //Initialize stats numBytesSent = 0; numBytesReceived = 0; numPacketsSent = 0; numPacketsReceived = 0; prevTimeSyncTick = 0; msgSendLock = 0; };
int Network_Connection::Send(void* buf, int size) { if (socketHandle >= 0) { int bytes = send(socketHandle, (char*)buf, size, 0); if (bytes < 0) { #ifdef WIN32 logWritem("network: socket write error %d, dropping (socket: %d)", GetLastError(), socketHandle); #else logWritem("network: socket write error %d, dropping (socket: %d)", errno, socketHandle); #endif Close(); return 0; } Network.numBytesSent += bytes; return bytes; } return 0; }
void Map_Manager::cellUncache(int cx, int cy) { LockID lockID = Thread.EnterLock(MUTEX_MAP); int idx = cellCacheIndex(cx,cy); if (idx == BAD_ID) { logWritem("cellUncache: map [%d;%d] is not cached",cx,cy); Thread.LeaveLock(lockID); return; } releaseMap(*cacheCityscape[idx]); cacheCityscape.Count--; if (cacheCityscape.Count > 0) { *cacheCityscape[idx] = *cacheCityscape[cacheCityscape.Count]; } logWritem("cellUncache: map [%d;%d] removed from cache",cx,cy); Thread.LeaveLock(lockID); }
AnimSeqID Animation_Manager::GetAnimationSeq(const char* name) { int nameLen = strlen(name); for (uint i = 0; i < Entries.Count; i++) { if (strncmp(Entries[i]->Name,name,nameLen) == 0) return i; } logWritem("Animation not found: %s",name); return BAD_ID; }
void Animation_Manager::LoadFromChunk(Chunk_Loader* TEX) { if (!TEX->IsChunk("ANIM")) return; if (!TEX->IsEndOfChunk()) { logWritem("Loading animations..."); } else { logWritem("Warning: animation chunk is empty"); } while (!TEX->IsEndOfChunk()) { char sprStartName[256]; char sprEndName[256]; char animName[256]; float animFPS; char animType; TEX->ReadString(animName); TEX->Read(&animType,1); logWritem("\t%s [type: %.2x]",animName,animType); animation_entry* anim = Animations.Entries.Add(); anim->Type = animType; anim->Name = mem.alloc_string(strlen(animName)+1); strcpy(anim->Name,animName); switch (animType) { // case ANIMTYPE_FORWARDLOOP: case ANIMTYPE_FORWARDONCE: TEX->ReadString(sprStartName); TEX->ReadString(sprEndName); TEX->Read(&animFPS,4); anim->FPS = animFPS; anim->Sprites = (TexID*)mem.alloc(2*sizeof(TexID)); anim->Sprites[0] = Graphics.GetTextureID(sprStartName); anim->Sprites[1] = Graphics.GetTextureID(sprEndName); anim->numSprites = 2; break; default: logError("Animation: unsupported type %d",animType); return; } } }
void debugTestDebug() { logWrite("Testing debugging..."); logError("This line should trigger a breakpoint"); assert(true); assert(false); logWrite("Log entry"); logWritem("Log debug entry"); logWrite("Debug test passed!\n"); }
TexID Graphics_Manager::GetTextureID(const char* name) { int nameLen = strlen(name); if (nameLen == 0) return BAD_ID; for (uint i = 0; i < graphicsEntries.Count; i++) { for (uint j = 0; j < graphicsEntries[i]->TextureAtlas->Textures.Count; j++) { if (strcmp(name,graphicsEntries[i]->TextureAtlas->Textures[j]->textureName) == 0) { return graphicsEntries[i]->TextureAtlas->FirstID + j; } } } logWritem("Sprite not found: %s",name); return BAD_ID; }
int Network_Connection::Recv(void* buf, int size) { if (socketHandle >= 0) { if (!canReceive()) return 0; #ifdef WIN32 int bytes = recv(socketHandle, (char*)buf, size, 0); #else int bytes = recv(socketHandle, buf, size, MSG_NOSIGNAL | MSG_DONTWAIT); #endif if (bytes <= 0) { //FIXME: proper error handling #ifdef WIN32 logWritem("network: socket read error %d, dropping (socket: %d)", GetLastError(), socketHandle); #else logWritem("network: socket read error %d, dropping (socket: %d)", errno, socketHandle); #endif bytes = 0; Close(); } Network.numBytesReceived += bytes; return bytes; } else return -1; }
void Map_Render_Debug::Initialize() { //glShadeModel(GL_SMOOTH); //glEnable(GL_LIGHTING); //glEnable(GL_NORMALIZE); //glEnable(GL_TEXTURE_2D); //glEnable(GL_CULL_FACE); nextAnimationTime = 0.0f; VBOEntry.Preallocate(4); for (uint i = 0; i < VBOEntry.AllocCount; i++) { logWritem("creating vertex buffer %d",i); VBOEntry[i]->VBO.Create(32768*3);//131072);//0); } //FIXME: VBOs take A LOT of space }
void Network_Connection::Update() { if (listenConnection) { struct sockaddr_in fromAddress; memset(&fromAddress, 0, sizeof(fromAddress)); #ifdef _WIN32 int fromLen = sizeof(fromAddress); #else socklen_t fromLen = sizeof(fromAddress); #endif if (canReceive()) { int clientHandle = accept(socketHandle, (struct sockaddr*)&fromAddress, &fromLen); if (clientHandle < 0) return; Network_Connection* clientConnection = Network.NewConnection(); if (!clientConnection) return; clientConnection->Open(0,0); //Initialize resources clientConnection->peerAddress = fromAddress; clientConnection->socketHandle = clientHandle; #ifdef WIN32 logWritem("network: incoming connection %.8X:%d (socket: %d, cid %d)", fromAddress.sin_addr.S_un.S_addr, htons(fromAddress.sin_port), clientHandle, Network.Connections.Count - 1); #else logWritem("network: incoming connection %.8X:%d (socket: %d, cid %d)", fromAddress.sin_addr.s_addr, htons(fromAddress.sin_port), clientHandle, Network.Connections.Count - 1); #endif } } else { LockID lockID = Thread.EnterLock(MUTEX_NETWORK_RECVBUF); //A slightly hacky message receiver //msgSize is set to how big message currently should be //addSize is set after first byte was fetched, and says how much extra data needs to be fetched // before msgSize will be reset from 1 (default) to actual message size (excluding additional data) int readSize = 3; while (canReceive() || (recvMessage.rawSize >= readSize)) { if (recvMessage.rawSize > 0) { //Message already carries message ID unsigned short msgSize = *(unsigned short*)((char*)recvMessage.rawBuffer+0); unsigned char msgID = *((char*)recvMessage.rawBuffer+2); if (msgSize > Network.maxMessageSize) msgSize = Network.maxMessageSize; readSize = msgSize; if (readSize == 0) return; //FIXME if (recvMessage.rawSize >= readSize) { if ((msgID < NETMESSAGE_LAST) && (Network.networkCallback[msgID])) { Network.numPacketsReceived++; recvMessage.rawPos = 3; //Skip message ID and size Network.networkCallback[msgID](this,&recvMessage); recvMessage.rawSize = 0; readSize = 3; //get ready to recv more messages } else { //Unhandled message, ignore recvMessage.rawSize = 0; } } } //read more bytes to the message if (recvMessage.rawSize < readSize) { recvMessage.rawSize += Recv((recvMessage.rawBuffer+recvMessage.rawSize),readSize-recvMessage.rawSize); } } Thread.LeaveLock(lockID); } }
bool Network_Connection::Open(char const* host, int port) { if (socketHandle >= 0) Close(); LockID lockID1 = Thread.EnterLock(MUTEX_NETWORK_SENDBUF); LockID lockID2 = Thread.EnterLock(MUTEX_NETWORK_RECVBUF); sendQueue.Preallocate(Convar[Network.cvSendQueueSize]->GetInt()); //FIXME: message size in convar char* msgBuffer = (char*)mem.alloc(Network.maxMessageSize*(sendQueue.AllocCount+1)); for (uint i = 0; i < sendQueue.AllocCount; i++) { sendQueue[i]->rawBuffer = (msgBuffer + Network.maxMessageSize*i); sendQueue[i]->rawSize = 0; sendQueue[i]->networkConnection = 0; } recvMessage.rawBuffer = (msgBuffer + Network.maxMessageSize*sendQueue.AllocCount); recvMessage.rawSize = 0; recvMessage.rawPos = 0; recvMessage.networkConnection = 0; connectionOpen = true; //Only initialize internals if ((!host) && (!port)) { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); return true; } peerAddress.sin_family = AF_INET; peerAddress.sin_port = htons(port); if (host) { peerAddress.sin_addr.s_addr = inet_addr(host); if (peerAddress.sin_addr.s_addr == (unsigned int)-1) { //struct hostent *he = gethostbyname(host); ///if (he) { // peerAddress.sin_addr.s_addr = ((struct in_addr*)*he->h_addr_list)->s_addr; //} else { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); Close(); return false; //} } } else { peerAddress.sin_addr.s_addr = htonl(INADDR_ANY); } socketHandle = socket(AF_INET, SOCK_STREAM, 0); if (socketHandle < 0) { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); Close(); return false; } if (!host) { int one = 1; setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)); if (bind(socketHandle, (struct sockaddr*)&peerAddress, sizeof(peerAddress)) < 0) { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); Close(); return false; } if (listen(socketHandle, 20) < 0) { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); Close(); return false; } #ifdef WIN32 logWritem("network: starting server on %.8x:%d (backlog %d)", peerAddress.sin_addr.S_un.S_addr, htons(peerAddress.sin_port), 20); #else logWritem("network: starting server on %.8x:%d (backlog %d)", peerAddress.sin_addr.s_addr, htons(peerAddress.sin_port), 20); #endif listenConnection = true; } else { if (connect(socketHandle, (struct sockaddr*)&peerAddress, sizeof(peerAddress)) < 0) { Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); Close(); return false; } listenConnection = false; } #ifdef WIN32 unsigned long int nonblock = 1; ioctlsocket(socketHandle, FIONBIO, &nonblock); #else // XXX Locks aren't released o_O int flags = fcntl(socketHandle, F_GETFL, 0); if (0 > flags) { logWritem("Unexpected condition in %s:%d", __FILE__, __LINE__); return false; } if (0 > fcntl(socketHandle, F_SETFL, flags | O_NONBLOCK)) { logWritem("Unexpected condition in %s:%d", __FILE__, __LINE__); return false; } #endif Thread.LeaveLock(lockID1); Thread.LeaveLock(lockID2); return true; }
void Graphics_Manager::texReadFonts(Chunk_Loader* TEX, bool scanOnly) { if (TEX->IsChunk("FNT0")) { unsigned short fontCount; TEX->Read(&fontCount,2); //Read graphics name (for font GFX) for (int f = 0; f < fontCount; f++) { char fontName[256]; unsigned short fontCharCount; //Read font name TEX->ReadString(fontName); //Make new font entry (or append to existing one) font_entry* font = 0; bool newEntry = false; for (uint i = 0; i < Fonts.fontEntries.Count; i++) { if (strncmp(Fonts.fontEntries[i]->FontName,fontName,strlen(fontName)) == 0) { font = Fonts.fontEntries[i]; break; } } if (!font) { //Do we need to add a new entry? font = Fonts.fontEntries.Add(); font->FontName = mem.alloc_string(strlen(fontName)+1); strcpy(font->FontName,fontName); newEntry = true; } //Initialize font data TEX->Read(&fontCharCount,2); char* memBuf = (char*)mem.alloc(fontCharCount*sizeof(TexID) + fontCharCount*sizeof(char*)); font->characterTextureID = (TexID*)(memBuf); font->characterTextureName = (char**)(memBuf+fontCharCount*sizeof(TexID)); for (int i = 0; i < fontCharCount; i++) { font->characterTextureName[i] = 0; font->characterTextureID[i] = BAD_ID; } font->firstCharacter = 0; font->numCharacters = fontCharCount; if (newEntry) { logWritem("Loading font %s (%d characters)...",fontName,fontCharCount); } else { logWritem("Appending to font %s...",fontName); } //Load all characters for (int i = 0; i < fontCharCount; i++) { char characterName[256]; TEX->ReadString(characterName); if (characterName[0]) { //Allocate name for this texture, if it wasn't yet defined if (font->characterTextureName[i] == 0) { font->characterTextureName[i] = mem.alloc_string(strlen(characterName)+1); } else { //See if there is enough name length to overwrite it if (strlen(characterName) > strlen(font->characterTextureName[i])) { //FIXME: this leaks memory in a way - there is no way to unalloc string font->characterTextureName[i] = mem.alloc_string(strlen(characterName)+1); } } //Copy new character texture name strcpy(font->characterTextureName[i],characterName); font->characterTextureName[i][strlen(characterName)] = 0; font->characterTextureID[i] = Graphics.GetTextureID(font->characterTextureName[i]); } } } } }
void Graphics_Manager::texReadGTA2Graphics(Chunk_Loader* TEX, bool scanOnly) { if (TEX->IsChunk("TILE") || //GTA2-compatible tiles and sprites TEX->IsChunk("SPR0")) { char graphicsName[256]; TEX->ReadString(graphicsName); logWritem("Found graphics set [%s], FirstID %d",graphicsName,globalIDCounter); graphics_entry* entry = 0; bool newEntry = false; for (uint i = 0; i < graphicsEntries.Count; i++) { if (strncmp(graphicsEntries[i]->GraphicName,graphicsName,256) == 0) { entry = graphicsEntries[i]; break; } } if (!entry) { //Do we need to add a new entry? entry = graphicsEntries.Add(); entry->TextureAtlas = (Texture_Atlas*)mem.alloc(sizeof(Texture_Atlas)); entry->TextureAtlas->Create(); entry->TextureAtlas->FirstID = globalIDCounter; entry->Filename = mem.alloc_string(strlen(TEX->fileName)+1); entry->GraphicName = mem.alloc_string(strlen(graphicsName)+1); strcpy(entry->Filename,TEX->fileName); //FIXME: has to use TEX filename strcpy(entry->GraphicName,graphicsName); newEntry = true; } if (TEX->IsChunk("TILE")) { //Load GTA2 tiles if (newEntry) { //Do we need to load textures entry->TextureAtlas->CreateTextures(1024); //No texture data globalIDCounter += 1024; logWritem("Loading 1024 tiles (GTA2-exported tileset)"); for (int i = 0; i < 1024; i++) { char newTexName[256]; snprintf(newTexName,256,"%s_%d",graphicsName,i); entry->TextureAtlas->Textures[i]->textureName = mem.alloc_string(strlen(newTexName)+1);//(char*)mem.alloc(strlen(newTexName)); strcpy(entry->TextureAtlas->Textures[i]->textureName,newTexName); float eps = 0.5f / 2048.0f; entry->TextureAtlas->Textures[i]->Left = 64*(i%32); entry->TextureAtlas->Textures[i]->Width = 64; entry->TextureAtlas->Textures[i]->Top = 64*(i/32); entry->TextureAtlas->Textures[i]->Height = 64; entry->TextureAtlas->Textures[i]->U1 = (64*(i%32)) / 2048.0f + eps; entry->TextureAtlas->Textures[i]->V1 = (64*(i/32)) / 2048.0f + eps; entry->TextureAtlas->Textures[i]->U2 = (63+64*(i%32)) / 2048.0f - eps; entry->TextureAtlas->Textures[i]->V2 = (63+64*(i/32)) / 2048.0f - eps; } } if (!scanOnly) { entry->TextureAtlas->CreateData(2048,2048); int readPos = 0; int prevPos = 0; while (TEX->chunkSize > 0) { int bytes = TEX->Read((char*)entry->TextureAtlas->textureData+readPos,min(2048,TEX->chunkSize - TEX->chunkPos)); if (bytes == 0) break; readPos += bytes; } entry->TextureAtlas->dataValid = true; } } if (TEX->IsChunk("SPR0")) { //Load GTA2 sprites (compatible format) int spriteCount; TEX->Read(&spriteCount,4); if (newEntry) { entry->TextureAtlas->CreateTextures(spriteCount); globalIDCounter += spriteCount; } logWritem("Loading %d sprites (GTA2-exported spriteset)",spriteCount); for (int i = 0; i < spriteCount; i++) { char newTexName[256]; int L,R,T,B; TEX->ReadString(newTexName); TEX->Read(&L,4); TEX->Read(&R,4); TEX->Read(&T,4); TEX->Read(&B,4); if (newEntry) { entry->TextureAtlas->Textures[i]->textureName = mem.alloc_string(strlen(newTexName)+1);//(char*)mem.alloc(strlen(newTexName)); strcpy(entry->TextureAtlas->Textures[i]->textureName,newTexName); entry->TextureAtlas->Textures[i]->Left = L; entry->TextureAtlas->Textures[i]->Width = R-L; entry->TextureAtlas->Textures[i]->Top = T; entry->TextureAtlas->Textures[i]->Height = B-T; entry->TextureAtlas->Textures[i]->U1 = L / 2048.0f; entry->TextureAtlas->Textures[i]->V1 = T / 2048.0f; entry->TextureAtlas->Textures[i]->U2 = R / 2048.0f; entry->TextureAtlas->Textures[i]->V2 = B / 2048.0f; } } if (!scanOnly) { entry->TextureAtlas->CreateData(2048,2048); int readPos = 0; while (TEX->chunkSize > 0) { int bytes = TEX->Read((char*)entry->TextureAtlas->textureData+readPos,min(2048,TEX->chunkSize - TEX->chunkPos)); if (bytes == 0) break; readPos += bytes; } while (readPos < 2048*2048*4) { *((char*)entry->TextureAtlas->textureData+readPos) = 0; readPos++; } entry->TextureAtlas->dataValid = true; } } } }
void Graphics_Manager::Initialize(bool PreloadTextures) { //Update maps info logWrite("Initializing graphics manager (updating texture file entries)"); int TexSize = 0; DIR *directory; struct dirent *direntry; globalIDCounter = 0; graphicsEntries.Create(); graphicsEntries.Preallocate(MAX_TEXTURE_FILE_ENTRIES); Animations.Initialize(); directory = opendir("./data"); while ((directory) && ((direntry = readdir(directory)) != 0)) { int len = strlen(direntry->d_name); switch (len) { case 1: if (direntry->d_name[0] == '.') continue; case 2: if (direntry->d_name[0] == '.' && direntry->d_name[1] == '.') continue; default: break; } //Check file format Chunk_Loader TEX; //Read map header char Filename[256]; snprintf(Filename,256,"./data/%s",direntry->d_name); TEX.Open(Filename); if (TEX.IsFileFormat("ZTEX",TEX_VERSION) == false) { continue; } logWrite("Loading graphics from %s...",Filename); //Read all chunks from file int lastRead = 0; while (TEX.ReadChunk()) { LockID lockID = Thread.EnterLock(MUTEX_CRESOURCE_LOAD); Graphics.texReadGTA2Graphics(&TEX, false); Graphics.texReadFonts(&TEX, false); Animations.LoadFromChunk(&TEX); Thread.LeaveLock(lockID); } TexSize += TEX.fileSize; TEX.Close(); } if (directory) closedir(directory); totalTextures = globalIDCounter + 1; logWrite("Found %d KB worth of texture files (%d textures)",TexSize/1024,totalTextures); logWritem("shrinking graphicsEntries, animationEntries, fontEntries..."); LockID lockID = Thread.EnterLock(MUTEX_CRESOURCE_LOAD); graphicsEntries.Shrink(); Thread.LeaveLock(lockID); Animations.Entries.Shrink(); //FIXME: should be shrink call directly Fonts.shrinkFontEntries(); //FIXME: ??? this should shrink font entries, call only after all fonts have been loaded }
void Map_Manager::Initialize() { logWrite("Initializing map manager (scanning for maps)"); DIR *directory; struct dirent *direntry; int MapSize = 0; //Total size of maps loaded (for stats) mapsInfo.Preallocate(1024); cacheCityscape.Preallocate(1024); directory = opendir("./data"); while ((directory) && ((direntry = readdir(directory)) != 0)) { int len = strlen(direntry->d_name); switch (len) { case 1: if (direntry->d_name[0] == '.') continue; case 2: if (direntry->d_name[0] == '.' && direntry->d_name[1] == '.') continue; default: break; } Chunk_Loader RMP; //Read the map char Filename[256]; snprintf(Filename,256,"./data/%s",direntry->d_name); RMP.Open(Filename); if (RMP.IsFileFormat("ZRMP",RMP_VERSION) == false) { continue; } //Read all chunks from file while (RMP.ReadChunk()) { if (RMP.IsChunk("RMAP")) { //Scan the chunk for district name, and cell coordinates int CX,CY; char districtName[5] = " "; RMP.Read(&CX,4); RMP.Read(&CY,4); RMP.Read(districtName,4); //Check if the map entry was already there int idx = cellEntryIndex(CX,CY); if (idx == BAD_ID) { logWritem("Found cell [%d;%d belongs to %s]: %s",CX,CY,districtName,direntry->d_name); rmp_entry* entry = mapsInfo.Add(); entry->CX = CX; entry->CY = CY; strncpy(entry->Filename,Filename,256); } else { logWritem("Duplicate map cell! No place for [%d;%d] from %s",CX,CY,direntry->d_name); } } } MapSize += RMP.fileSize; RMP.Close(); } if (directory) closedir(directory); logWrite("Found %d KB worth of maps",MapSize/1024); //Shrink entry table mapsInfo.Shrink(); }