logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
Vaultster fix

 
Post new topic   Reply to topic    nwnx.org Forum Index -> Windows development
View previous topic :: View next topic  
Author Message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Fri May 09, 2008 19:43    Post subject: Vaultster fix Reply with quote

Hi folks,

I've just made the following fix to lines 182+ of Client.cpp in Vaultster to solve a bug we've encountered on Arelith. Posting it here in case anyone else wants it.

Now I just have to figure out how to build it Wink

-Mith

Code:
void CClient::createPattern (char* pattern)
{
   // make sure there is no dot in the filename
   char* dot = strchr (character, '.');
   if (dot != NULL)
      dot[0] = 0;

   // determine the length of the current pattern
  // Edit by Mithreas - cap the pattern length to 12 (not 14).  This is because
  // if a player makes several chars with the same name, and their name is 14
  // chars long, their filenames are (e.g.)
  // daedinangthalion.bic
  // daedinangthalio1.bic
  // i.e. the last character(s) may be replaced by numbers.
   int characterLen = strlen (character);
   int patternLen = (characterLen >= 12) ? 12 : characterLen;
   
   // create the pattern string
   strncpy (pattern, character, characterLen);
   strcat (pattern, "*.bic");
}

(I've also made an equivalent fix to CServerClient::createPattern, and merged across the no-dot-in-filenames fix to that method).
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Fri May 09, 2008 20:29    Post subject: Reply with quote

OK, as a Java type working in C for the first time, I'm finding this a bit challenging Smile

I've downloaded MS Visual Studio 2008 Express, created a project, downloaded the NWNX base code to ensure that all the references I need are in place, and fixed up the errors so far by changing my project configuration (character sets, and a compiler option).

Now I'm left with this error:

1>.\Vaultster.cpp(12) : error C2491: 'GetClassObject' : definition of dllimport function not allowed

Problem is that in Vaultster.h we say...
Code:
#ifdef VAULTSTER_EXPORTS
#define VAULTSTER_API extern "C" __declspec(dllexport)
#else
#define VAULTSTER_API extern "C" __declspec(dllimport)
#endif

VAULTSTER_API CNWNXBase* GetClassObject ();
We don't seem to have VAULTSTER_EXPORTS defined, so we're treating this as a dllimport function. But we then define the function in Vaultster.cpp:
Code:
VAULTSTER_API CNWNXBase* GetClassObject ()
{
   return &vaultster;
}


Google tells me that Visual Studio 6 probably allowed this but 2008 doesn't. Any ideas on what I should do to work around it (short of finding another version of VS)? Bear in mind this is my first time ever working with C++ code...

Cheers,
-Mith
Back to top
View user's profile Send private message
Skywing



Joined: 03 Jan 2008
Posts: 321

PostPosted: Fri May 09, 2008 21:44    Post subject: Reply with quote

If that code is being compiled into the Vaultster dll, then you want to be getting the __declspec(dllexport) tag and not the __declspec(dllimport) tag. Perhaps you need to define the VAULTSTER_EXPORTS preprocessor symbol in the preprocessor properties node for the project.

- S
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Fri May 09, 2008 21:52    Post subject: Reply with quote

OK, I've added the #define VAULTSTER_EXPORTS line above the test to get myself moving fowards - I'll keep that there for now and look at putting into preprocessor properties later Smile

Hitting further issues now which look like I'm unable to reference methods in Winsock.h. If I build the project as a debug project, I get output like this:

Code:
1>   Creating library C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Debug\Vaultster.lib and object C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Debug\Vaultster.exp
1>Server.obj : error LNK2028: unresolved token (0A0002F6) "extern "C" char * __stdcall inet_ntoa(struct in_addr)" (?inet_ntoa@@$$J14YGPADUin_addr@@@Z) referenced in function "protected: bool __thiscall CServer::isValidClient(struct sockaddr_in &)" (?isValidClient@CServer@@$$FIAE_NAAUsockaddr_in@@@Z)
1>Sock.obj : error LNK2028: unresolved token (0A0002A5) "extern "C" int __stdcall WSAGetLastError(void)" (?WSAGetLastError@@$$J10YGHXZ) referenced in function "public: bool __thiscall CSock::Create(int)" (?Create@CSock@@$$FQAE_NH@Z)
...(lots more)


Google tells me that inet_ntoa is defined in Winsock.h but simply including that leaves me with a whole raft of other errors (in code that isn't part of the project). After some googling I tried doing a release build instead of a debug build. I then get similar errors but referencing methods like gethostbyname which is part of Winsock2.h (the inet_ntoa errors go, though).

Any more tips for a novice?

Thanks,
-Mith
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Fri May 09, 2008 22:46    Post subject: Reply with quote

Nearly there...

I needed to link the following libraries in Properties > Configuration Properties > Linker > Input
wsock32.lib
ws2_32.lib
user32.lib
zlib.lib

Now I just have to figure out where the compress2 and uncompress methods come from since linking zlib hasn't found them...
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Sat May 10, 2008 0:24    Post subject: Reply with quote

Hrm, well, I don't seem to be making any further progress. The linker is definitely seeing my zlib.lib (I've now upgraded to the latest version of zlib and the file is now called zdll.lib, but exhibits the same symptoms). And the methods that aren't showing up are definitely part of zlib. I'd really appreciate it if anyone can give me a hint here - has anyone had similar issues in trying to link to a library?

Generated commands:
Code:
Creating temporary file "c:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\RSP00006528122532.rsp" with contents
[
/GL /I "C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\lib" /D "WIN32" /D "NDEBUG" /D "_CRT_SECURE_NO_WARNINGS" /D "VAULTSTER_EXPORTS" /D "_WINDLL" /D "_MBCS" /FD /EHsc /MD /Fo"Release\\" /Fd"Release\vc90.pdb" /W3 /c /Zi /Gr /TP ".\Stdafx.cpp"

".\Vaultster.cpp"

".\Sock.cpp"

".\Server.cpp"

".\NWNXVaultster.cpp"

".\NWNXBase.cpp"

".\Client.cpp"

".\blowfish.cpp"
]
Creating command line "cl.exe @"c:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\RSP00006528122532.rsp" /nologo /errorReport:prompt"
Creating temporary file "c:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\RSP00006628122532.rsp" with contents
[
/OUT:"C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\Vaultster.dll" /INCREMENTAL:NO /DLL /MANIFEST /MANIFESTFILE:"Release\Vaultster.dll.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\Vaultster.pdb" /LTCG /DYNAMICBASE /NXCOMPAT /MACHINE:X86 wsock32.lib ws2_32.lib user32.lib zdll.lib

".\Release\blowfish.obj"

".\Release\Client.obj"

".\Release\NWNXBase.obj"

".\Release\NWNXVaultster.obj"

".\Release\Server.obj"

".\Release\Sock.obj"

".\Release\Vaultster.obj"

".\zdll.lib"

".\Release\Stdafx.obj"
]
Creating command line "link.exe @"c:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\RSP00006628122532.rsp" /NOLOGO /ERRORREPORT:PROMPT"



Output:
Code:
Compiling...
Stdafx.cpp
Vaultster.cpp
Sock.cpp
Server.cpp
NWNXVaultster.cpp
.\NWNXVaultster.cpp(68) : warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)
.\NWNXVaultster.cpp(69) : warning C4800: 'int' : forcing value to bool 'true' or 'false' (performance warning)
NWNXBase.cpp
Client.cpp
.\Client.cpp(290) : warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
.\Client.cpp(293) : warning C4800: 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
blowfish.cpp
Linking...
   Creating library C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\Vaultster.lib and object C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\Vaultster.exp
