/* * psserver.cpp - author: Matze Braun * * Copyright (C) 2001 Atomic Blue (info@planeshift.it, http://www.atomicblue.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 (version 2 of the License) * 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. * */ #include #include #include #include #include #include #include #include #include "engine/netpersist.h" #include "gem.h" #include "questionmanager.h" #include "advicemanager.h" #include "actionmanager.h" #include "entitymanager.h" #include "psserver.h" #include "util/serverconsole.h" #include "util/sleep.h" #include "util/mathscript.h" #include "chatmanager.h" #include "netmanager.h" #include "net/msghandler.h" #include "client.h" #include "authentserver.h" #include "guildmanager.h" #include "groupmanager.h" #include "playergroup.h" #include "usermanager.h" #include "tutorialmanager.h" #include "net/messages.h" #include "util/psdatabase.h" #include "psserverchar.h" #include "spawnmanager.h" #include "adminmanager.h" #include "commandmanager.h" #include "exchangemanager.h" #include "util/eventmanager.h" #include "marriagemanager.h" #include "combatmanager.h" #include "spellmanager.h" #include "weathermanager.h" #include "npcmanager.h" #include "serverstatus.h" #include "progressionmanager.h" #include "cachemanager.h" #include "creationmanager.h" #include "questmanager.h" #include "economymanager.h" #include "workmanager.h" #include "minigamemanager.h" #include "globals.h" #include "bulkobjects/pscharacterloader.h" #include "bulkobjects/psitem.h" #include "bulkobjects/psaccountinfo.h" #include "util/log.h" #include "util/consoleout.h" psCharacterLoader psServer::CharacterLoader; psServer::psServer () { serverthread = NULL; marriageManager = NULL; entitymanager = NULL; tutorialmanager = NULL; database = NULL; usermanager = NULL; guildmanager = NULL; groupmanager = NULL; charmanager = NULL; eventmanager = NULL; charCreationManager = NULL; workmanager = NULL; questionmanager = NULL; advicemanager = NULL; actionmanager = NULL; minigamemanager = NULL; logcsv = NULL; vfs = NULL; // Initialize the RNG using current time() as the seed value randomGen.Initialize(); } #define PS_CHECK_REF_COUNT(c) printf("%3d %s\n",c->GetRefCount(),#c) psServer::~psServer() { // Kick players from server ClientConnectionSet* clients = serverthread->GetConnections(); Client* p; do { // this is needed to not block the RemovePlayer later... { ClientIterator i(*clients); p = i.First(); } if (p) { psAuthRejectedMessage msgb (p->GetClientNum(),"The server was restarted or shut down. Please check the website or forums for more news."); eventmanager->Broadcast(msgb.msg, NetBase::BC_FINALPACKET); RemovePlayer(p->GetClientNum(),"The server was restarted or shut down. Please check the website or forums for more news."); } } while (p); delete economymanager; delete tutorialmanager; delete charmanager; delete entitymanager; delete usermanager; delete exchangemanager; delete serverthread; delete database; delete marriageManager; delete spawnmanager; delete adminmanager; delete combatmanager; delete weathermanager; delete charCreationManager; delete workmanager; delete mathscriptengine; delete progression; delete npcmanager; delete spellmanager; delete minigamemanager; delete cachemanager; delete questmanager; delete logcsv; delete rng; /* PS_CHECK_REF_COUNT(guildmanager); PS_CHECK_REF_COUNT(questionmanager); PS_CHECK_REF_COUNT(groupmanager); PS_CHECK_REF_COUNT(authserver); PS_CHECK_REF_COUNT(vfs); PS_CHECK_REF_COUNT(eventmanager); PS_CHECK_REF_COUNT(configmanager); PS_CHECK_REF_COUNT(chatmanager); PS_CHECK_REF_COUNT(advicemanager); PS_CHECK_REF_COUNT(actionmanager); */ guildmanager = NULL; questionmanager = NULL; groupmanager = NULL; authserver = NULL; chatmanager = NULL; advicemanager = NULL; actionmanager = NULL; minigamemanager = NULL; } bool psServer::Initialize(iObjectRegistry* object_reg) { //Map isn't loaded yet MapLoaded=false; objreg = object_reg; // Start logging asap csRef cmdline = CS_QUERY_REGISTRY(objreg, iCommandLineParser); if (cmdline) { const char* ofile = cmdline->GetOption ("output"); if (ofile != NULL) { ConsoleOut::SetOutputFile (ofile, false); } else { const char* afile = cmdline->GetOption ("append"); if (afile != NULL) { ConsoleOut::SetOutputFile (afile, true); } } } rng = new csRandomGen( csGetTicks() ); vfs = CS_QUERY_REGISTRY(objreg, iVFS); configmanager = CS_QUERY_REGISTRY(object_reg, iConfigManager); vfs = CS_QUERY_REGISTRY(object_reg, iVFS); if (!configmanager || !vfs) { Error1 ("Couldn't find Configmanager!\n"); return false; } // Which plugin is loaded here is specified in psserver.cfg file. // Apperantly we need to do this for it to work correctly configmanager->Load("psserver.cfg"); // Load the log settings LoadLogSettings(); // Start Database database = new psDatabase(object_reg); csString db_host, db_user, db_pass, db_name; db_host = configmanager->GetStr("Planeshift.Database.host", "localhost"); db_user = configmanager->GetStr("Planeshift.Database.userid", "planeshift"); db_pass = configmanager->GetStr("Planeshift.Database.password", "planeshift"); db_name = configmanager->GetStr("Planeshift.Database.name", "planeshift"); Debug4(LOG_STARTUP,0,COL_BLUE "Database Host: '%s' User: '%s' Databasename: '%s'\n" COL_NORMAL, (const char*) db_host, (const char*) db_user, (const char*) db_name); if (!database->Initialize(db_host, db_user, db_pass, db_name)) { Error2("Could not create database or connect to it: %s",database->GetLastError()); delete database; return false; } csString db_version; if ( ! GetServerOption("db_version", db_version) ) { CPrintf (CON_ERROR, "Couldn't determine database version. Error was %s.\n", db->GetLastError() ); db = NULL; return false; } if ( strcmp( db_version, DATABASE_VERSION_STR ) ) { CPrintf (CON_ERROR, "Database version mismatch: we have '%s' while we are looking for '%s'. Recreate your database using create_all.sql\n", (const char*)db_version, DATABASE_VERSION_STR); db = NULL; return false; } Debug1(LOG_STARTUP,0,"Started Database\n"); // Initialise the CSV logger logcsv = new LogCSV(configmanager, vfs); cachemanager = new CacheManager(); //Loads the standard motd message from db Result result(db->Select("SELECT option_value FROM server_options WHERE option_name = 'standard_motd'")); if (result.IsValid() && result.Count()>0) motd = result[0][0]; else motd = ""; // MathScript Engine mathscriptengine = new MathScriptEngine("/planeshift/data/rpgrules.xml",vfs); // Initialize DB settings cache if (!cachemanager->PreloadAll()) { CPrintf(CON_ERROR, "Could not initialize database cache.\n"); delete database; return false; } Debug1(LOG_STARTUP,0,"Preloaded mesh names, texture names, part names, image names, race info, sector info, traits. item categories, item stats, ways and spells.\n"); if (!CharacterLoader.Initialize()) { CPrintf(CON_ERROR, "Could not initialize Character Loader.\n"); cachemanager->UnloadAll(); delete database; return false; } // Start Network Thread serverthread=new NetManager; if (!serverthread->Initialize(MSGTYPE_PREAUTHENTICATE,MSGTYPE_NPCAUTHENT)) { Error1 ("Network thread initialization failed!\n"); Error1 ("Is there already a server running?\n"); delete serverthread; return false; } csString serveraddr = configmanager->GetStr("Planeshift.Server.Addr", "0.0.0.0"); int port = configmanager->GetInt("Planeshift.Server.Port", 1243); Debug3(LOG_STARTUP,0,COL_BLUE "Listening on '%s' Port %d.\n" COL_NORMAL, (const char*) serveraddr, port); if (!serverthread->Bind (serveraddr, port)) { delete serverthread; return false; } Debug1(LOG_STARTUP,0,"Started Network Thread\n"); // Start Event Manager // Create the MAIN GAME thread. The eventhandler handles // both messages and events. For backward compablility // we still store two points. But they are one object // and one thread. eventmanager = csPtr( new EventManager ); // This gives access to msghandler to all message types psMessageCracker::msghandler = eventmanager; if (!eventmanager->Initialize(serverthread, 1000)) return false; // Attach to incoming messages. if (!eventmanager->StartThread() ) return false; Debug1(LOG_STARTUP,0,"Started Event Manager Thread\n"); usermanager = new UserManager(serverthread->GetConnections() ); Debug1(LOG_STARTUP,0,"Started User Manager\n"); entitymanager = new EntityManager; if (!entitymanager->Initialize(object_reg, serverthread->GetConnections(), usermanager)) { Error1("Failed to initialise CEL!\n"); delete entitymanager; return false; } entitymanager->SetReady(false); serverthread->SetEngine(entitymanager->GetEngine()); Debug1(LOG_STARTUP,0,"Started CEL\n"); // Start Combat Manager combatmanager = new psCombatManager(); if (!combatmanager->InitializePVP()) { return false; } Debug1(LOG_STARTUP,0,"Started Combat Manager\n"); // Start Spell Manager spellmanager = new psSpellManager(serverthread->GetConnections(), object_reg); Debug1(LOG_STARTUP,0,"Started Spell Manager\n"); // Start Weather Manager weathermanager = new WeatherManager(); weathermanager->Initialize(); Debug1(LOG_STARTUP,0,"Started Weather Manager\n"); marriageManager = new psMarriageManager(); questmanager = new QuestManager; if (!questmanager->Initialize()) return false; chatmanager = csPtr (new ChatManager); Debug1(LOG_STARTUP,0,"Started Chat Manager\n"); guildmanager = csPtr(new GuildManager(serverthread->GetConnections(), chatmanager)); Debug1(LOG_STARTUP,0,"Started Guild Manager\n"); questionmanager = csPtr(new QuestionManager() ); Debug1(LOG_STARTUP,0,"Started Question Manager\n"); advicemanager = csPtr(new AdviceManager( database ) ); Debug1(LOG_STARTUP,0,"Started Advice Manager\n"); groupmanager = csPtr (new GroupManager(serverthread->GetConnections(),chatmanager)); Debug1(LOG_STARTUP,0,"Started Group Manager\n"); charmanager = new psServerCharManager(); if ( !charmanager->Initialize(serverthread->GetConnections()) ) return false; Debug1(LOG_STARTUP,0,"Started Character Manager\n"); spawnmanager = new SpawnManager(database); Debug1(LOG_STARTUP,0,"Started NPC Spawn Manager\n"); adminmanager = new AdminManager; Debug1(LOG_STARTUP,0,"Started Admin Manager\n"); tutorialmanager = new TutorialManager(GetConnections()); actionmanager = csPtr(new ActionManager( database)); Debug1(LOG_STARTUP,0,"Started Action Manager\n"); authserver = csPtr (new psAuthenticationServer(serverthread->GetConnections(), usermanager, guildmanager)); Debug1(LOG_STARTUP,0,"Started Authentication Server\n"); exchangemanager = new ExchangeManager(serverthread->GetConnections()); Debug1(LOG_STARTUP,0,"Started Exchange Manager\n"); npcmanager = new NPCManager(serverthread->GetConnections(), database, eventmanager); Debug1(LOG_STARTUP,0,"Started NPC Superclient Manager\n"); progression = new ProgressionManager(serverthread->GetConnections()); Debug1(LOG_STARTUP,0,"Started Progression Manager\n"); // Start work manager workmanager = new psWorkManager(); Debug1(LOG_STARTUP,0,"Started Work Manager\n"); // Start economy manager economymanager = new EconomyManager(); Debug1(LOG_STARTUP,0,"Started Economy Manager\n"); // Start droping economymanager->ScheduleDrop(1 * 60 * 60 * 1000,true); // 1 hour // Start minigame manager minigamemanager = new psMiniGameManager(); Debug1(LOG_STARTUP, 0, "Started Minigame Manager\n"); charCreationManager = new psCharCreationManager(); if ( !charCreationManager->Initialize() ) { Error1("Failed to load character creation data"); return false; } Debug1(LOG_STARTUP,0, "Started Character Creation Manager"); if (!ServerStatus::Initialize (object_reg)) { CPrintf (CON_WARNING, "Warning: Couldn't initialize server status reporter.\n"); } else { Debug1(LOG_STARTUP,0,"Server status reporter initialized.\n"); } return true; } void psServer::MainLoop () { // Eventually load an autoexec file csRef cmdline = CS_QUERY_REGISTRY(objreg, iCommandLineParser); if (cmdline) { const char* autofile; for (int i = 0; (autofile = cmdline->GetOption("run", i)); i++) { char toexec[1000]; strcpy(toexec, "exec "); strcat(toexec, autofile); ServerConsole::ExecuteScript(toexec); } } csString status("Server initialized"); logcsv->Write(CSV_STATUS, status); ServerConsole::MainLoop (); status = "Server shutdown"; logcsv->Write(CSV_STATUS, status); // Save log settings SaveLogSettings(); // Shut things down eventmanager->StopThread(); } void psServer::RemovePlayer (uint32_t clientnum,const char *reason) { Client* client = serverthread->GetConnections()->FindAny(clientnum); if (!client) { CPrintf (CON_WARNING, "Tried to remove non-existent client: %d\n", clientnum); return; } char ipaddr[20]; client->GetIPAddress(ipaddr); csString status; status.Format("%s, %u, Client (%s) removed", ipaddr, client->GetClientNum(), client->GetName()); psserver->GetLogCSV()->Write(CSV_AUTHENT, status); Notify3(LOG_CHARACTER, "Remove player '%s' (%d)\n", client->GetName(),client->GetClientNum() ); client->Disconnect(); authserver->SendDisconnect(client,reason); entitymanager->DeletePlayer(client); if (client->IsSuperClient()) npcmanager->Disconnect(client); serverthread->GetConnections()->Delete(client); } void psServer::MutePlayer (uint32_t clientnum,const char *reason) { Client* client = serverthread->GetConnections()->Find(clientnum); if (!client) { CPrintf (CON_WARNING, "Tried to mute non-existent client: %d\n", clientnum); return; } CPrintf (CON_DEBUG, "Mute player '%s' (%d)\n", client->GetName(), client->GetClientNum() ); client->SetMute(true); psserver->SendSystemInfo(client->GetClientNum(),reason); } void psServer::UnmutePlayer (uint32_t clientnum,const char *reason) { Client* client = serverthread->GetConnections()->Find(clientnum); if (!client) { CPrintf (CON_WARNING, "Tried to unmute non-existent client: %d\n", clientnum); return; } CPrintf (CON_DEBUG, "Unmute player '%s' (%d)\n", client->GetName(), client->GetClientNum() ); client->SetMute(false); psserver->SendSystemInfo(client->GetClientNum(),reason); } bool psServer::LoadMap(char* mapname) { if (entitymanager->LoadMap(mapname)) { MapLoaded=true; } return MapLoaded; } bool psServer::IsReady() { if (!entitymanager) return false; return entitymanager->IsReady(); } bool psServer::HasBeenReady() { if (!entitymanager) return false; return entitymanager->HasBeenReady(); } bool psServer::IsFull(size_t numclients, Client * client) { unsigned int maxclients = GetConfig()->GetInt("Planeshift.Server.User.connectionlimit", 20); if (client) { return numclients > maxclients && !CacheManager::GetSingleton().GetCommandManager()->Validate(client->GetSecurityLevel(), "always login"); } else { return numclients > maxclients; } } void psServer::SendSystemInfo(int clientnum, const char *fmt, ... ) { if ( clientnum == 0 || fmt == NULL ) return; va_list args; va_start(args, fmt); psSystemMessage newmsg(clientnum ,MSG_INFO, fmt, args); va_end(args); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); else { Bug2("Could not create valid psSystemMessage for client %u.\n",clientnum); } } void psServer::SendSystemBaseInfo(int clientnum, const char *fmt, ...) { if ( clientnum == 0 || fmt == NULL ) return; va_list args; va_start(args, fmt); psSystemMessage newmsg(clientnum ,MSG_INFO_BASE, fmt, args); va_end(args); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); else { Bug2("Could not create valid psSystemMessage for client %u.\n",clientnum); } } void psServer::SendSystemResult(int clientnum, const char *fmt, ... ) { if ( clientnum == 0 || fmt == NULL ) return; va_list args; va_start(args, fmt); psSystemMessage newmsg(clientnum ,MSG_RESULT, fmt, args); va_end(args); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); else { Bug2("Could not create valid psSystemMessage for client %u.\n",clientnum); } } void psServer::SendSystemOK(int clientnum, const char *fmt, ... ) { if ( clientnum == 0 || fmt == NULL ) return; va_list args; va_start(args, fmt); psSystemMessage newmsg(clientnum ,MSG_OK, fmt, args); va_end(args); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); else { Bug2("Could not create valid psSystemMessage for client %u.\n",clientnum); } } void psServer::SendSystemError(int clientnum, const char *fmt, ... ) { if ( clientnum == 0 || fmt == NULL ) return; va_list args; va_start(args, fmt); psSystemMessage newmsg(clientnum ,MSG_ERROR, fmt, args); va_end(args); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); else { Bug2("Could not create valid psSystemMessage for client %u.\n",clientnum); } } void psServer::LoadLogSettings() { int count=0; for (int i=0; i< MAX_FLAGS; i++) { if (pslog::GetName(i)) { pslog::SetFlag(pslog::GetName(i),configmanager->GetBool(pslog::GetSettingName(i)),0); if(configmanager->GetBool(pslog::GetSettingName(i))) count++; } } if (count==0) { CPrintf(CON_CMDOUTPUT,"All LOGS are off.\n"); } csString debugFile = configmanager->GetStr("PlaneShift.DebugFile"); if ( debugFile.Length() > 0 ) { csRef reporter = CS_QUERY_REGISTRY( objreg, iStandardReporterListener ); reporter->SetMessageDestination (CS_REPORTER_SEVERITY_DEBUG, true, false ,false, false, true, false); reporter->SetMessageDestination (CS_REPORTER_SEVERITY_ERROR, true, false ,false, false, true, false); reporter->SetMessageDestination (CS_REPORTER_SEVERITY_BUG, true, false ,false, false, true, false); time_t curr=time(0); tm* localtm = localtime(&curr); csString timeStr; timeStr.Format("-%d-%02d-%02d-%02d:%02d:%02d", localtm->tm_year+1900, localtm->tm_mon+1, localtm->tm_mday, localtm->tm_hour, localtm->tm_min, localtm->tm_sec); debugFile.Append( timeStr ); reporter->SetDebugFile( debugFile, true ); Debug2(LOG_STARTUP,0,"PlaneShift Server Log Opened............. %s", timeStr.GetData()); } } void psServer::SaveLogSettings() { for (int i=0; i< MAX_FLAGS; i++) { if (pslog::GetName(i)) { configmanager->SetBool(pslog::GetSettingName(i),pslog::GetValue(pslog::GetName(i))); } } configmanager->Save(); } ClientConnectionSet* psServer::GetConnections() { return serverthread->GetConnections(); } /*-----------------Buddy List Management Functions-------------------------*/ bool psServer::IsBuddy(int self,int buddy) { Result result(db->Select("SELECT player_id" " from buddy_list" " where player_buddy=%d " " and player_id=%d",buddy,self)); if (!result.IsValid()) { database->SetLastError(database->GetLastSQLError()); return false; } if (result.Count() < 1) { database->SetLastError("No buddy found"); return false; } return true; } bool psServer::AddBuddy(int self,int buddy) { //int rows=db->Command("insert into buddy_list values (%d, %d)", int rows = db->Command( "insert into character_relationships ( character_id, related_id, relationship_type ) values ( %d, %d, 'buddy' )", self, buddy); if (rows != 1) { database->SetLastError(database->GetLastSQLError()); return false; } return true; } bool psServer::RemoveBuddy(int self,int buddy) { //int rows=db->Command("delete" // " from buddy_list" // " where player_id=%d" // " and player_buddy=%d", int rows=db->Command("delete from character_relationships where character_id=%d and related_id=%d and relationship_type='buddy'", self, buddy); if (rows != 1) { database->SetLastError(database->GetLastSQLError()); return false; } return true; } #ifdef PS_EGGHUNT bool psServer::GetHiddenCrystalSpawnLoc(csVector3& pos,csString& sector,csRandomGen *randomgen) { int count = db->SelectSingleNumber("select count(*) from " "hunt_locations"); if (count) { int rule_id = randomgen->Get(count)+1; Result result (db->Select("select x,y,z,sector " " from hunt_locations " " where id=%d ", rule_id)); if (!result.IsValid()) { database->SetLastError(database->GetLastSQLError()); return false; } if (result.Count() != 1) { database->SetLastError(database->GetLastSQLError()); return false; } pos.x = atof(result[0][0]); pos.y = atof(result[0][1]); pos.z = atof(result[0][2]); sector = result[0][3]; csString escape; db->Escape( escape, sector ); int check = db->SelectSingleNumber("select count(*)" " from objects " " where locx=%1.2f " " and locy=%1.2f " " and locz=%1.2f " " and sector='%s'", pos.x,pos.y,pos.z,escape.GetData()); return (check)?false:true; // Skip this spawn if already here. } return false; } int psServer::CreateCrystalEntry(float x,float y,float z,const char *sector ) { csString escape; db->Escape( escape, sector ); db->Command("INSERT INTO hunt_locations (x,y,z,sector)" " VALUES (%1.2f, %1.2f, %1.2f, \"%s\" )", x,y,z,escape.GetData()); int id = db->SelectSingleNumber("SELECT last_insert_id()" ); return id; } #endif // Egg hunt void psServer::UpdateDialog( const char* area, const char* trigger, const char* response, int num ) { csString escTrigger; csString escArea; csString escResp; db->Escape( escTrigger, trigger ); db->Escape( escArea, area ); db->Escape( escResp, response ); // Find the response id: int id = db->SelectSingleNumber("SELECT response_id FROM npc_triggers " "WHERE trigger=\"%s\" AND area=\"%s\"", escTrigger.GetData(), escArea.GetData()); db->Command( "UPDATE npc_responses SET response%d=\"%s\" WHERE id=%d", num, escResp.GetData(), id ); } void psServer::UpdateDialog( const char* area, const char* trigger, const char* prohim, const char* proher, const char* proit, const char* prothem ) { csString escTrigger; csString escArea; csString escHim,escHer,escIt,escThem; db->Escape( escTrigger, trigger ); db->Escape( escArea, area ); db->Escape( escHim, prohim ); db->Escape( escHer, proher ); db->Escape( escIt, proit ); db->Escape( escThem, prothem ); // Find the response id: int id = db->SelectSingleNumber("SELECT response_id FROM npc_triggers " "WHERE trigger=\"%s\" AND area=\"%s\"", escTrigger.GetData(), escArea.GetData()); db->Command( "UPDATE npc_responses SET " "pronoun_him=\"%s\", " "pronoun_her=\"%s\", " "pronoun_it=\"%s\", " "pronoun_them=\"%s\" " "WHERE id=%d", escHim.GetData(), escHer.GetData(), escIt.GetData(), escThem.GetData(), id); } iResultSet* psServer::GetAllTriggersInArea(csString data) { iResultSet* rs; csString escape; db->Escape( escape, data ); rs = db->Select("SELECT trigger FROM npc_triggers " "WHERE area='%s'", escape.GetData()); if ( !rs ) { Error2("db ERROR: %s", db->GetLastError()); Error2("LAST QUERY: %s", db->GetLastQuery()); return NULL; } return rs; } iResultSet* psServer::GetAllResponses( csString& trigger ) { iResultSet* rs; csString escTrigger; db->Escape( escTrigger, trigger ); // Get the response ID: int id = db->SelectSingleNumber("SELECT response_id FROM npc_triggers " "WHERE trigger=\"%s\"", escTrigger.GetData()); if ( !id ) { Error2("db ERROR: %s", db->GetLastError()); Error2("LAST QUERY: %s", db->GetLastQuery()); return NULL; } rs = db->Select("SELECT * FROM npc_responses " "WHERE id=%d",id); if ( !rs ) { Error2("db ERROR: %s", db->GetLastError()); Error2("LAST QUERY: %s", db->GetLastQuery()); return NULL; } return rs; } iResultSet *psServer::GetSuperclientNPCs(int superclientID) { iResultSet *rs; rs = db->Select("SELECT id" " FROM characters" " WHERE account_id='%d'", superclientID); if (!rs) { database->SetLastError(database->GetLastSQLError()); } return rs; } bool psServer::GetServerOption(const char *option_name,csString& value) { csString escape; db->Escape( escape, option_name ); Result result (db->Select("select option_value from server_options where " "option_name='%s'", escape.GetData())); if (!result.IsValid()) { csString temp; temp.Format("Couldn't execute query.\nCommand was " "<%s>.\nError returned was <%s>\n", db->GetLastQuery(),db->GetLastError()); database->SetLastError(temp); return false; } if (result.Count() == 1) { value = result[0]["option_value"]; return true; } return false; }