Sock.obj : error LNK2001: unresolved external symbol @uncompress@16
Sock.obj : error LNK2001: unresolved external symbol @compress2@20
C:\Documents and Settings\James\My Documents\Visual Studio 2008\Projects\Vaultster\Release\Vaultster.dll : fatal error LNK1120: 2 unresolved externals
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Sat May 10, 2008 16:07    Post subject: Reply with quote

Aha! I needed to use the _cdecl calling convention. I now, finally, have a DLL... woohoo.
Back to top
View user's profile Send private message
Zebranky



Joined: 04 Jun 2006
Posts: 415

PostPosted: Wed May 14, 2008 6:21    Post subject: Reply with quote

You may find this works better for you:
Change
Code:
strncpy (pattern, character, characterLen);

to
Code:
strncpy (pattern, character, patternLen);


And make CClient::findLatestFile look like this:
Code:
bool CClient::findLatestFile (char* pattern, char* filename)
{
   WIN32_FIND_DATA Search;
   WIN32_FILE_ATTRIBUTE_DATA fad;
   char path[MAX_PATH];
   bool found = false;

   // try to find the first file in the dir
   sprintf (path, "%s\\%s\\%s", serverVault, gamespy, pattern);
   vaultster.Log ("o Searching for %s\n", path);
   HANDLE hSearch = FindFirstFile (path, &Search);
   if (hSearch != INVALID_HANDLE_VALUE) {
      FILETIME latestFT;
      char buffer[256];

      // get date from this first file
      sprintf (buffer, "%s\\%s\\%s", serverVault, gamespy, Search.cFileName);
      GetFileAttributesEx (buffer, GetFileExInfoStandard, &fad);
      latestFT = fad.ftLastAccessTime;
      strcpy (filename, buffer);
      if(strlen(Search.cFileName) < 32)
      {
         memset (character, 0, 32);
         strncpy(character, Search.cFileName, strcspn(Search.cFileName, "."));
      }
      else
      {
         vaultster.Log("o Name of found file too long: %s\n", Search.cFileName);
         return false;
      }

      vaultster.Log ("o Found at least one file.\n");
            
      // check the rest of the files (if any)
      while (FindNextFile (hSearch, &Search)) {
         sprintf (buffer, "%s%s\\%s", serverVault, gamespy, Search.cFileName);
         GetFileAttributesEx (buffer, GetFileExInfoStandard, &fad);
         if (CompareFileTime (&fad.ftLastAccessTime, &latestFT) > 0) {
            latestFT = fad.ftLastAccessTime;
            strcpy (filename, buffer);
            if(strlen(Search.cFileName) < 32)
            {
               memset (character, 0, 32);
               strncpy(character, Search.cFileName, strcspn(Search.cFileName, "."));
            }
            else
            {
               vaultster.Log("o Name of found file too long: %s\n", Search.cFileName);
               return false;
            }
         }
      }

      // we at least found one, otherwise we wouldn't be here
      found = true;
   }

   // finalize
   FindClose (hSearch);
   return found;
}


This makes createPattern() actually behave usefully, and adds, uh... some kind of error-checking to findLatestFile(), I think. I made these changes in a psycho-coding spree over a year ago, along with a number of other bugfixes I should publish. We're still encountering a few bugs I'd like to squash before I do that, though.
_________________
Win32 SVN builds: http://www.mercuric.net/nwn/nwnx/

<Fluffy-Kooshy> NWNx plugin is to this as nuclear warheads are to getting rid of fire ants.

<ThriWork> whenever I hear nwn extender, I think what does NWN need a penis extender for?
Back to top
View user's profile Send private message Visit poster's website
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Mon Mar 01, 2010 4:38    Post subject: Reply with quote

OK, there are three problems with your latest code.
Arrow Characters who have remade with the same name will have 13 characters + a number. By substringing to 14, you pick up the original copy and not the remade version. [This is the same bug I fixed above].

Arrow Characters who have changed their name no longer have a name that resembles their filename. The only way to work around this is to pass a pattern of *.bic and just pick up the latest file.

Arrow Which would be fine, but for a bug in the following code:
Code:
    while (FindNextFile (hSearch, &Search)) {
         sprintf (buffer, "%s%s\\%s", serverVault, gamespy, Search.cFileName);

You're missing a \\ between the servervault and gamespy ID in the second line there. Which means that when I passed *.bic to the function, I ended up portalling the first alphabetically ordered char from everyone's vaults... painful! That'll teach me to test better Wink

See also unrelated bug fix in http://www.nwnx.org/phpBB2/viewtopic.php?t=1521&highlight=.

-Mith
_________________
Admin team, Arelith PW
Hakless world that relies heavily on the great work done by the NWNX team.
Back to top
View user's profile Send private message
Mithreas



Joined: 09 May 2008
Posts: 24

PostPosted: Mon Mar 01, 2010 21:39    Post subject: Reply with quote

Fixed and fully working version of Client.cpp as follows. Note I also changed from using last access time to last modified time, since character files in use in game were not registering as having been accessed. This library is now live on Arelith (regularly hitting >100 concurrent players across 3 servers), which should be enough testing to declare it 'good' Wink

Code:

/***************************************************************************
    Client.cpp - Implementation of the client class for Windows.
    Copyright (C) 2004 Jeroen Broekhuizen (jeroen@nwnx.org)
   
    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 2 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, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 ***************************************************************************
    Updates:
   04-07-2005:
     - Added extra logging information
    05-07-2005:
     - Fixed return value of transmitFile function. Now realy returns
       value of the socket transmision functions.
     - Added extra check for dots in the character name in the
       createPattern function.
     - Added extra logging.

 ***************************************************************************/

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <time.h>
#include <fstream>
using namespace std;

#include "nwnxvaultster.h"
#include "client.h"

typedef unsigned char uchar;
typedef unsigned long ulong;

// use this variable for logging purposes
extern CNWNXVaultster vaultster;

CClient::CClient(void)
{
   status = STATUS_OK;
}

CClient::~CClient(void)
{
}

char CClient::serverVault[] = "";
char CClient::cryptoKey[] = "";
char CClient::password[] = "";
int CClient::port = 5100;

void CClient::setCommand (Functions cmd)
{
   switch (cmd) {
   case Get:
      command = CMD_GET;
      break;
   case Put:
      command = CMD_SEND;
      break;
   }
}

void CClient::setStatus (int stat)
{
   status = stat;
}
   
int CClient::getStatus ()
{
   return status;
}

void CClient::setPort (int p)
{
   // save the port number, which is used to connect
   port = p;
}

void CClient::setServervault (char* vault)
{
   // save a static string with the servervault directory
   strcpy (serverVault, vault);
}

void CClient::setCryptoKey (char* key)
{
   // save a copy of the blowfish key
   strcpy (cryptoKey, key);
}

void CClient::setPassword (char* pwd)
{
   // save the password for identification
   strcpy (password, pwd);
}

DWORD CClient::thread (LPVOID param)
{
   CClient* client = (CClient*)param;

   if (!client->run ()) {
      client->setStatus (STATUS_ERROR);
     vaultster.Log ("o There was an error during the transmission.\n");
   }
   else {
      client->setStatus (STATUS_OK);
     vaultster.Log ("o Portal successfully finished.\n");
   }

   int stat = client->getStatus ();
   return stat;
}

bool CClient::run ()
{
   char filename[256], pattern[32];
   fish.Initialize ((unsigned char*)cryptoKey, strlen (cryptoKey));

   memset (filename, 0, 256);
   memset (pattern, 0, 32);

   // make it linux compatible:
   strlwr (character);

   // try to find the latest file
   createPattern (pattern);
   vaultster.Log ("o Start searching for %s\n", pattern);
   if (!findLatestFile (pattern, filename)) {
      vaultster.Log ("o Could not find latest file for %s\\%s!\n", gamespy, character);
      return false;
   }

   // make it linux compatible again:
   strlwr (character);

   vaultster.Log ("o Trying to connect to server %s...\n", server);
   // try to connect to the server
   socket.Create ();
   if (!socket.Connect (server, port)) {
      vaultster.Log ("o Could not connect to %s on port %d.\n", server, port);
      return false;
   }

   if (!handShake ()) {
      // wrong password, bail out!
      vaultster.Log ("o Wrong password, can not finish job.\n");
      socket.Close ();
      return false;
   }
   vaultster.Log ("o Ready for file transmission.\n");

   if (!transmitFile (filename)) {
      // to bad! it failed
      vaultster.Log ("o Failed to send over file '%s'.\n", filename);
      socket.Close ();
      return false;
   }
   vaultster.Log ("o Job ready\n");

   socket.Close ();
   return true;
}

void CClient::createPattern (char* pattern)
{
   // make sure there is no dot in the filename
   char* dot = strchr (character, '.');
   if (dot != NULL)
      dot[0] = 0;

   // determine the length of the current pattern
   int characterLen = strlen (character);
   int patternLen = (characterLen >= 14) ? 14 : characterLen;
   
   // create the pattern string
   //strncpy (pattern, character, patternLen);
   strcat (pattern, "*.bic");
}

bool CClient::findLatestFile (char* pattern, char* filename)
{
   WIN32_FIND_DATA Search;
   WIN32_FILE_ATTRIBUTE_DATA fad;
   char path[MAX_PATH];
   bool found = false;

   // try to find the first file in the dir
   sprintf (path, "%s\\%s\\%s", serverVault, gamespy, pattern);
   vaultster.Log ("o Searching for %s\n", path);
   HANDLE hSearch = FindFirstFile (path, &Search);
   if (hSearch != INVALID_HANDLE_VALUE) {
      FILETIME latestFT;
      char buffer[256];

      // get date from this first file
      sprintf (buffer, "%s\\%s\\%s", serverVault, gamespy, Search.cFileName);
      GetFileAttributesEx (buffer, GetFileExInfoStandard, &fad);
      latestFT = fad.ftLastWriteTime;
      strcpy (filename, buffer);
      if(strlen(Search.cFileName) < 32)
      {
         memset (character, 0, 32);
         strncpy(character, Search.cFileName, strcspn(Search.cFileName, "."));
      }
      else
      {
         vaultster.Log("o Name of found file too long: %s\n", Search.cFileName);
         return false;
      }

      vaultster.Log ("o Found file: %s\n", Search.cFileName);
            
      // check the rest of the files (if any)
      while (FindNextFile (hSearch, &Search)) {
         sprintf (buffer, "%s\\%s\\%s", serverVault, gamespy, Search.cFileName);
         GetFileAttributesEx (buffer, GetFileExInfoStandard, &fad);
          vaultster.Log ("o Found file: %s\n", Search.cFileName);

         if (CompareFileTime (&fad.ftLastWriteTime, &latestFT) > 0) {
              vaultster.Log ("o File has a later modified time.\n", Search.cFileName);
            latestFT = fad.ftLastWriteTime;
            strcpy (filename, buffer);
            if(strlen(Search.cFileName) < 32)
            {
               memset (character, 0, 32);
               strncpy(character, Search.cFileName, strcspn(Search.cFileName, "."));
            }
            else
            {
               vaultster.Log("o Name of found file too long: %s\n", Search.cFileName);
               return false;
            }
         }
      }

      // we at least found one, otherwise we wouldn't be here
      found = true;
   }

   // finalize
   FindClose (hSearch);
   return found;
}

void CClient::generatePassword (char pwd[128])
{
   memset (pwd, 0, 128);
   strcpy (pwd, password);
   
   // build the dummy part of the password
   srand ((unsigned)time(NULL));
   for (int i = strlen(password); i < 128; i++)
      pwd[i] = 33 + (rand() % 90);
}

bool CClient::handShake ()
{
   int ret = 0;
   char pwd[128];
   uchar encrypted[128];
   
   // send the command to the server
   socket.Send (command);
   socket.Receive (ret);
   if (ret == INFO_NACK)
      return false;
   
   // now encrypt the password and send it over
   generatePassword (pwd);
   fish.Encode ((uchar*)pwd, encrypted, 128);
   socket.Send (encrypted, 128);
   
   // hopefully the password is accepted
   socket.Receive (ret);
   if (ret == INFO_NACK)
      return false;
   return true;
}

bool CClient::transmitFile (char* localFile)
{
   int ret = 0;
   sendEncrypted (gamespy);
   sendEncrypted (character);
   socket.Receive (ret);
   if (ret == INFO_NACK)
      return false;
      
   // now perform the requested job
   switch (command) {
   case CMD_SEND:
      vaultster.Log ("o Sending file\n");
      return socket.SendFile (localFile);
   case CMD_GET:
      vaultster.Log ("o Receiving file\n");
      return socket.ReceiveFile (localFile);
   }

   // invalid command (should not be able to get here).
   return false;
}

void CClient::sendEncrypted (char* str)
{
   uchar encrypted[256];
   
   // encode the string
   ulong patternLen = strlen (str);
   ulong len = fish.GetOutputLength (patternLen);
   fish.Encode ((uchar*)str, encrypted, patternLen);

   // send the data to the server
   socket.Send (len);
   socket.Send (encrypted, len);
}

_________________
Admin team, Arelith PW
Hakless world that relies heavily on the great work done by the NWNX team.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> Windows development All times are GMT + 2 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group