/* * adminmanager.cpp * * Copyright (C) 2001-2005 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * 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 "iutil/object.h" #include #include #include #include #include "adminmanager.h" #include "spawnmanager.h" #include "chatmanager.h" #include "marriagemanager.h" #include "gem.h" #include "clients.h" #include "psserver.h" #include "playergroup.h" #include "util/psdatabase.h" #include "net/msghandler.h" #include "entitymanager.h" #include "bulkobjects/psnpcdialog.h" #include "bulkobjects/psnpcloader.h" #include "engine/drmessage.h" #include "util/log.h" #include "util/serverconsole.h" #include "util/strutil.h" #include "util/eventmanager.h" #include "psserver.h" #include "usermanager.h" #include "globals.h" #include "iserver/idal.h" #include "iengine/mesh.h" #include "iengine/movable.h" #include "bulkobjects/pscharacterloader.h" #include "bulkobjects/psitem.h" #include "bulkobjects/psraceinfo.h" #include "bulkobjects/psmerchantinfo.h" #include "bulkobjects/psactionlocationinfo.h" #include "cachemanager.h" #include "combatmanager.h" #include "netmanager.h" #include "npcmanager.h" #include "psserverchar.h" #include "guildmanager.h" #include "weathermanager.h" #include "commandmanager.h" #include "authentserver.h" // Show only items up to this ID when using the item spawn GUI (hide randomly generated items with IDs set above this) #define SPAWN_ITEM_ID_CEILING 10000 //----------------------------------------------------------------------------- AdminManager::AdminManager() { clients = psserver->GetNetManager()->GetConnections(); psserver->GetEventManager()->Subscribe(this,MSGTYPE_ADMIN,REQUIRE_READY_CLIENT); psserver->GetEventManager()->Subscribe(this,MSGTYPE_ADMINCMD,REQUIRE_READY_CLIENT); psserver->GetEventManager()->Subscribe(this,MSGTYPE_PETITION_REQUEST,REQUIRE_READY_CLIENT); psserver->GetEventManager()->Subscribe(this,MSGTYPE_GMGUI,REQUIRE_READY_CLIENT); psserver->GetEventManager()->Subscribe(this,MSGTYPE_GMSPAWNITEMS,REQUIRE_READY_CLIENT); psserver->GetEventManager()->Subscribe(this,MSGTYPE_GMSPAWNITEM,REQUIRE_READY_CLIENT); // this makes sure that the player dictionary exists on start up. npcdlg = new psNPCDialog(NULL); npcdlg->Initialize( db ); } AdminManager::~AdminManager() { psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_ADMIN); psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_ADMINCMD); psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_PETITION_REQUEST); psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_GMGUI); psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_GMSPAWNITEMS); psserver->GetEventManager()->Unsubscribe(this,MSGTYPE_GMSPAWNITEM); delete npcdlg; } void AdminManager::AdminItemRequestTypes(uint32_t clientnum) { csString outBuffer; outBuffer.Append("
    "); /* TODO - this is currently not used, which is good since it has a infinite loop unsigned int i=0; psItemStats *itemstats; char itemBuffer[256]; while ((itemstats=CacheManager::GetSingleton().GetBasicItemStatsByIndex(i))!=NULL) { csString escpxml = EscpXML(itemstats->GetName()); sprintf(itemBuffer, "", escpxml.GetData(), itemstats->GetUID() ); outBuffer.Append(itemBuffer); } */ outBuffer.Append("
"); psAdminMessage out( clientnum,ADMIN_ITEM_REQUEST_TYPES,outBuffer ); out.SendMessage(); } bool AdminManager::AdminItemCreate( psAdminMessage &msg ) { csRef xml = csPtr(new csTinyDocumentSystem); csRef doc = xml->CreateDocument(); const char* error = doc->Parse(msg.data); if ( error ) { Error2("Error in XML: %s", error ); return false; } csRef newitemNode = doc->GetRoot()->GetNode("NEWITEM"); if (!newitemNode) return false; return CreateItem(newitemNode->GetAttributeValue("NAME"), newitemNode->GetAttributeValueAsFloat("X"), newitemNode->GetAttributeValueAsFloat("Y"), newitemNode->GetAttributeValueAsFloat("Z"), 0.0f,newitemNode->GetAttributeValue("SECTOR"), newitemNode->GetAttributeValueAsInt("COUNT"),0,0); } void AdminManager::HandleMessage(MsgEntry *me,Client *client) { switch ( me->GetType() ) { case MSGTYPE_ADMIN: { psAdminMessage msg(me); HandleAdminMessage(me, msg,client); break; } case MSGTYPE_ADMINCMD: { psAdminCmdMessage msg(me); HandleAdminCmdMessage(me, msg,client); break; } case MSGTYPE_PETITION_REQUEST: { psPetitionRequestMessage msg(me); HandlePetitionMessage(me, msg,client); break; } case MSGTYPE_GMGUI: { psGMGuiMessage msg(me); HandleGMGuiMessage(me, msg,client); break; } case MSGTYPE_GMSPAWNITEMS: { psGMSpawnItems msg(me); SendSpawnItems(me,msg,client); break; } case MSGTYPE_GMSPAWNITEM: { psGMSpawnItem msg(me); SpawnItemInv(me,msg,client); break; } } } void AdminManager::HandleAdminMessage(MsgEntry *me, psAdminMessage & msg,Client *client) { if (!msg.valid) return; switch ( msg.command ) { case ADMIN_ITEM_REQUEST_TYPES: { AdminItemRequestTypes(me->clientnum); break; } case ADMIN_ITEM_CREATE: { if ( AdminItemCreate(msg) ) { psserver->SendSystemInfo(me->clientnum,"Item Created"); } break; } /* These three cases send a list of the races available with a particular gender */ case ADMIN_MALE_RACES: { AdminRaceRequest(me->clientnum, "M",client); break; } case ADMIN_FEMALE_RACES: { AdminRaceRequest(me->clientnum, "F",client); break; } case ADMIN_NEUTRAL_RACES: { AdminRaceRequest(me->clientnum, "N",client); break; } case ADMIN_REQUEST_KNOWLEDGE_AREAS: { AdminRequestKnowledgeAreas(me->clientnum); break; } /// A new NPC is incomming case ADMIN_CREATE_NEW_NPC: { Debug1(LOG_ADMIN,me->clientnum,"Creating new NPC!"); AdminCreateNewNPC(me->clientnum, msg.data); break; } /// A request for the triggers in a dialog area case ADMIN_REQUEST_TRIGGERS: { Debug1(LOG_ADMIN,me->clientnum,"Admin_request_triggers"); AdminRequestTriggers( me->clientnum, msg.data); break; } case ADMIN_REQUEST_RESPONSES: { Debug1(LOG_ADMIN,me->clientnum,"Admin request responses"); AdminRequestResponses(me->clientnum, msg.data); break; } case ADMIN_DIALOG_CHANGE: { Debug1(LOG_ADMIN,me->clientnum,"Admin dialog change"); AdminDialogChange(me->clientnum, msg.data); } } } bool AdminManager::IsReseting(const csString& command) { // Grab the first 8 characters after the command and see if we're resetting ourself // Everyone is allowed to reset themself via /deputize (should probably use this for other commands, too) return command.Slice(command.FindFirst(' ')+1,8) == "me reset"; } void AdminManager::HandleAdminCmdMessage(MsgEntry *me, psAdminCmdMessage & msg,Client *client) { if (!msg.valid) { psserver->SendSystemInfo(me->clientnum,"Invalid admin command"); return; } // Security check if ( !IsReseting(msg.cmd) && !Valid(client->GetSecurityLevel(), msg.command, me->clientnum) ) return; // Called functions should report all needed errors gemObject* targetobject = NULL; gemActor* targetactor = NULL; Client* targetclient = NULL; // Targeting for all commands if ( msg.player.Length() > 0 ) { if (msg.player == "target") { targetobject = client->GetTargetObject(); if (!targetobject) { psserver->SendSystemError(client->GetClientNum(), "You must have a target selected."); return; } } else if (msg.player.StartsWith("area:",true)) { int range = atoi( msg.player.Slice(msg.player.FindLast(':')+1).GetDataSafe() ); CommandArea(me,msg,client,range); return; // Done. } else { if (msg.player == "me") targetclient = client; // Self else targetclient = FindPlayerClient(msg.player,0); // Other player? if (targetclient) // Found client { targetactor = targetclient->GetActor(); targetobject = (gemObject*)targetactor; } else // Not found yet targetobject = FindObjectByString(msg.player); // Find by ID or name } } else // Only command specified; just get the target { targetobject = client->GetTargetObject(); } if (targetobject && !targetactor) // Get the actor, client, and name for a found object { targetactor = targetobject->GetActorPtr(); targetclient = targetobject->GetClient(); msg.player = (targetclient)?targetclient->GetName():targetobject->GetName(); } // Sector finding for all commands if ( msg.sector == "here" ) { iSector* here = NULL; if (client->GetActor()) here = client->GetActor()->GetSector(); if (here) msg.sector = here->QueryObject()->GetName(); else msg.sector.Clear(); // Bad sector } int targetID = 0; if (targetobject) targetID = targetobject->GetPlayerID(); LogGMCommand( client->GetPlayerID(), targetID, msg.cmd ); if (msg.command == "/npc") { CreateNPC(me,msg,client,targetactor); } else if (msg.command == "/killnpc") { KillNPC(me,msg,client); } else if (msg.command == "/item") { if (msg.item.Length()) // If arg, make simple CreateItem(me,msg,client); else // If no arg, load up the spawn item GUI SendSpawnTypes(me,msg,client); } else if (msg.command == "/key") { ModifyKey(me,msg,client); } else if (msg.command == "/rain") { Rain(me,msg,client); } else if (msg.command == "/snow") { Snow(me,msg,client); } else if (msg.command == "/thunder") { Thunder(me,msg,client); } else if (msg.command == "/fog") { Fog(me,msg,client); } else if (msg.command == "/info") { GetInfo(me,msg,client,targetobject); } else if (msg.command == "/charlist") { GetSiblingChars(me,msg,client); } else if (msg.command == "/crystal") { CreateHuntLocation(me,msg,client); } else if (msg.command == "/mute") { MutePlayer(me,msg,client,targetclient); } else if (msg.command == "/unmute") { UnmutePlayer(me,msg,client,targetclient); } else if (msg.command == "/teleport") { Teleport(me,msg,client,targetobject); } else if (msg.command == "/slide") { Slide(me,msg,client,targetobject); } else if (msg.command == "/petition") { HandleAddPetition(me,msg,client); } else if (msg.command == "/warn") { WarnMessage(me,msg,client,targetclient); } else if (msg.command == "/kick") { KickPlayer(me,msg,client,targetclient); } else if (msg.command == "/death" ) { Death(me,msg,client,targetactor); } else if (msg.command == "/impersonate" ) { Impersonate(me, msg,client); } else if (msg.command == "/deputize") { TempSecurityLevel(me,msg,client,targetclient); } else if (msg.command == "/deletechar" ) { DeleteCharacter( me, msg, client ); } else if (msg.command == "/changename" ) { ChangeName( me, msg,client ); } else if (msg.command == "/changeguildname") { RenameGuild( me, msg,client ); } else if (msg.command == "/banname" ) { BanName( me, msg,client ); } else if (msg.command == "/unbanname" ) { UnBanName( me, msg,client ); } else if (msg.command == "/ban" ) { BanClient( me,msg,client ); } else if (msg.command == "/unban" ) { UnbanClient( me,msg,client ); } else if (msg.command == "/awardexp" ) { AwardExperience(me,msg,client,targetclient); } else if (msg.command == "/giveitem" ) { TransferItem(me,msg,client,targetclient); } else if (msg.command == "/takeitem" ) { TransferItem(me,msg,targetclient,client); } else if (msg.command == "/freeze") { FreezeClient(me,msg,client,targetclient); } else if (msg.command == "/thaw") { ThawClient(me, msg, client,targetclient); } else if (msg.command == "/inspect") { Inspect(me,msg,client,targetactor); } else if (msg.command == "/modify") { ModifyHuntLocation(me,msg,client,targetobject); } else if (msg.command == "/morph") { Morph(me,msg,client,targetclient); } else if (msg.command == "/setskill") { SetSkill(me,msg,client); } else if (msg.command == "/set") { SetAttrib(me,msg,client); } else if (msg.command == "/divorce") { Divorce(me, msg, client); } else if (msg.command == "/waypoint") { Waypoint(me, msg, client); } } gemObject* AdminManager::FindObjectByString(const csString& str) { gemObject* found = NULL; GEMSupervisor *gem = GEMSupervisor::GetSingletonPtr(); if (!gem) return NULL; if ( str.StartsWith("pid:",true) ) // Find by player ID { int pID = atoi( str.Slice(4).GetDataSafe() ); if (pID) found = gem->FindPlayerEntity( pID ); } else if ( str.StartsWith("eid:",true) ) // Find by entity ID { PS_ID eID = (PS_ID)atoi( str.Slice(4).GetDataSafe() ); if (eID) found = gem->FindObject( eID ); } else // Try finding an entity by name found = gem->FindObject(str); return found; } void AdminManager::CommandArea(MsgEntry *me, psAdminCmdMessage& msg, Client *client, int range) { if (!Valid(client->GetSecurityLevel(), "command area", me->clientnum)) { psserver->SendSystemInfo(me->clientnum,"You are not allowed to use area"); return; } if (range) { gemActor* self = client->GetActor(); if (!self) { psserver->SendSystemError(client->GetClientNum(), "You do not exist..."); return; } int mode = 0; if (msg.player.StartsWith("area:players",true)) mode = 0; else if (msg.player.StartsWith("area:actors",true)) mode = 1; else if (msg.player.StartsWith("area:items",true)) mode = 2; else if (msg.player.StartsWith("area:npcs",true)) mode = 3; else if (msg.player.StartsWith("area:entities",true)) mode = 4; csVector3 pos; iSector* sector; self->GetPosition(pos,sector); GEMSupervisor* gem = GEMSupervisor::GetSingletonPtr(); if (!gem) return; csRef nearlist = gem->pl->FindNearbyEntities(sector,pos,range); size_t count = nearlist->GetCount(); size_t handled_entities = 0; for (size_t i=0; iGetObjectFromEntityList(nearlist,i); if (!nearobj) continue; switch (mode) { case 0: // Target players { if (nearobj->GetClientID()) { msg.player.Format("pid:%d",nearobj->GetPlayerID()); break; } else continue; } case 1: // Target actors { if (nearobj->GetPlayerID()) { msg.player.Format("pid:%d",nearobj->GetPlayerID()); break; } else continue; } case 2: // Target items { if (nearobj->GetItem()) { msg.player.Format("eid:%u",nearobj->GetEntity()->GetID()); break; } else continue; } case 3: // Target NPCs { if (nearobj->GetNPCPtr()) { msg.player.Format("pid:%d",nearobj->GetPlayerID()); break; } else continue; } case 4: // Target everything { msg.player.Format("eid:%u",nearobj->GetEntity()->GetID()); break; } } // Run this once for every target in range (each one will be verified and logged seperately) HandleAdminCmdMessage(me,msg,client); handled_entities++; } if (!handled_entities) { psserver->SendSystemError(client->GetClientNum(), "Nothing of specified type in range."); return; } } else { psserver->SendSystemError(client->GetClientNum(), "You must specify a range."); } } void AdminManager::GetSiblingChars(MsgEntry* me,psAdminCmdMessage& msg,Client *client) { if (!msg.player || msg.player.Length() == 0) { psserver->SendSystemError(me->clientnum, "You must specify a character name."); return; } size_t accountId = 0; Result result(db->Select("SELECT account_id FROM characters WHERE name = '%s'", msg.player.GetData())); if (result.IsValid() && result.Count()) { iResultRow& accountRow = result[0]; accountId = accountRow.GetUInt32("account_id"); psserver->SendSystemInfo(client->GetClientNum(), "Account ID of player %s is %d. Characters on this account:", msg.player.GetData(), accountId); } else { psserver->SendSystemError(me->clientnum, "No player with name %s found in database.", msg.player.GetData()); return; } if (accountId != 0) { Result result2(db->Select("SELECT id, name, lastname, last_login FROM characters WHERE account_id = %d", accountId)); if (result2.IsValid() && result2.Count()) { for (int i = 0; i < (int)result2.Count(); i++) { iResultRow& row = result2[i]; psserver->SendSystemInfo(client->GetClientNum(), "Player ID: %d, %s %s, last login: %s", row.GetUInt32("id"), row["name"], row["lastname"], row["last_login"] ); } } } else { psserver->SendSystemError(me->clientnum, "Error executing SQL-statement to retrieve characters on the account of player %s.", msg.player.GetData()); return; } } bool AdminManager::Valid( int level, const char* command, int clientnum ) { csString errorStr; if ( !CacheManager::GetSingleton().GetCommandManager()->Validate( level, command, errorStr ) ) { psserver->SendSystemError(clientnum, errorStr); return false; } return true; } void AdminManager::GetInfo(MsgEntry* me,psAdminCmdMessage& msg,Client *client, gemObject* target) { PS_ID entityId = 0; if ( target && target->GetEntity() ) entityId = target->GetEntity()->GetID(); if (target && strcmp(target->GetObjectType(), "ActionLocation") == 0) // Action location { gemActionLocation *item = dynamic_cast(target); if (!item) { psserver->SendSystemError(client->GetClientNum(), "Error! Target is not a valid gemActionLocation object."); return; } psActionLocation *action = item->GetAction(); csString info; info.Format("ActionLocation: %s with ", item->GetName()); if (action) info.AppendFmt("ID %u, and instance ID of the container %u.", action->id, action->GetInstanceIDOfContainer()); else info.Append("no action location information."); psserver->SendSystemInfo(client->GetClientNum(), info); return; } if ( target && target->GetItem() && target->GetItem()->GetBaseStats() ) // Item { psItem* item = target->GetItem(); csString info; info.Format("Item: %s ", item->GetName() ); if ( item->GetStackCount() > 1 ) info.AppendFmt("(x%d) ", item->GetStackCount() ); info.AppendFmt("with item ID %u, instance ID %u, and entity ID %u", item->GetBaseStats()->GetUID(), item->GetUID(), entityId ); if ( item->GetScheduledItem() ) info.AppendFmt(", spawns with interval %d + %d max modifier", item->GetScheduledItem()->GetInterval(), item->GetScheduledItem()->GetMaxModifier() ); // Get all flags on this item int flags = item->GetFlags(); if (flags) { info += ", has flags:"; if ( flags & PSITEM_FLAG_CRAFTER_ID_IS_VALID ) info += " 'vlaid crafter id'"; if ( flags & PSITEM_FLAG_GUILD_ID_IS_VALID ) info += " 'vlaid guild id'"; if ( flags & PSITEM_FLAG_UNIQUE_ITEM ) info += " 'unique'"; if ( flags & PSITEM_FLAG_USES_BASIC_ITEM ) info += " 'uses basic item'"; if ( flags & PSITEM_FLAG_PURIFIED ) info += " 'purified'"; if ( flags & PSITEM_FLAG_PURIFYING ) info += " 'purifying'"; if ( flags & PSITEM_FLAG_LOCKED ) info += " 'locked'"; if ( flags & PSITEM_FLAG_LOCKABLE ) info += " 'lockable'"; if ( flags & PSITEM_FLAG_NOPICKUP ) info += " 'no pickup'"; if ( flags & PSITEM_FLAG_KEY ) info += " 'key'"; if ( flags & PSITEM_FLAG_TRANSIENT ) info += " 'transient'"; } psserver->SendSystemInfo(client->GetClientNum(),info); return; // Done } char ipaddr[20] = {0}; csString name, ipAddress, securityLevel; int playerId = 0, accountId = 0; float timeConnected = 0.0f; if (target) // Online { Client* targetclient = target->GetClient(); playerId = target->GetPlayerID(); if (target->GetCharacterData()) timeConnected = target->GetCharacterData()->GetTimeConnected() / 3600; if (targetclient) // Player { name = targetclient->GetName(); targetclient->GetIPAddress(ipaddr); ipAddress = ipaddr; accountId = targetclient->GetAccountID(); // Because of /deputize we'll need to get the real SL from DB int currSL = targetclient->GetSecurityLevel(); int trueSL = GetTrueSecurityLevel(accountId); if (currSL != trueSL) securityLevel.Format("%d(%d)",currSL,trueSL); else securityLevel.Format("%d",currSL); } else // NPC { name = target->GetName(); psserver->SendSystemInfo(client->GetClientNum(), "NPC: %s has a player ID %d, entity ID %u, and has been active for %1.1f hours", name.GetData(), playerId, entityId, timeConnected ); return; // Done } } else // Offline { Result result(db->Select("SELECT id, name, lastname, account_id, time_connected_sec from characters where name='%s'", msg.player.GetData())); if (!result.IsValid() || result.Count() == 0) { psserver->SendSystemError(client->GetClientNum(), "Cannot find player %s",msg.player.GetData() ); return; } else { iResultRow& row = result[0]; name = row["name"]; if (row["lastname"] != "") { name.Append(" "); name.Append(row["lastname"]); } playerId = row.GetUInt32("id"); accountId = row.GetUInt32("account_id"); ipAddress = "(offline)"; timeConnected = row.GetFloat("time_connected_sec") / 3600; securityLevel.Format("%d",GetTrueSecurityLevel(accountId)); } } if (playerId != 0) { csString info; info.Format("Player: %s has ", name.GetData() ); if (securityLevel != "0") info.AppendFmt("security level %s, ", securityLevel.GetData() ); info.AppendFmt("account ID %d, player ID %d, ", accountId, playerId ); if (ipAddress != "(offline)") info.AppendFmt("entity ID %u, IP is %s, ", entityId, ipAddress.GetData() ); else info.Append("is offline, "); info.AppendFmt("total time connected is %1.1f hours", timeConnected ); psserver->SendSystemInfo(client->GetClientNum(),info); } else { psserver->SendSystemError(client->GetClientNum(), "Error! Object is not an item, player, or NPC." ); } } void AdminManager::HandlePetitionMessage(MsgEntry *me, psPetitionRequestMessage& msg,Client *client) { // Check which message and if this is a GM message or user message if (msg.request == "query") { if (msg.isGM) GMListPetitions(me, msg,client); else ListPetitions(me, msg,client); } else if (msg.request == "cancel" && !msg.isGM) { CancelPetition(me, msg,client); } else if (msg.isGM) { GMHandlePetition(me, msg,client); } } void AdminManager::HandleGMGuiMessage(MsgEntry *me, psGMGuiMessage& msg,Client *client) { if (msg.type == psGMGuiMessage::TYPE_QUERYPLAYERLIST) { SendGMPlayerList(me, msg,client); } } void AdminManager::CreateHuntLocation(MsgEntry* me,psAdminCmdMessage& msg,Client *client) { if (msg.item.IsEmpty()) { psserver->SendSystemError(me->clientnum, "Insufficent parameters. Use /crystal "); return; } if (msg.interval != msg.interval || msg.random != msg.random) { psserver->SendSystemError(me->clientnum, "Invalid interval(s)"); return; } if (msg.interval < 1 || msg.random < 1) { psserver->SendSystemError(me->clientnum, "Intervals need to be greater than 0"); return; } // In seconds int interval = 1000*msg.interval; int random = 1000*msg.random; psItemStats* rawitem = CacheManager::GetSingleton().GetBasicItemStatsByName(msg.item); if (!rawitem) { psserver->SendSystemError(me->clientnum, "Invalid item to spawn"); return; } // Find the location csVector3 pos; float angle; iSector* sector = 0; client->GetActor()->GetPosition(pos, angle, sector); if (!sector) { psserver->SendSystemError(me->clientnum, "Invalid sector"); return; } psSectorInfo *spawnsector = CacheManager::GetSingleton().GetSectorInfoByName(sector->QueryObject()->GetName()); if(!spawnsector) { CPrintf(CON_ERROR,"Player is in invaild sector %s!",sector->QueryObject()->GetName()); return; } // to db db->Command( "INSERT INTO hunt_locations" "(`x`,`y`,`z`,`itemid`,`sector`,`interval`,`max_random`)" "VALUES ('%f','%f','%f','%u','%s','%d','%d')", pos.x,pos.y,pos.z, rawitem->GetUID(),sector->QueryObject()->GetName(),interval,random); psScheduledItem* schedule = new psScheduledItem(db->GetLastInsertID(),rawitem->GetUID(),pos,spawnsector,interval,random); psItemSpawnEvent* event = new psItemSpawnEvent(schedule); psserver->GetEventManager()->Push(event); // Done! psserver->SendSystemInfo(me->clientnum,"New hunt location created!"); } void AdminManager::SetAttrib(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { gemActor * actor = client->GetActor(); bool onoff = false; bool toggle = false; bool already = false; if (msg.setting == "on") onoff = true; else if (msg.setting == "off") onoff = false; else toggle = true; if (msg.attribute == "list") { psserver->SendSystemInfo(me->clientnum, "invincible = %s\n" "invisible = %s\n" "viewall = %s\n" "nevertired = %s\n" "nofalldamage = %s\n" "infiniteinventory = %s\n" "questtester = %s", (actor->GetInvincibility())?"on":"off", (!actor->GetVisibility())?"on":"off", (actor->GetViewAllObjects())?"on":"off", (actor->nevertired)?"on":"off", (actor->safefall)?"on":"off", (!actor->GetFiniteInventory())?"on":"off", (actor->questtester)?"on":"off" ); return; } else if (msg.attribute == "invinciblity" || msg.attribute == "invincible") { if (toggle) { actor->SetInvincibility(!actor->GetInvincibility()); onoff = actor->GetInvincibility(); } else if (actor->GetInvincibility() == onoff) already = true; else actor->SetInvincibility(onoff); } else if (msg.attribute == "invisiblity" || msg.attribute == "invisible") { if (toggle) { actor->SetVisibility(!actor->GetVisibility()); onoff = !actor->GetVisibility(); } else if (actor->GetVisibility() == !onoff) already = true; else actor->SetVisibility(!onoff); } else if (msg.attribute == "viewall") { if (toggle) { actor->SetViewAllObjects(!actor->GetViewAllObjects()); onoff = actor->GetViewAllObjects(); } else if (actor->GetViewAllObjects() == onoff) already = true; else actor->SetViewAllObjects(onoff); } else if (msg.attribute == "nevertired") { if (toggle) { actor->nevertired = !actor->nevertired; onoff = actor->nevertired; } else if (actor->nevertired == onoff) already = true; else actor->nevertired = onoff; } else if (msg.attribute == "nofalldamage") { if (toggle) { actor->safefall = !actor->safefall; onoff = actor->safefall; } else if (actor->safefall == onoff) already = true; else actor->safefall = onoff; } else if (msg.attribute == "infiniteinventory") { if (toggle) { actor->SetFiniteInventory(!actor->GetFiniteInventory()); onoff = !actor->GetFiniteInventory(); } else if (actor->GetFiniteInventory() == !onoff) already = true; else actor->SetFiniteInventory(!onoff); } else if (msg.attribute == "questtester") { if (toggle) { actor->questtester = !actor->questtester; onoff = actor->questtester; } else if (actor->questtester == onoff) already = true; else actor->questtester = onoff; } else if (!msg.attribute.IsEmpty()) { psserver->SendSystemInfo(me->clientnum, "%s is not a supported attribute", msg.attribute.GetData() ); return; } else { psserver->SendSystemInfo(me->clientnum, "Correct syntax is: \"/set [attribute] [on|off]\""); return; } psserver->SendSystemInfo(me->clientnum, "%s %s %s", msg.attribute.GetData(), (already)?"is already":"has been", (onoff)?"enabled":"disabled" ); } void AdminManager::Divorce(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { bool onlineDivorcer = false; bool onlineTarget = false; if ( !msg.player.Length()) { psserver->SendSystemInfo( me->clientnum, "Usage: \"/divorce [character]\""); return; } Client* divorcer = clients->Find( msg.player ); onlineDivorcer = ( divorcer != NULL ); //If the player that wishes to divorce is not online, we can't proceed. if ( !onlineDivorcer ) { psserver->SendSystemInfo( me->clientnum, "The player that wishes to divorce must be online." ); return; } psCharacter* divorcerChar = divorcer->GetCharacterData(); //If the player is not married, we can't divorce anything. if( !divorcerChar->GetIsMarried() ) { psserver->SendSystemInfo( me->clientnum, "For obtaining a divorce, there must be a marriage before."); return; } csString spouseFullName = divorcerChar->GetSpouseName(); csString spouseName = spouseFullName.Slice( 0, spouseFullName.FindFirst(' ')); //If the "spouse" is online, than there is no need to use GM powers for the divorce. Client* target = clients->Find( spouseName ); onlineTarget = ( target != NULL ); if ( onlineTarget ) { psserver->SendSystemInfo( me->clientnum, "The two players can agree by themselves on the divorce, since both are online."); return; } if ( !psserver->GetCharManager()->HasConnected( spouseName ))//The player was connected recently. { psserver->SendSystemInfo( me->clientnum, "The spouse has been online recently, therefore it is not up to you to divorce the couple."); return; } //Now is it time to divorce the player. psMarriageManager* marriageMgr = psserver->GetMarriageManager(); if ( !marriageMgr ) { psserver->SendSystemError( me->clientnum, "Can't load MarriageManager."); Error1( "MarriageManager failed to load." ); return; } // Delete entries of character's from DB marriageMgr->DeleteMarriageInfo( divorcerChar ); psserver->SendSystemInfo( me->clientnum, "You have divorced %s from %s.", msg.player.GetData(), spouseName.GetData() ); Debug3( LOG_MARRIAGE, me->clientnum, "%s divorced from %s.", msg.player.GetData(), spouseName.GetData() ); } void AdminManager::Teleport(MsgEntry* me, psAdminCmdMessage& msg, Client *client, gemObject* subject) { if (msg.target == "") { psserver->SendSystemInfo(client->GetClientNum(), "Use: /teleport subject destination\n" "Subject : me/target////eid:/pid:\n" "Destination: here/last/spawn/restore/map [|here] x y z\n"); return; } // If player is offline and the special argument is called if (subject == NULL && msg.target == "restore") { psString sql; iSector * mySector; csVector3 myPoint; float yRot = 0.0; client->GetActor()->GetPosition(myPoint, yRot, mySector); psSectorInfo * mysectorinfo = NULL; if (mySector != NULL) mysectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(mySector->QueryObject()->GetName()); if (mysectorinfo == NULL) { psserver->SendSystemError(client->GetClientNum(), "Sector not found!"); return; } sql.AppendFmt("update characters set loc_x=%10.2f, loc_y=%10.2f, loc_z=%10.2f, loc_yrot=%10.2f, loc_sector_id=%u where name='%s'", myPoint.x, myPoint.y, myPoint.z, yRot, mysectorinfo->uid, msg.player.GetData()); if (db->Command(sql) != 1) { Error3 ("Couldn't save character's position to database.\nCommand was " "<%s>.\nError returned was <%s>\n",db->GetLastQuery(),db->GetLastError()); psserver->SendSystemError(client->GetClientNum(), "Offline character %s could not be moved!", msg.player.GetData()); } else psserver->SendSystemResult(client->GetClientNum(), "%s will next log in at your current location", msg.player.GetData()); return; } else if (subject == NULL) { psserver->SendSystemError(client->GetClientNum(), "Cannot teleport target"); return; } csVector3 targetPoint; float yRot = 0.0; iSector *targetSector; if ( !GetTargetOfTeleport(client, msg, targetSector, targetPoint, yRot, subject) ) { psserver->SendSystemError(client->GetClientNum(), "Cannot teleport %s to %s", msg.player.GetData(), msg.target.GetData() ); return; } //Error6("tele %s to %s %f %f %f",subject->GetName(), targetSector->QueryObject()->GetName(), targetPoint.x, targetPoint.y, targetPoint.z); csVector3 oldpos; float oldyrot; iSector *oldsector; subject->GetPosition(oldpos,oldyrot,oldsector); if ( (oldsector == targetSector) && (oldpos == targetPoint) ) { psserver->SendSystemError(client->GetClientNum(), "What's the point?"); return; } // Do the teleport if ( !MoveObject(client,subject,targetPoint,yRot,targetSector) ) return; csString destName; if (msg.map.Length()) destName.Format("map %s", msg.map.GetData() ); else destName.Format("sector %s", targetSector->QueryObject()->GetName() ); // Update ProxList on sector crossing if (oldsector != targetSector) { subject->UpdateProxList(true); psserver->SendSystemOK(subject->GetClientID(), "Welcome to " + destName); } else subject->UpdateProxList(false); // Update ProxList if needed if ( dynamic_cast(subject) ) // Record old location of actor, for undo ((gemActor*)subject)->UpdateValidLocation(oldpos, 0.0f, oldyrot, oldsector, true); // Send explanations if (subject->GetClientID() != (int)client->GetClientNum()) { psserver->SendSystemResult(client->GetClientNum(), "Teleported %s to %s", subject->GetName(), ((msg.target=="map")?destName:msg.target).GetData() ); psserver->SendSystemResult(subject->GetClientID(), "You were moved by a GM"); } if (msg.player == "me" && msg.target != "map" && msg.target != "here") { psGUITargetUpdateMessage updateMessage( client->GetClientNum(), subject->GetEntity()->GetID() ); updateMessage.SendMessage(); } } int AdminManager::WaypointCreate(csString& name, csVector3& pos, csString& sectorName, float radius, csString& flags) { const char *fieldnames[]= { "name", "x", "y", "z", "radius", "flags", "loc_sector_id" }; psSectorInfo * si = CacheManager::GetSingleton().GetSectorInfoByName(sectorName); psStringArray values; values.FormatPush("%s", name.GetDataSafe()); values.FormatPush("%10.2f",pos.x); values.FormatPush("%10.2f",pos.y); values.FormatPush("%10.2f",pos.z); values.FormatPush("%10.2f",radius); values.FormatPush("%s",flags.GetDataSafe()); values.FormatPush("%u",si->uid); unsigned int id = db->GenericInsertWithID("sc_waypoints",fieldnames,values); if (id==0) { Error2("Failed to create new WP Error %s",db->GetLastError()); return -1; } return id; } void AdminManager::Waypoint(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if ( !msg.waypointCmd.Length()) { psserver->SendSystemInfo( me->clientnum, "Usage: \"/waypoint [add|create|link|path]\""); return; } if (msg.waypointCmd == "help") { psserver->SendSystemInfo( me->clientnum, "/waypoint help\n" "/waypoint add radius [flags]\n" "/waypoint create WP radius [flags]\n" "/waypoint link WP1 WP2 [flags]\n" "/waypoint path format\n" "/waypoint display\n" "/waypoint hide"); } else if (msg.waypointCmd == "path") { client->WaypointSetPath(msg.wp1,msg.value); csString wp; wp.Format(client->WaypointGetPathName(),client->WaypointGetPathIndex()); psserver->SendSystemInfo( me->clientnum, "New waypoint path. First WP will be: '%s'",wp.GetDataSafe()); } else if (msg.waypointCmd == "create") { if ( !msg.wp1.Length() || msg.radius == 0.0f) { psserver->SendSystemInfo( me->clientnum, "Usage: \"/waypoint create WP radius [flags]\""); return; } csVector3 myPos; float myRotY; iSector* mySector = 0; int wp_id; client->GetActor()->GetPosition(myPos, myRotY, mySector); csString sectorName = mySector->QueryObject()->GetName(); wp_id = WaypointCreate(msg.wp1,myPos,sectorName,msg.radius,msg.attribute); psserver->SendSystemInfo( me->clientnum, "Created new WP %u",wp_id); } else if (msg.waypointCmd == "add") { if ( msg.radius == 0.0f ) { psserver->SendSystemInfo( me->clientnum, "Usage: \"/waypoint add radius [flags]\""); return; } csVector3 myPos; float myRotY; iSector* mySector = 0; int wp_id,wp_last; client->GetActor()->GetPosition(myPos, myRotY, mySector); csString sectorName = mySector->QueryObject()->GetName(); csString wp; wp.Format(client->WaypointGetPathName(),client->WaypointGetNewPathIndex()); wp_id = WaypointCreate(wp,myPos,sectorName,msg.radius,msg.attribute); wp_last = client->WaypointGetLast(); if (wp_last != -1) { const char *fieldnames[]= { "wp1", "wp2", "flags" }; psStringArray values; values.FormatPush("%d", wp_last); values.FormatPush("%d", wp_id); values.FormatPush("%s",msg.attribute2.GetDataSafe()); unsigned int link_id = db->GenericInsertWithID("sc_waypoint_links",fieldnames,values); if (link_id==0) { Error2("Failed to create new WP Link Error %s",db->GetLastError()); } } client->WaypointSetLast(wp_id); psserver->SendSystemInfo( me->clientnum, "Created new WP %s(%u)",wp.GetDataSafe(),wp_id); } else if (msg.waypointCmd == "link") { if ( !msg.wp1.Length() || !msg.wp2.Length()) { psserver->SendSystemInfo( me->clientnum, "Usage: \"/waypoint link wp1 wp2 [flags]\""); return; } const char *fieldnames[]= { "wp1", "wp2", "flags" }; psStringArray values; values.FormatPush("%s", msg.wp1.GetDataSafe()); values.FormatPush("%s", msg.wp2.GetDataSafe()); values.FormatPush("%s",msg.attribute.GetDataSafe()); unsigned int link_id = db->GenericInsertWithID("sc_waypoint_links",fieldnames,values); psserver->SendSystemInfo( me->clientnum, "Created new WP link %u",link_id); } else if (msg.waypointCmd == "display") { csVector3 myPos; float myRotY; iSector* mySector = 0; client->GetActor()->GetPosition(myPos, myRotY, mySector); csString sectorName = mySector->QueryObject()->GetName(); Result rs(db->Select("select wp.* from sc_waypoints wp, sectors s where wp.loc_sector_id = s.id and s.name ='%s'",sectorName.GetDataSafe())); if (!rs.IsValid()) { Error2("Could not load waypoints from db: %s",db->GetLastError() ); return ; } for (int i=0; i<(int)rs.Count(); i++) { csVector3 pos(rs[i].GetFloat("x"),rs[i].GetFloat("y"),rs[i].GetFloat("z")); psEffectMessage msg(me->clientnum,"x_obj_star",pos,0,0,client->WaypointGetEffectID()); msg.SendMessage(); } psserver->SendSystemInfo(me->clientnum, "Displaying all WP in sector"); } else if (msg.waypointCmd == "hide") { psStopEffectMessage msg(me->clientnum,client->WaypointGetEffectID()); msg.SendMessage(); psserver->SendSystemInfo(me->clientnum, "All WPs hidden"); } } bool AdminManager::GetTargetOfTeleport(Client *client, psAdminCmdMessage& msg, iSector * & targetSector, csVector3 & targetPoint, float &yRot, gemObject *subject) { // when teleporting to a map if (msg.target == "map") { if (msg.sector.Length()) { targetSector = EntityManager::GetSingleton().GetEngine()->FindSector(msg.sector); if (!targetSector) { psserver->SendSystemError(client->GetClientNum(), "Cannot find sector " + msg.sector); return false; } targetPoint = csVector3(msg.x, msg.y, msg.z); } else { return GetStartOfMap(client, msg.map, targetSector, targetPoint); } } // when teleporting to the place where we are standing at else if (msg.target == "here") { client->GetActor()->GetPosition(targetPoint, yRot, targetSector); } // Teleport to last valid location (force unstick/teleport undo) else if (msg.target == "last") { if ( dynamic_cast(subject) ) ((gemActor*)subject)->GetValidPos(targetPoint, yRot, targetSector); else return false; // Actors only } // Teleport to spawn point else if (msg.target == "spawn") { if ( dynamic_cast(subject) ) ((gemActor*)subject)->GetSpawnPos(targetPoint, yRot, targetSector); else return false; // Actors only } // Teleport to target else if (msg.target == "target") { gemObject* obj = client->GetTargetObject(); if (!obj) return false; obj->GetPosition(targetPoint, yRot, targetSector); } // when teleporting to a player/npc else { Client * player = FindPlayerClient(msg.target,0); if (player) { player->GetActor()->GetPosition(targetPoint, yRot, targetSector); } else { gemObject* obj = FindObjectByString(msg.target); // Find by ID or name if (!obj) // Didn't find return false; obj->GetPosition(targetPoint, yRot, targetSector); } } return true; } bool AdminManager::GetStartOfMap(Client * client, const csString & map, iSector * & targetSector, csVector3 & targetPoint) { iEngine* engine = EntityManager::GetSingleton().GetEngine(); iRegionList* regions = engine->GetRegions(); assert(regions); if ( map.Length() == 0 ) { psserver->SendSystemError( client->GetClientNum(), "Map name not given"); return false; } iRegion* region = regions->FindByName(map.GetData()); if (region == NULL) { psserver->SendSystemError(client->GetClientNum(), "Map not found."); return false; } iCameraPosition* loc = region->FindCameraPosition("Camera01"); if (loc == NULL) { psserver->SendSystemError(client->GetClientNum(), "Starting location not found in map."); return false; } targetSector = engine->FindSector(loc->GetSector()); targetPoint = loc->GetPosition(); return true; } void AdminManager::Slide(MsgEntry* me, psAdminCmdMessage& msg, Client *client, gemObject *target) { if (!target) { psserver->SendSystemError(me->clientnum, "Invalid target"); return; } if (msg.direction.IsEmpty()) { psserver->SendSystemError(me->clientnum, "Syntax: /slide [name|'target'] [direction]\nAllowed directions: U D L R F B T"); return; } float slideAmount = (msg.amt == 0)?1:msg.amt; // default to 1 if (slideAmount > 1000 || slideAmount < -1000 || slideAmount != slideAmount) // Check bounds and NaN { psserver->SendSystemError(me->clientnum, "Invalid slide amount"); return; } csVector3 pos; float yrot; iSector* sector = 0; target->GetPosition(pos, yrot, sector); if (sector) { switch (toupper(msg.direction.GetAt(0))) { case 'U': pos.y += slideAmount; break; case 'D': pos.y -= slideAmount; break; case 'L': pos.x += slideAmount*cos(yrot); pos.z -= slideAmount*sin(yrot); break; case 'R': pos.x -= slideAmount*cos(yrot); pos.z += slideAmount*sin(yrot); break; case 'F': pos.x -= slideAmount*sin(yrot); pos.z -= slideAmount*cos(yrot); break; case 'B': pos.x += slideAmount*sin(yrot); pos.z += slideAmount*cos(yrot); break; case 'T': slideAmount = (msg.amt == 0)?90:msg.amt; // defualt to 90 deg yrot += slideAmount*PI/180; // Rotation units are degrees break; default: psserver->SendSystemError(me->clientnum, "Invalid direction given (Use one of: U D L R F B T)"); return; } // Update the object if ( !MoveObject(client,target,pos,yrot,sector) ) return; target->UpdateProxList(false); // Update ProxList if needed if (target->GetActorPtr() && client->GetActor() != target->GetActorPtr()) psserver->SendSystemInfo(me->clientnum, "Sliding %s...", target->GetName()); } else { psserver->SendSystemError(me->clientnum, "Invalid sector; cannot slide. Please contact Planeshift support."); } } bool AdminManager::MoveObject(Client *client, gemObject *target, csVector3& pos, float yrot, iSector* sector) { // This is a powerful feature; not everyone is allowed to use all of it csString response; bool allowedToMoveOthers = CacheManager::GetSingleton().GetCommandManager()->Validate(client->GetSecurityLevel(), "move others", response); if ( client->GetActor() != (gemActor*)target && !allowedToMoveOthers ) { psserver->SendSystemError(client->GetClientNum(),response); return false; } if ( dynamic_cast(target) ) // Item? { gemItem* item = (gemItem*)target; // Check to see if this client has the admin level to move this particular item bool extras = CacheManager::GetSingleton().GetCommandManager()->Validate(client->GetSecurityLevel(), "move unpickupables/spawns", response); if ( !item->IsPickable() && !extras ) { psserver->SendSystemError(client->GetClientNum(),response); return false; } // Move the item item->GetMeshWrapper()->GetMovable()->SetPosition(pos); item->SetPosition(pos,yrot,sector); // Check to see if this client has the admin level to move this spawn point if ( item->GetItem()->GetScheduledItem() && extras ) { psserver->SendSystemInfo(client->GetClientNum(), "Moving spawn point for %s", item->GetName()); // Update spawn pos item->GetItem()->GetScheduledItem()->UpdatePosition(pos,sector->QueryObject()->GetName()); } } else if ( dynamic_cast(target) ) // Actor? (Player/NPC) { gemActor* actor = (gemActor*)target; actor->pcmove->SetVelocity(csVector3(0.0f,0.0f,0.0f)); // Halt actor actor->SetPosition(pos,yrot,sector); actor->GetCharacterData()->SaveLocationInWorld(); // force save the pos actor->MulticastDRUpdate(); } else { psserver->SendSystemError(client->GetClientNum(),"Unknown target type"); return false; } return true; } void AdminManager::CreateNPC(MsgEntry* me,psAdminCmdMessage& msg,Client *client, gemActor* basis) { if (!basis || !basis->GetCharacterData()) { psserver->SendSystemError(me->clientnum, "Invalid target"); return; } unsigned int masterNPC = 0; gemNPC *masternpc = basis->GetNPCPtr(); if (masternpc) masterNPC = masternpc->GetCharacterData()->GetCharacterID(); if (masterNPC == 0) { psserver->SendSystemError(me->clientnum, "%s was not found as a valid master NPC", basis->GetName() ); return; } if ( !psserver->GetConnections()->FindAccount(masternpc->GetSuperclientID()) ) { psserver->SendSystemError(me->clientnum, "%s's superclient is not online", basis->GetName() ); return; } csVector3 pos; float angle; psSectorInfo* sectorInfo = NULL; client->GetActor()->GetCharacterData()->GetLocationInWorld(sectorInfo, pos.x, pos.y, pos.z, angle ); iSector* sector = NULL; if (sectorInfo != NULL) sector = EntityManager::GetSingleton().FindSector(sectorInfo->name); if (sector == NULL) { psserver->SendSystemError(me->clientnum, "Invalid sector"); return; } // Copy the master NPC into a new player record, with all child tables also unsigned int newNPCID = CopyNPCFromDatabase(masterNPC, pos.x, pos.y, pos.z, angle, sectorInfo->name ); if (newNPCID == 0) { psserver->SendSystemError(me->clientnum, "Could not copy the master NPC"); return; } // Make new entity PS_ID eid = EntityManager::GetSingleton().CreateNPC(newNPCID, false); // Get gemNPC for new entity gemNPC* npc = GEMSupervisor::GetSingleton().FindNPCEntity(newNPCID); if (npc == NULL) { psserver->SendSystemError(client->GetClientNum(), "Could not find GEM and set its location"); return; } npc->GetCharacterData()->SetLocationInWorld(sectorInfo, pos.x, pos.y, pos.z, angle); npc->SetPosition(pos, angle, sector); psserver->npcmanager->ControlNPC(npc); psserver->npcmanager->NewNPCNotify(npc, masterNPC); npc->UpdateProxList(true); psserver->SendSystemInfo(me->clientnum, "New %s with PID %u and EID %u at (%1.2f,%1.2f,%1.2f) in %s.", npc->GetName(), newNPCID, eid, pos.x, pos.y, pos.z, sectorInfo->name.GetData() ); psserver->SendSystemOK(me->clientnum, "New NPC created!"); } int AdminManager::CopyNPCFromDatabase(int master_id, float x, float y, float z, float angle, const csString & sector) { psCharacter* npc = NULL; int new_id; npc = psServer::CharacterLoader.LoadCharacterData(master_id,false); if (npc == NULL) return 0; psSectorInfo* sectorInfo = CacheManager::GetSingleton().GetSectorInfoByName( sector ); if (sectorInfo != NULL) npc->SetLocationInWorld(sectorInfo,x,y,z,angle); if (psServer::CharacterLoader.NewNPCCharacterData(0, npc)) { new_id = npc->GetCharacterID(); db->Command("update characters set npc_master_id=%i where id=%i", master_id, new_id); } else new_id = 0; delete npc; return new_id; } void AdminManager::CreateItem(MsgEntry* me, psAdminCmdMessage& msg,Client *client) { csVector3 pos; iSector* sector = 0; float angle; client->GetActor()->GetPosition(pos, angle, sector); // TODO: Get number of items to create from client int stackCount = 1; if (CreateItem((const char*)msg.item,pos.x,pos.y,pos.z,angle,sector->QueryObject()->GetName(),stackCount,msg.random,msg.value)) { psserver->SendSystemInfo(me->clientnum, "New item %s added!",msg.item.GetData()); } else { psserver->SendSystemError(me->clientnum, "Can't create item %s!",msg.item.GetData()); } } bool AdminManager::CreateItem(const char * name, double xPos, double yPos, double zPos, float angle, const char * sector, int stackCount, int random, int cap) { psSectorInfo *sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(sector); if (sectorinfo==NULL) { Error2("'%s' was not found as a valid sector.",sector); return false; } if ( name == NULL ) { Error1( "No item name was given" ); return false; } // retrieve base stats item psItemStats *basestats=CacheManager::GetSingleton().GetBasicItemStatsByName(name); if (basestats==NULL) { Error2("'%s' was not found as a valid base item.",name); return false; } psItem *newitem = NULL; // randomize if requested if (random) { LootRandomizer* lootRandomizer = psserver->GetSpawnManager()->GetLootRandomizer(); psItemStats *newstats = lootRandomizer->RandomizeItem( basestats, cap ); newitem = newstats->InstantiateBasicItem(true); } else newitem = basestats->InstantiateBasicItem(true); if (newitem==NULL) { Error2("Could not instanciate from base item '%s'.",name); return false; } newitem->SetItemQuality(basestats->GetQuality()); newitem->SetStackCount(stackCount); newitem->SetLocationInWorld(sectorinfo,xPos,yPos,zPos,angle); if (!EntityManager::GetSingleton().CreateItem(newitem, true)) { delete newitem; return false; } newitem->SetLoaded(); // Item is fully created newitem->Save(); // First save return true; } void AdminManager::ModifyKey(MsgEntry *me, psAdminCmdMessage& msg,Client *client) { psItem* key = client->GetCharacterData()->Inventory().GetEquipmentItem(PSCHARACTER_SLOT_RIGHTHAND); if (msg.keyCmd == "make") { if ( !key ) key = client->GetCharacterData()->Inventory().GetEquipmentItem(PSCHARACTER_SLOT_LEFTHAND); if ( !key ) { psserver->SendSystemError(me->clientnum,"You need to be holding the object you want to make a key"); return; } if ( key->GetIsKey() ) { psserver->SendSystemError(me->clientnum,"item %s is already a key", key->GetName()); return; } key->SetIsKey(true); key->Save(); psserver->SendSystemOK(me->clientnum,"item %s is now a key", key->GetName()); return; } if ( !key || !key->GetIsKey() ) { if (key) psserver->SendSystemError(me->clientnum,"item in right hand %s is not a key", key->GetName()); key = client->GetCharacterData()->Inventory().GetEquipmentItem(PSCHARACTER_SLOT_LEFTHAND); } if ( !key || !key->GetIsKey() ) { if (key) psserver->SendSystemError(me->clientnum,"item in left hand %s is not a key", key->GetName()); psserver->SendSystemError(me->clientnum,"You need to be holding the key you want to work on"); return; } if ( msg.keyCmd == "clearlocks" ) { key->ClearOpenableLocks(); key->Save(); psserver->SendSystemInfo(me->clientnum, "Your %s can no longer unlock anything", key->GetName()); return; } if ( msg.keyCmd == "addlock" || msg.keyCmd == "removelock" ) { gemItem* target = (gemItem*)client->GetTargetObject(); if(!target) { if ( msg.keyCmd == "addlock" ) psserver->SendSystemError(me->clientnum,"You need to target the item you want to encode the key to unlock"); else psserver->SendSystemError(me->clientnum,"You need to target the item you want to stop the key from unlocking"); return; } psItem* item = target->GetItem(); if ( !item ) { Error1("Found gemItem but no psItem was attached!\n"); return; } if(!item->GetIsLockable()) { psserver->SendSystemError(me->clientnum,"This object isn't lockable"); return; } if ( msg.keyCmd == "addlock" ) { key->AddOpenableLock(item->GetUID()); psserver->SendSystemInfo(me->clientnum, "You encoded %s to unlock %s", key->GetName(), item->GetName()); } else { key->RemoveOpenableLock(item->GetUID()); psserver->SendSystemInfo(me->clientnum, "Your %s can no longer unlock %s", key->GetName(), item->GetName()); } key->Save(); return; } if ( msg.keyCmd == "skel" ) { bool b = key->GetIsSkeleton(); key->MakeSkeleton(!b); if (b) psserver->SendSystemInfo(me->clientnum, "Your %s is no longer a skeleton key", key->GetName()); else psserver->SendSystemInfo(me->clientnum, "Your %s is now a skeleton key", key->GetName()); key->Save(); } } void AdminManager::KillNPC (MsgEntry *me, psAdminCmdMessage& msg, Client *client ) { gemObject* obj = NULL; if(msg.target.Length() != 0) if (msg.target.FindFirst(':')!=(size_t)-1) obj = GEMSupervisor::GetSingleton().FindNPCEntity(atoi(msg.target.Slice(3).GetData())); if ( !obj ) obj = client->GetTargetObject(); if ( obj ) { gemActor *target = obj->GetActorPtr(); if (target && target->GetClientID() == 0) { if (msg.action != "reload") target->Kill(client->GetActor()); else { unsigned int npcid = target->GetCharacterData()->GetCharacterID(); psCharacter * npcdata = psServer::CharacterLoader.LoadCharacterData(npcid,true); EntityManager::GetSingleton().RemoveActor(obj); EntityManager::GetSingleton().CreateNPC(npcdata); psserver->SendSystemResult(me->clientnum, "NPC (id %d) has been reloaded.",npcid); } return; } } psserver->SendSystemError(me->clientnum, "No NPC was targeted."); } void AdminManager::Admin ( int playerID, int clientnum,Client *client ) { // Set client security level in case security level have // changed in database. csString commandList; int type = client->GetSecurityLevel(); // for now consider all levels > 30 as level 30. if (type>30) type=30; CacheManager::GetSingleton().GetCommandManager()->BuildXML( type, commandList ); psAdminMessage admin(clientnum, ADMIN_ACCESS, commandList.GetDataSafe() ); admin.SendMessage(); } void AdminManager::AdminRaceRequest(int clientnum, const char* sex,Client *client) { // Check to see if this client has admin level if ( client->GetSecurityLevel() < GM_LEVEL_4 ) { //TODO: Should be an automatic kick here for trying to cheat return; } // Builds up the xml list psAdminRaceList list; psRaceInfo *raceinfo; int i=0; PSCHARACTER_GENDER gender; gender=PSCHARACTER_GENDER_NONE; if (sex[0]=='M' || sex[0]=='m') gender=PSCHARACTER_GENDER_MALE; if (sex[0]=='F' || sex[0]=='f') gender=PSCHARACTER_GENDER_FEMALE; while ((raceinfo=CacheManager::GetSingleton().GetRaceInfoByIndex(i++))!=NULL) { if (raceinfo->gender==gender) list.AddRace(raceinfo->name); } // Send race list back to client psAdminMessage mesg( clientnum, ADMIN_RACES_LIST, list.XML() ); mesg.SendMessage(); } void AdminManager::AdminRequestKnowledgeAreas( int clientnum ) { psAdminKnowledgeAreaList list; /// Fill up the XML list Result result(db->Select("SELECT DISTINCT area FROM npc_knowledge_areas")); if (!result.IsValid()) { Error1("Error with npc_knowledge_areas table!"); return; } for ( unsigned int i=0; i < result.Count(); i++ ) { list.AddArea( result[i][0] ); } psAdminMessage mesg( clientnum, ADMIN_REQUEST_KNOWLEDGE_AREAS, list.XML() ); mesg.SendMessage(); } class psAdminGameEvent : public psGameEvent { public: psAdminGameEvent( AdminManager * mgr, csString& NPCName ) : psGameEvent(0,0,"psAdminGameEvent"),adminMgr(mgr),name(NPCName) { } virtual void Trigger() { adminMgr->AdminCreateNewNPC(0,name); } private: AdminManager * adminMgr; csString name; }; /** * This function is thread safe, uses a game event to call * the executing function. */ void AdminManager::AdminCreateNewNPC(csString& data) { psserver->GetEventManager()->Push( new psAdminGameEvent(this, data ) ); } void AdminManager::AdminCreateNewNPC(int clientnum, csString& data) { CPrintf(CON_DEBUG, "Start loading npc: %s\n",data.GetData()); // Removed pending changes /* bool success = false; // Create the npcdata cracker psAdminNPCData * newnpc = new psAdminNPCData(data); // Insert data into the database int id = database->CreateNewNPC(newnpc); if ( id ) { #if 1 { Debug1(LOG_ADMIN,"Dictionary Exists"); int totalTrig = newnpc->dialogManager->triggerIDs.Length(); for ( int trig = 0; trig < totalTrig; trig++ ) { int idToAdd = newnpc->dialogManager->triggerIDs[trig]; npcdlg->AddNewTrigger( idToAdd ); } int totalResp = newnpc->dialogManager->responseIDs.Length(); for ( int resp = 0; resp < totalResp; resp++ ) { int idToAdd = newnpc->dialogManager->responseIDs[resp]; npcdlg->AddNewResponse( idToAdd ); } } #endif // entitymanager handles the detials of broadcasting the npc if ( entitymanager->CreateNPC(id) ) { success = true; } } // Error response to send to client. char buffer[256]; if (success) { sprintf(buffer, "New NPC %s created and added", newnpc->GetName()); Debug2(LOG_ADMIN,"Result: %s\n", buffer); } else { sprintf(buffer, "New NPC %s FAILED to be created", newnpc->GetName()); Error2("Result: %s\n", buffer); } if (clientnum != 0) { psSystemMessage newmsg(clientnum,MSG_INFO, buffer); if (newmsg.valid) eventmanager->SendMessage(newmsg.msg); } delete newnpc; */ } void AdminManager::AdminRequestTriggers( uint32_t clientnum, csString& data ) { iResultSet* triggers = psserver->GetAllTriggersInArea(data); if (triggers == NULL) return; psAdminTriggerList triggerList; for ( unsigned int i = 0; i < triggers->Count(); i++ ) { triggerList.AddTrigger( (*triggers)[i][0] ); } psAdminMessage outmessage(clientnum, ADMIN_REQUEST_TRIGGERS, triggerList.XML()); outmessage.SendMessage(); triggers->Release(); } void AdminManager::AdminRequestResponses(uint32_t clientnum, csString& trig) { iResultSet* responses = psserver->GetAllResponses(trig); if ( !responses ) return; psAdminResponseList responseList; responseList.AddResponse( (*responses)[0]["response1"]); responseList.AddResponse( (*responses)[0]["response2"]); responseList.AddResponse( (*responses)[0]["response3"]); responseList.AddResponse( (*responses)[0]["response4"]); responseList.AddResponse( (*responses)[0]["response5"]); responseList.AddPronounSet( (*responses)[0]["pronoun_him"], (*responses)[0]["pronoun_her"], (*responses)[0]["pronoun_it"], (*responses)[0]["pronoun_them"]); responses->Release(); psAdminMessage outmesg(clientnum, ADMIN_REQUEST_RESPONSES, responseList.XML() ); if (outmesg.valid) outmesg.SendMessage(); } void AdminManager::AdminDialogChange(uint32_t clientnum, csString& data) { psAdminResponseList dialog(data); for ( int z = 0; z < dialog.responses.Length(); z++ ) { psserver->UpdateDialog( dialog.GetArea(), dialog.GetTrigger(), dialog.responses[z]->GetData(), z+1); // database goes from 1-5 } // Update pronoun set psserver->UpdateDialog( dialog.GetArea(), dialog.GetTrigger(), dialog.GetPronounHim(), dialog.GetPronounHer(), dialog.GetPronounIt(), dialog.GetPronounThem()); psserver->SendSystemInfo(clientnum,"Dialog changes made"); } void AdminManager::WarnMessage(MsgEntry* me, psAdminCmdMessage& msg,Client *client,Client *target) { if (!target) { psserver->SendSystemError(me->clientnum, "Invalid target to warn"); return; } if (msg.reason.Length() == 0) { psserver->SendSystemError(me->clientnum, "Please enter a warn message"); return; } // This message will be shown in adminColor (red) in all chat tabs for this player psSystemMessage newmsg(target->GetClientNum(), MSG_INFO_SERVER, "GM warning from %s: " + msg.reason, client->GetName()); psserver->GetEventManager()->SendMessage(newmsg.msg); // This message will be in big red letters on their screen psserver->SendSystemError(target->GetClientNum(), msg.reason); psserver->SendSystemInfo(client->GetClientNum(), "You warned '%s': " + msg.reason, target->GetName()); } void AdminManager::KickPlayer(MsgEntry* me, psAdminCmdMessage& msg,Client *client,Client *target) { if (!target) { psserver->SendSystemError(me->clientnum, "Invalid target to kick"); return; } if (msg.reason.Length() < 5) { psserver->SendSystemError(me->clientnum, "You must specify a reason to kick"); return; } // Remove from server and show the reason message psserver->RemovePlayer(target->GetClientNum(),"You were kicked from the server by a GM. Reason: " + msg.reason); psserver->SendSystemInfo(me->clientnum,"You kicked '%s' off the server.",(const char*)msg.player); } void AdminManager::Death( MsgEntry* me, psAdminCmdMessage& msg, Client *client, gemActor* target) { if (!target) { psserver->SendSystemError(me->clientnum,"You can't kill things that are not alive!"); return; } target->Kill(NULL); // Have a nice day ;) if (target->GetClientID() != 0) psserver->SendSystemError(target->GetClientID(), "You were killed by a GM"); } void AdminManager::Impersonate( MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if (msg.player.IsEmpty() || msg.text.IsEmpty() || msg.commandMod.IsEmpty()) { psserver->SendSystemError(me->clientnum, "Invalid parameters"); return; } // If no commandMod is given, default to say if (msg.commandMod != "say" && msg.commandMod != "shout" && msg.commandMod != "worldshout") { msg.text = msg.commandMod + " " + msg.text; msg.commandMod = "say"; } csString sendText; // We need specialised say/shout as it is a special GM chat message if (msg.player == "text") sendText = msg.text; else sendText.Format("%s %ss: %s", msg.player.GetData(), msg.commandMod.GetData(), msg.text.GetData() ); psChatMessage newMsg(client->GetClientNum(), msg.player, sendText, CHAT_GM, false); gemObject* source = (gemObject*)client->GetActor(); // Invisible; multicastclients list is empty if (!source->GetVisibility() && msg.commandMod != "worldshout") { // Try to use target as source source = client->GetTargetObject(); if (source == NULL || source->GetClientID() == client->GetClientNum()) { psserver->SendSystemError(me->clientnum, "Invisible; select a target to use as source"); return; } } if (msg.commandMod == "say") newMsg.Multicast(source->GetMulticastClients(), 0, CHAT_SAY_RANGE); else if (msg.commandMod == "shout") newMsg.Multicast(source->GetMulticastClients(), 0, PROX_LIST_ANY_RANGE); else if (msg.commandMod == "worldshout") psserver->GetEventManager()->Broadcast(newMsg.msg, NetBase::BC_EVERYONE); else psserver->SendSystemInfo(me->clientnum, "Syntax: /impersonate name command text\nCommand can be one of say, shout, or worldshout.\nIf name is \"text\" the given text will be the by itself."); } void AdminManager::MutePlayer(MsgEntry* me, psAdminCmdMessage& msg, Client *client, Client *target) { if (!target) { psserver->SendSystemError(me->clientnum, "Invalid target to mute"); return; } psserver->MutePlayer(target->GetClientNum(),"You were muted by a GM, until log off."); // Finally, notify the GM that the client was successfully muted psserver->SendSystemInfo(me->clientnum, "You muted '%s' until he/she/it logs back in.",(const char*)msg.player); } void AdminManager::UnmutePlayer(MsgEntry* me, psAdminCmdMessage& msg, Client *client, Client *target) { if (!target) { psserver->SendSystemError(me->clientnum, "Invalid target to unmute"); return; } psserver->UnmutePlayer(target->GetClientNum(),"You were unmuted by a GM."); // Finally, notify the GM that the client was successfully unmuted psserver->SendSystemInfo(me->clientnum, "You unmuted '%s'.",(const char*)msg.player); } void AdminManager::HandleAddPetition(MsgEntry *me, psAdminCmdMessage& msg,Client *client) { if (msg.petition.Length() == 0) { psserver->SendSystemError(me->clientnum,"You must enter a petition question/description after '/petition '"); return; } // Try and add the petition to the database: if (!AddPetition(client->GetPlayerID(), (const char*)msg.petition)) { psserver->SendSystemError(me->clientnum,"SQL Error: %s", db->GetLastError()); return; } // Tell client the petition was added: psserver->SendSystemInfo(me->clientnum, "Your petition was successfully submitted!"); BroadcastDirtyPetitions(me->clientnum, true); } void AdminManager::BroadcastDirtyPetitions(int clientNum, bool includeSelf) { psPetitionMessage dirty(clientNum, NULL, "", true, PETITION_DIRTY, true); if (dirty.valid) { if (includeSelf) psserver->GetEventManager()->Broadcast(dirty.msg, NetBase::BC_EVERYONE); else psserver->GetEventManager()->Broadcast(dirty.msg, NetBase::BC_EVERYONEBUTSELF); } } void AdminManager::ListPetitions(MsgEntry *me, psPetitionRequestMessage& msg,Client *client) { // Try and grab the result set from the database: iResultSet *rs = GetPetitions(client->GetPlayerID()); if (rs) { // Send list to client: csArray petitions; psPetitionInfo info; for (unsigned int i=0; iCount(); i++) { // Set info info.id = atoi((*rs)[i][0]); info.petition = (*rs)[i][1]; info.status = (*rs)[i][2]; info.created = csString((*rs)[i][3]).Slice(0, 16); info.assignedgm = (*rs)[i][4]; if (info.assignedgm.Length() == 0) info.assignedgm = "No GM Assigned"; // Append to the message: petitions.Push(info); } psPetitionMessage message(me->clientnum, &petitions, "List retrieved successfully.", true, PETITION_LIST); message.SendMessage(); rs->Release(); } else { // Return no succeed message to client csString error; error.Format("SQL Error: %s", db->GetLastError()); psPetitionMessage message(me->clientnum, NULL, error, false, PETITION_LIST); message.SendMessage(); } } void AdminManager::CancelPetition(MsgEntry *me, psPetitionRequestMessage& msg,Client *client) { // Tell the database to change the status of this petition: if (!CancelPetition(client->GetPlayerID(), msg.id)) { psPetitionMessage error(me->clientnum, NULL, db->GetLastError(), false, PETITION_CANCEL); error.SendMessage(); return; } // Try and grab the result set from the database: iResultSet *rs = GetPetitions(client->GetPlayerID()); if (rs) { // Send list to client: csArray petitions; psPetitionInfo info; for (unsigned int i=0; iCount(); i++) { // Set info info.id = atoi((*rs)[i][0]); info.petition = (*rs)[i][1]; info.status = (*rs)[i][2]; info.created = (*rs)[i][3]; info.assignedgm = (*rs)[i][4]; if (info.assignedgm.Length() == 0) info.assignedgm = "No GM Assigned"; // Append to the message: petitions.Push(info); } psPetitionMessage message(me->clientnum, &petitions, "Cancel was successful.", true, PETITION_CANCEL); message.SendMessage(); rs->Release(); } else { // Tell client deletion was successful: psPetitionMessage message(me->clientnum, NULL, "Cancel was successful.", true, PETITION_CANCEL); message.SendMessage(); } BroadcastDirtyPetitions(me->clientnum); } void AdminManager::GMListPetitions(MsgEntry *me, psPetitionRequestMessage& msg,Client *client) { // Check to see if this client has GM level access if ( client->GetSecurityLevel() < GM_LEVEL_1 && client->GetSecurityLevel() <= GM_LEVEL_9 ) { psserver->SendSystemError(me->clientnum, "Access denied. Only GMs can manage petitions."); return; } // Try and grab the result set from the database: iResultSet *rs = GetPetitions(-1, client->GetPlayerID(), client->GetSecurityLevel()); if (rs) { // Send list to GM: csArray petitions; psPetitionInfo info; for (unsigned int i=0; iCount(); i++) { // Set info info.id = atoi((*rs)[i][0]); info.petition = (*rs)[i][1]; info.status = (*rs)[i][2]; info.escalation = atoi((*rs)[i][3]); info.created = csString((*rs)[i][4]).Slice(0, 16); info.player = (*rs)[i][5]; // Append to the message: petitions.Push(info); } psPetitionMessage message(me->clientnum, &petitions, "List retrieved successfully.", true, PETITION_LIST, true); message.SendMessage(); rs->Release(); } else { // Return no succeed message to GM csString error; error.Format("SQL Error: %s", db->GetLastError()); psPetitionMessage message(me->clientnum, NULL, error, false, PETITION_LIST, true); message.SendMessage(); } } void AdminManager::GMHandlePetition(MsgEntry *me, psPetitionRequestMessage& msg,Client *client) { // Check to see if this client has GM level access if ( client->GetSecurityLevel() < GM_LEVEL_1 && client->GetSecurityLevel() > GM_LEVEL_9 ) { psserver->SendSystemError(me->clientnum, "Access denied. Only GMs can manage petitions."); return; } // Check what operation we are executing based on the request: int type = -1; bool result = false; if (msg.request == "cancel") { // Cancellation: type = PETITION_CANCEL; result = CancelPetition(-1, msg.id); } else if (msg.request == "close") { // Closing petition: type = PETITION_CLOSE; result = ClosePetition(client->GetPlayerID(), msg.id, msg.desc); } else if (msg.request == "assign") { // Assigning petition: type = PETITION_ASSIGN; result = AssignPetition(client->GetPlayerID(), msg.id); } else if (msg.request == "escalate") { // Escalate petition: type = PETITION_ESCALATE; result = EscalatePetition(client->GetPlayerID(), client->GetSecurityLevel(), msg.id); } else if (msg.request == "descalate") { // Descalate petition: type = PETITION_DESCALATE; result = DescalatePetition(client->GetPlayerID(), client->GetSecurityLevel(), msg.id); } // Check result of operation if (!result) { psPetitionMessage error(me->clientnum, NULL, db->GetLastError(), false, type, true); error.SendMessage(); return; } // Try and grab the result set from the database: iResultSet *rs = GetPetitions(-1, client->GetPlayerID(), client->GetSecurityLevel()); if (rs) { // Send list to GM: csArray petitions; psPetitionInfo info; for (unsigned int i=0; iCount(); i++) { // Set info info.id = atoi((*rs)[i][0]); info.petition = (*rs)[i][1]; info.status = (*rs)[i][2]; info.escalation = atoi((*rs)[i][3]); info.created = (*rs)[i][4]; info.player = (*rs)[i][5]; // Append to the message: petitions.Push(info); } // Tell GM operation was successful psPetitionMessage message(me->clientnum, &petitions, "Successful", true, type, true); message.SendMessage(); rs->Release(); } else { // Tell GM operation was successful eventhough we don't have a list of petitions psPetitionMessage message(me->clientnum, NULL, "Successful", true, type, true); message.SendMessage(); } BroadcastDirtyPetitions(me->clientnum); } void AdminManager::SendGMPlayerList(MsgEntry* me, psGMGuiMessage& msg,Client *client) { if ( client->GetSecurityLevel() < GM_LEVEL_1 && client->GetSecurityLevel() > GM_LEVEL_9 && !client->IsSuperClient()) { psserver->SendSystemError(me->clientnum,"You don't have access to GM functions!"); CPrintf(CON_ERROR, "Client %d tried to get GM player list, but hasn't got GM access!\n"); return; } csArray playerList; // build the list of players Client *curr; ClientIterator i(*clients); for (curr = i.First(); curr; curr = i.Next()) { if (curr->IsSuperClient() || !curr->GetActor()) continue; psGMGuiMessage::PlayerInfo playerInfo; playerInfo.name = curr->GetName(); playerInfo.lastName = curr->GetCharacterData()->lastname; playerInfo.gender = curr->GetCharacterData()->GetRaceInfo()->gender; psGuildInfo *guild = curr->GetCharacterData()->GetGuild(); if (guild) playerInfo.guild = guild->GetName(); else playerInfo.guild = ""; //Get sector name csVector3 vpos; float yrot; iSector* sector; curr->GetActor()->GetPosition(vpos,yrot,sector); playerInfo.sector = sector->QueryObject()->GetName(); playerList.Push(playerInfo); } // send the list of players psGMGuiMessage message(me->clientnum, &playerList, psGMGuiMessage::TYPE_PLAYERLIST); message.SendMessage(); } bool AdminManager::EscalatePetition(int gmID, int gmLevel, int petitionID) { int result = db->Command("UPDATE petitions SET status='Open',assigned_gm=-1," "escalation_level=(escalation_level+1)" "WHERE id=%d AND escalation_level<=%d AND (assigned_gm=%d OR status='Open')", petitionID, gmLevel, gmID); // If this failed if means that there is a serious error if (result == -1) { lasterror.Format("Couldn't escalate petition #%d.", petitionID); return false; } return (result != -1); } bool AdminManager::DescalatePetition(int gmID, int gmLevel, int petitionID) { int result = db->Command("UPDATE petitions SET status='Open',assigned_gm=-1," "escalation_level=(escalation_level-1)" "WHERE id=%d AND escalation_level<=%d AND (assigned_gm=%d OR status='Open')", petitionID, gmLevel, gmID); // If this failed if means that there is a serious error if (result == -1) { lasterror.Format("Couldn't descalate petition #%d.", petitionID); return false; } return (result != -1); } bool AdminManager::AddPetition(int playerID, const char* petition) { /* The columns in the table NOT included in this command * have default values and thus we do not need to put them in * the INSERT statement */ csString escape; db->Escape( escape, petition ); int result = db->Command("INSERT INTO petitions " "(player,petition,created_date,status,resolution) " "VALUES (%d,\"%s\",Now(),\"Open\",\"Not Resolved\")",playerID, escape.GetData()); return (result != -1); } iResultSet *AdminManager::GetPetitions(int playerID, int gmID, int gmLevel) { iResultSet *rs; // Check player ID (if ID is -1, get a complete list for the GM): if (playerID == -1) { rs = db->Select("SELECT pet.id,pet.petition,pet.status,pet.escalation_level,pet.created_date,pl.name FROM petitions pet, " "characters pl WHERE (pet.player!=%d AND ((pet.status=\"Open\" AND pet.escalation_level<=%d) " "OR (pet.assigned_gm=%d AND pet.status=\"In Progress\"))) " "AND pet.player=pl.id " "ORDER BY pet.status ASC,pet.escalation_level DESC,pet.created_date ASC", gmID, gmLevel, gmID); } else { rs = db->Select("SELECT pet.id,pet.petition,pet.status,pet.created_date,pl.name " "FROM petitions pet LEFT JOIN characters pl " "ON pet.assigned_gm=pl.id " "WHERE pet.player=%d AND pet.status!=\"Closed\" " "AND pet.status!=\"Cancelled\" " "ORDER BY pet.status ASC,pet.escalation_level DESC", playerID); } if (!rs) { lasterror = GetLastSQLError(); } return rs; } bool AdminManager::CancelPetition(int playerID, int petitionID) { // If player ID is -1, just cancel the petition (a GM is requesting the change) if (playerID == -1) { int result = db->Command("UPDATE petitions SET status='Cancelled' WHERE id=%d", petitionID); return (result != -1); } // Attempt to select this petition; two things can go wrong: it doesn't exist or the player didn't create it int result = db->SelectSingleNumber("SELECT id FROM petitions WHERE id=%d AND player=%d", petitionID, playerID); if (!result || result <= -1) { // Failure was due to nonexistant petition or ownership rights: lasterror.Format("Couldn't cancel the petition. Either it does not exist, or you did not " "create the petition."); return false; } // Update the petition status result = db->Command("UPDATE petitions SET status='Cancelled' WHERE id=%d AND player=%d", petitionID, playerID); return (result != -1); } bool AdminManager::ClosePetition(int gmID, int petitionID, const char* desc) { csString escape; db->Escape( escape, desc ); int result = db->Command("UPDATE petitions SET status='Closed',closed_date=Now(),resolution='%s' " "WHERE id=%d AND assigned_gm=%d", escape.GetData(), petitionID, gmID); // If this failed if means that there is a serious error, or the GM was not assigned if (result == -1) { lasterror.Format("Couldn't close petition #%d. You must be assigned to the petition before you close it.", petitionID); return false; } return (result != -1); } bool AdminManager::AssignPetition(int gmID, int petitionID) { int result = db->Command("UPDATE petitions SET assigned_gm=%d,status=\"In Progress\" WHERE id=%d AND assigned_gm=-1",gmID, petitionID); // If this failed if means that there is a serious error, or another GM was already assigned if (result == -1) { lasterror.Format("Couldn't assign you to petition #%d. Another GM is already assigned to that petition.", petitionID); return false; } return true; } bool AdminManager::LogGMCommand(int gmID, int playerID, const char* cmd) { if (!strncmp(cmd,"/slide",6)) // don't log all these. spamming the GM log table. return true; csString escape; db->Escape( escape, cmd ); int result = db->Command("INSERT INTO gm_command_log " "(gm,command,player,ex_time) " "VALUES (%d,\"%s\",%d,Now())",gmID, escape.GetData(), playerID); return (result != -1); } const char *AdminManager::GetLastSQLError() { if (!db) return ""; return db->GetLastError(); } void AdminManager::DeleteCharacter(MsgEntry* me, psAdminCmdMessage& msg,Client *client) { WordArray words (msg.cmd, false); csString zombie = words[1]; csString requestor = words[2]; unsigned int zombieID = 0; if ( zombie.StartsWith("pid:",true) ) // Find by player ID { zombieID = atoi( zombie.Slice(4).GetData() ); if (!zombieID) { psserver->SendSystemError(me->clientnum,"Error, bad PID"); return; } } if (zombieID == 0) // Deleting by name; verify the petitioner gave us one of their characters { if (words.GetCount() < 3) { psserver->SendSystemInfo(me->clientnum,"Syntax: \"/deletechar CharacterName RequestorName\" OR \"/deletechar pid:[id]\""); return; } csString escape; db->Escape( escape, zombie ); // Check account unsigned int zombieAccount = db->SelectSingleNumber( "SELECT account_id FROM characters WHERE name='%s'\n", escape.GetData() ); if ( zombieAccount == QUERY_FAILED ) { psserver->SendSystemInfo(me->clientnum,"Character %s has no account.", zombie.GetData()); return; } zombieID = (unsigned int)db->SelectSingleNumber( "SELECT id FROM characters WHERE name='%s'\n", escape.GetData() ); db->Escape( escape, requestor ); unsigned int requestorAccount = db->SelectSingleNumber( "SELECT account_id FROM characters WHERE name='%s'\n", escape.GetData() ); if ( requestorAccount == QUERY_FAILED ) { psserver->SendSystemInfo(me->clientnum,"Requestor %s has no account.", requestor.GetData()); return; } if ( zombieAccount != requestorAccount ) { psserver->SendSystemInfo(me->clientnum,"Zombie/Requestor Mismatch, no deletion."); return; } } else // Deleting by PID; make sure this isn't a unique or master NPC { Result result(db->Select("SELECT name, character_type, npc_master_id FROM characters WHERE id='%u'",zombieID)); if (!result.IsValid() || result.Count() != 1) { psserver->SendSystemError(me->clientnum,"No character found with PID %u!",zombieID); return; } iResultRow& row = result[0]; zombie = row["name"]; unsigned int charType = row.GetUInt32("character_type"); unsigned int masterID = row.GetUInt32("npc_master_id"); if (charType == PSCHARACTER_TYPE_NPC) { if (masterID == 0) { psserver->SendSystemError(me->clientnum,"%s is a unique NPC, and may not be deleted", zombie.GetData() ); return; } if (masterID == zombieID) { psserver->SendSystemError(me->clientnum,"%s is a master NPC, and may not be deleted", zombie.GetData() ); return; } } } csString error; if ( psserver->CharacterLoader.DeleteCharacterData(zombieID,error) ) psserver->SendSystemInfo(me->clientnum,"Character %s (PID %u) has been deleted.", zombie.GetData(), zombieID ); else { if ( error.Length() ) psserver->SendSystemError(me->clientnum,"Deletion error: %s", error.GetData() ); else psserver->SendSystemError(me->clientnum,"Character deletion got unkown error!", error.GetData() ); } } void AdminManager::ChangeName(MsgEntry* me, psAdminCmdMessage& msg,Client *client) { if (!msg.player.Length()) { psserver->SendSystemError(me->clientnum,"You have to specify a character"); return; } if (!msg.newName.Length()) { psserver->SendSystemError(me->clientnum,"You have to specify a new character name"); return; } // Fix names msg.newName = NormalizeCharacterName(msg.newName); msg.newLastName = NormalizeCharacterName(msg.newLastName); csString name = NormalizeCharacterName(msg.player); bool online; unsigned int id; unsigned int type; Client* target = clients->Find(msg.player); online = (target != NULL); csString prevFirstName,prevLastName; // Check the DB if the player isn't online if(!online) { Result result(db->Select("SELECT id,name,lastname,character_type FROM characters WHERE name='%s' LIMIT 1",name.GetData())); if (!result.IsValid() || result.Count() == 0) { psserver->SendSystemError(me->clientnum,"No online or offline player found with the name %s!",name.GetData()); return; } else { iResultRow& row = result[0]; prevFirstName = row["name"]; prevLastName = row["lastname"]; id = row.GetUInt32("id"); type = row.GetUInt32("character_type"); if (type == PSCHARACTER_TYPE_NPC) { if (!Valid(client->GetSecurityLevel(), "change NPC names", me->clientnum)) return; } } } else { prevFirstName = target->GetCharacterData()->GetCharName(); prevLastName = target->GetCharacterData()->GetCharLastName(); id = target->GetCharacterData()->GetCharacterID(); type = target->GetCharacterData()->GetCharType(); } bool checkFirst=true; //If firstname is same as before, skip DB check bool checkLast=true; //If we make the newLastName var the current value, we need to skip the db check on that if(msg.newLastName == "No") { msg.newLastName = ""; checkLast = false; } else if (msg.newLastName.Length() == 0 || msg.newLastName == prevLastName) { msg.newLastName = prevLastName; checkLast = false; } if (msg.player == msg.newName) checkFirst = false; if (!checkFirst && !checkLast && msg.newLastName.Length() != 0) return; if(checkFirst) { if (!psServerCharManager::FilterName(msg.newName)) { psserver->SendSystemError(me->clientnum,"The name %s is invalid!",msg.newName.GetData()); return; } } if(checkLast) { if (!psServerCharManager::FilterName(msg.newLastName)) { psserver->SendSystemError(me->clientnum,"The last name %s is invalid!",msg.newLastName.GetData()); return; } } // Querying the DB is slow, but I don't see another way to do it if (checkFirst && type == PSCHARACTER_TYPE_PLAYER) { Result result1(db->Select("SELECT * FROM characters WHERE name='%s'",msg.newName.GetData())); if (result1.IsValid() && result1.Count() > 0) { psserver->SendSystemError(me->clientnum,"The name %s is not unique!",msg.newName.GetData()); return; } } // If the last name should be unique, check it if (msg.uniqueName && checkLast && msg.newLastName.Length()) { Result result1(db->Select("SELECT * FROM characters WHERE lastname='%s'",msg.newLastName.GetData())); if (result1.IsValid() && result1.Count() > 0) { psserver->SendSystemError(me->clientnum,"The last name %s is not unique!",msg.newLastName.GetData()); return; } } // Apply csString fullName; PS_ID actorId = 0; if(online) { target->GetCharacterData()->SetFullName(msg.newName, msg.newLastName); fullName = target->GetCharacterData()->GetCharFullName(); target->SetName(msg.newName); target->GetActor()->SetName(fullName); actorId = target->GetActor()->GetEntity()->GetID(); } else if (type == PSCHARACTER_TYPE_NPC || type == PSCHARACTER_TYPE_PET) { gemNPC *npc = GEMSupervisor::GetSingleton().FindNPCEntity( id ); if (!npc) { psserver->SendSystemError(me->clientnum,"Unable to find NPC %s!", name.GetData()); return; } npc->GetCharacterData()->SetFullName(msg.newName, msg.newLastName); fullName = npc->GetCharacterData()->GetCharFullName(); actorId = npc->GetEntity()->GetID(); } // Inform if(online) { psserver->SendSystemInfo( target->GetClientNum(), "Your name has been changed to %s %s by GM %s", msg.newName.GetData(), msg.newLastName.GetData(), client->GetName() ); } psserver->SendSystemInfo(me->clientnum, "%s %s is now known as %s %s", prevFirstName.GetDataSafe(), prevLastName.GetDataSafe(), msg.newName.GetDataSafe(), msg.newLastName.GetDataSafe() ); // Update if (online || type == PSCHARACTER_TYPE_NPC || type == PSCHARACTER_TYPE_PET) { psUpdateObjectNameMessage newNameMsg(0, actorId, fullName); psserver->GetEventManager()->Broadcast( newNameMsg.msg, NetBase::BC_EVERYONE); } // Need instant DB update if we should be able to change the same persons name twice db->Command("UPDATE characters SET name='%s', lastname='%s' WHERE id='%u'",msg.newName.GetData(),msg.newLastName.GetDataSafe(), id ); // Resend group list if(online) { csRef group = target->GetActor()->GetGroup(); if(group) group->BroadcastMemberList(); // Handle guild update psGuildInfo* guild = target->GetActor()->GetGuild(); if(guild) { psGuildMember* mem = guild->FindMember((unsigned int)target->GetActor()->GetCharacterData()->GetCharacterID()); if(mem) mem->name = msg.newName; } } Client* buddy; /*We update the buddy list of the people who have the target in their own buddy list and they are online. */ if(online) { csArray buddyOfList=target->GetCharacterData()->buddyOfList; for (size_t i=0; iFindPlayer(buddyOfList[i]); if (buddy) { buddy->GetCharacterData()->RemoveBuddy(id); buddy->GetCharacterData()->AddBuddy(id, msg.newName); //We refresh the buddy list psserver->usermanager->BuddyList(buddy, buddy->GetClientNum(), true); } } } else { unsigned int buddyid; //If the target is offline then we select all the players online that have him in the buddylist Result result2(db->Select("SELECT player_id FROM buddy_list WHERE player_buddy='%u'",id)); if (result2.IsValid()) { for(unsigned long j=0; jFindPlayer(buddyid); if (buddy) { buddy->GetCharacterData()->RemoveBuddy(id); buddy->GetCharacterData()->AddBuddy(id, msg.newName); //We refresh the buddy list psserver->usermanager->BuddyList(buddy, buddy->GetClientNum(), true); } } } } } void AdminManager::BanName(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if (!msg.player.Length()) { psserver->SendSystemError(me->clientnum,"You have to specify a name to ban"); return; } if (psserver->GetCharManager()->IsBanned(msg.player)) { psserver->SendSystemError(me->clientnum,"That name is already banned"); return; } CacheManager::GetSingleton().AddBadName(msg.player); psserver->SendSystemInfo(me->clientnum,"You banned the name '%s'",msg.player.GetDataSafe()); } void AdminManager::UnBanName(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if (!msg.player.Length()) { psserver->SendSystemError(me->clientnum,"You have to specify a name to unban"); return; } if (!psserver->GetCharManager()->IsBanned(msg.player)) { psserver->SendSystemError(me->clientnum,"That name is not banned"); return; } CacheManager::GetSingleton().DelBadName(msg.player); psserver->SendSystemInfo(me->clientnum,"You unbanned the name '%s'",msg.player.GetDataSafe()); } void AdminManager::BanClient(MsgEntry* me, psAdminCmdMessage& msg,Client *client) { const time_t year = 31536000UL; //one year should be enough time_t secs = (msg.mins * 60) + (msg.hours * 60 * 60) + (msg.days * 24 * 60 * 60); if ((secs > year) || (secs == 0)) secs = year; //some errors if time was too high if (msg.player.Length() == 0) { psserver->SendSystemError(me->clientnum, "You must specify a player name or an account name or number."); return; } if (msg.reason.Length() < 5) { psserver->SendSystemError(me->clientnum, "You must specify a reason to ban"); return; } Result result; unsigned int account = atoi( msg.player.GetDataSafe() ); // See if we're going by character name or account ID if (account == 0) { if ( !GetAccount(msg.player,result) ) { // not found psserver->SendSystemError(me->clientnum, "Couldn't find account with the name %s",msg.player.GetData()); return; } account = result[0].GetUInt32("id"); } else { result = db->Select("SELECT * FROM accounts WHERE id = '%u' LIMIT 1",account); if ( !result.IsValid() || !result.Count() ) { psserver->SendSystemError(me->clientnum, "Couldn't find account with id %u",account); return; } } csString user = result[0]["username"]; // Ban by IP range, as well as account csString ip_range = Client::GetIPRange(result[0]["last_login_ip"]); if ( !psserver->GetAuthServer()->GetBanManager()->AddBan(account,ip_range,secs,msg.reason) ) { // Error adding; entry must already exist psserver->SendSystemError(me->clientnum, "%s is already banned", user.GetData() ); return; } // Find client to get target Client *target = clients->Find(NormalizeCharacterName(msg.player)); // Now we have a valid player target, so remove from server if(target) { if (secs < year) { csString reason; reason.Format("You were banned from the server by a GM for %d minutes, %d hours and %d days. Reason: %s", msg.mins, msg.hours, msg.days, msg.reason.GetData() ); psserver->RemovePlayer(target->GetClientNum(),reason); } else psserver->RemovePlayer(target->GetClientNum(),"You were banned from the server by a GM. Reason: " + msg.reason); } csString notify; notify.Format("You%s banned '%s' off the server for ", (target)?" kicked and":"", user.GetData() ); if (secs == year) notify.Append("a year. "); else notify.AppendFmt("%d minutes, %d hours and %d days. ", msg.mins, msg.hours, msg.days ); notify.AppendFmt("They will also be banned by IP range%s.", (secs > 60*60*24*2)?" for the first 2 days":"" ); // Finally, notify the client who kicked the target psserver->SendSystemInfo(me->clientnum,notify); } void AdminManager::UnbanClient(MsgEntry* me, psAdminCmdMessage& msg,Client *gm) { if (msg.player.Length() == 0) { psserver->SendSystemError(me->clientnum, "You must specify a player name or an account name or number."); return; } Client* target = clients->Find(msg.player); // Check if the target is online, if he/she/it is he/she/it can't be unbanned (No logic in it). if (target) { psserver->SendSystemError(me->clientnum, "The player is active and is playing."); return; } Result result; unsigned int account = atoi( msg.player.GetDataSafe() ); // See if we're going by character name or account ID if (account == 0) { if ( !GetAccount(msg.player,result) ) { // not found psserver->SendSystemError(me->clientnum, "Couldn't find account with the name %s",msg.player.GetDataSafe()); return; } account = result[0].GetUInt32("id"); } else { result = db->Select("SELECT * FROM accounts WHERE id = '%u' LIMIT 1",account); if ( !result.IsValid() || !result.Count() ) { psserver->SendSystemError(me->clientnum, "Couldn't find account with id %u",account); return; } } csString user = result[0]["username"]; if ( psserver->GetAuthServer()->GetBanManager()->RemoveBan(account) ) psserver->SendSystemResult(me->clientnum, "%s has been unbanned", user.GetData() ); else psserver->SendSystemError(me->clientnum, "%s is not banned", user.GetData() ); } bool AdminManager::GetAccount(csString useroracc,Result& resultre ) { unsigned int id = 0; bool character = false; csString usr; // Check if it's a character // Uppercase in names usr = NormalizeCharacterName(useroracc); resultre = db->Select("SELECT * FROM characters WHERE name = '%s' LIMIT 1",usr.GetData()); if (resultre.IsValid() && resultre.Count() == 1) { id = resultre[0].GetUInt32("account_id"); // store id character = true; } if (character) resultre = db->Select("SELECT * FROM accounts WHERE id = '%u' LIMIT 1",id); else { // account uses lowercase usr.Downcase(); resultre = db->Select("SELECT * FROM accounts WHERE username = '%s' LIMIT 1",usr.GetData()); } if ( !resultre.IsValid() || !resultre.Count() ) return false; return true; } void AdminManager::SendSpawnTypes(MsgEntry* me, psAdminCmdMessage& msg,Client *client) { csArray itemCat; unsigned int size = 0; for(int i = 1;; i++) { psItemCategory* cat = CacheManager::GetSingleton().GetItemCategoryByID(i); if(!cat) break; size += (int)strlen(cat->name)+1; itemCat.Push(cat->name); } itemCat.Sort(); psGMSpawnTypes msg2(me->clientnum,size); // Add the numbers of types msg2.msg->Add((uint32_t)itemCat.Length()); for(size_t i = 0;i < itemCat.Length(); i++) { msg2.msg->Add(itemCat.Get(i)); } msg2.SendMessage(); } void AdminManager::SendSpawnItems (MsgEntry* me, psGMSpawnItems& msg,Client *client) { csArray items; unsigned int size = 0; psItemCategory * category = CacheManager::GetSingleton().GetItemCategoryByName(msg.type); if ( !category ) { psserver->SendSystemError(me->clientnum, "Category %s is not valid.", msg.type.GetData() ); return; } // Database hit. // Justification: This is a rare event and it is quicker than us doing a sort. // Is also a read only event. Result result(db->Select("SELECT id FROM item_stats WHERE category_id=%d AND id < %d ORDER BY Name", category->id, SPAWN_ITEM_ID_CEILING)); if (!result.IsValid() || result.Count() == 0) { psserver->SendSystemError(me->clientnum, "Could not query database for category %s.", msg.type.GetData() ); return; } for ( unsigned int i=0; i < result.Count(); i++ ) { unsigned id = result[i].GetUInt32(0); psItemStats* item = CacheManager::GetSingleton().GetBasicItemStatsByID(id); if(item) { csString name(item->GetName()); csString mesh(item->GetMeshName()); size += (int)name.Length()+(int)mesh.Length()+2; items.Push(item); } } psGMSpawnItems msg2(me->clientnum,msg.type,size); // Add the numbers of types msg2.msg->Add((uint32_t)items.Length()); for(size_t i = 0;i < items.Length(); i++) { psItemStats* item = items.Get(i); msg2.msg->Add(item->GetName()); msg2.msg->Add(item->GetMeshName()); } CPrintf(CON_DEBUG, "Sending %d items from the %s category to client %d\n",items.Length(),msg.type.GetData(),me->clientnum); msg2.SendMessage(); } void AdminManager::SpawnItemInv(MsgEntry* me, psGMSpawnItem& msg,Client *client) { psCharacter* charData = client->GetCharacterData(); if (!charData) { psserver->SendSystemError(me->clientnum, "Couldn't find your character data!"); return; } // Get the basic stats psItemStats* stats = CacheManager::GetSingleton().GetBasicItemStatsByName(msg.item); if (!stats) { psserver->SendSystemError(me->clientnum, "Couldn't find basic stats for that item!"); return; } // Check skill PSSKILL skill = CacheManager::GetSingleton().ConvertSkillString(msg.lskill); if (skill == PSSKILL_NONE && msg.lockable) { psserver->SendSystemError(me->clientnum, "Couldn't find the lock skill!"); return; } // Create the new item psItem *item = stats->InstantiateBasicItem(); item->SetStackCount(msg.count); item->SetIsLockable(msg.lockable); item->SetIsLocked(msg.locked); item->SetIsPickupable(msg.pickupable); if (msg.lockable) { item->SetLockpickSkill(skill); item->SetLockStrength(msg.lstr); } // Place the new item in the GM's inventory csString text; if ( charData->MoveToInventory(item) ) { if (item) { item->SetLoaded(); // Item is fully created item->Save(); // First save } text.Format("You spawned %s to your inventory",msg.item.GetData()); } else { text.Format("Couldn't spawn %s to your inventory, maybe it's full?",msg.item.GetData()); CacheManager::GetSingleton().RemoveInstance(item); } psserver->SendSystemInfo(me->clientnum,text); } void AdminManager::AwardExperience(MsgEntry* me, psAdminCmdMessage& msg, Client* client, Client* target) { if (!target || !target->GetCharacterData()) { psserver->SendSystemError(me->clientnum, "Invalid target to award experience to"); return; } if (msg.value == 0) { psserver->SendSystemError(me->clientnum, "Invalid experience specified"); return; } int pp = target->GetCharacterData()->GetProgressionPoints(); if (pp == 0 && msg.value < 0) { psserver->SendSystemError(me->clientnum, "Target has no experience to penalize"); return; } pp += msg.value; // Negative changes are allowed if (pp < 0) // Negative values are not { msg.value += -pp; pp = 0; } target->GetCharacterData()->SetProgressionPoints(pp,true); if (msg.value > 0) { psserver->SendSystemOK(target->GetClientNum(),"You have been awarded experience by a GM"); psserver->SendSystemInfo(target->GetClientNum(),"You gained %d progression points.", msg.value); } else { psserver->SendSystemError(target->GetClientNum(),"You have been penalized experience by a GM"); psserver->SendSystemInfo(target->GetClientNum(),"You lost %d progression points.", -msg.value); } psserver->SendSystemInfo(me->clientnum, "You awarded %s %d progression points.", msg.player.GetData(), msg.value); } void AdminManager::TransferItem(MsgEntry* me, psAdminCmdMessage& msg, Client* source, Client* target) { if (!target || !target->GetCharacterData()) { psserver->SendSystemError(me->clientnum, "Invalid character to give to"); return; } if (!source || !source->GetCharacterData()) { psserver->SendSystemError(me->clientnum, "Invalid character to take from"); return; } if (source == target) { psserver->SendSystemError(me->clientnum, "Source and target must be different"); return; } if (msg.value == 0 || msg.item.IsEmpty()) { psserver->SendSystemError(me->clientnum, "Syntax: \"/[giveitem|takeitem] [target] [quantity|'all'|''] [item]\""); return; } psItemStats* itemstats = CacheManager::GetSingleton().GetBasicItemStatsByName(msg.item); if (!itemstats) { psserver->SendSystemError(me->clientnum, "Invalid item name"); return; } psCharacter* targetchar = target->GetCharacterData(); psCharacter* sourcechar = source->GetCharacterData(); bool equipped = false; int slot = sourcechar->Inventory().FindItemInTopLevelBulkWithStats(itemstats); if (slot == -1) { equipped = true; slot = sourcechar->Inventory().FindItemInTopLevelEquipmentWithStats(itemstats); if (slot == -1) { psserver->SendSystemError(me->clientnum, "There are not any %s in %s's inventory", msg.item.GetData(), source->GetActor()->GetName() ); return; } } psItem* item; if (!equipped) { item = sourcechar->Inventory().GetBulkItem(slot); int count = item->GetStackCount(); if (msg.value == -1) // All msg.value = count; if (count < msg.value) { psserver->SendSystemError(me->clientnum, "There are only %d, not %d", count, msg.value ); return; } } else { item = sourcechar->Inventory().GetEquipmentItem(slot); msg.value = 1; } size_t room = targetchar->Inventory().CanFit(item); if (room && room < (size_t)msg.value) { psserver->SendSystemError(me->clientnum, "Only %u can fit, not %d", room, msg.value ); return; } else if (room == 0) { psserver->SendSystemError(me->clientnum, "Target inventory is full"); return; } if (!equipped) item = sourcechar->Inventory().RemoveBulk(slot,msg.value); else item = sourcechar->Inventory().RemoveEquipment(slot); if ( targetchar->MoveToInventory(item) ) { if (equipped) // If we removed from equipment, change shown equipment psserver->GetCharManager()->SendOutEquipmentMessages( source, slot, item, psEquipmentMessage::DEEQUIP ); // Inform the GM doing the transfer psserver->SendSystemOK(me->clientnum, "%s transfered from %s's %s to %s", item->GetName(), source->GetActor()->GetName(), equipped?"equipment":"inventory", target->GetActor()->GetName() ); // If we're giving to someone else, notify them if (target->GetClientNum() != me->clientnum) psserver->SendSystemOK(target->GetClientNum(), "You were given %s by GM %s", item->GetName(), source->GetActor()->GetName() ); // If we're taking from someone else, notify them if (source->GetClientNum() != me->clientnum) psserver->SendSystemResult(source->GetClientNum(), "%s was taken by GM %s", item->GetName(), target->GetActor()->GetName() ); } else // All slots are probably full { sourcechar->Inventory().PutInBulk(item); psserver->SendSystemError(me->clientnum, "Target inventory is full"); } } void AdminManager::FreezeClient(MsgEntry* me, psAdminCmdMessage& msg, Client* client, Client* target) { if (!target) { psserver->SendSystemError(me->clientnum,"Invalid target for freeze"); return; } if (target->IsFrozen()) { psserver->SendSystemError(me->clientnum,"The player is alreday frozen"); return; } target->SetFrozen(true); target->GetActor()->SetMode(PSCHARACTER_MODE_SIT); psserver->SendSystemError(target->GetClientNum(), "You have been frozen in place by a GM."); psserver->SendSystemInfo(me->clientnum, "You froze '%s'.",(const char*)msg.player); } void AdminManager::ThawClient(MsgEntry* me, psAdminCmdMessage& msg, Client* client, Client* target) { if (!target) { psserver->SendSystemError(me->clientnum,"Invalid target for thaw"); return; } if (!target->IsFrozen()) { psserver->SendSystemError(me->clientnum,"The player is not frozen"); return; } target->SetFrozen(false); target->GetActor()->SetMode(PSCHARACTER_MODE_PEACE); psserver->SendSystemOK(target->GetClientNum(), "You have been released by a GM."); psserver->SendSystemInfo(me->clientnum, "You released '%s'.",(const char*)msg.player); } void AdminManager::SetSkill(MsgEntry* me, psAdminCmdMessage& msg, Client* client) { if (msg.value<0 || msg.value>MAX_SKILL) { psserver->SendSystemError(me->clientnum, "Valid values are between 0 and %i", MAX_SKILL); return; } if (msg.skill.IsEmpty()) { psserver->SendSystemError(me->clientnum, "Syntax: /setskill [skill] [value]"); return; } psCharacter * pchar = client->GetCharacterData(); if (!pchar) { psserver->SendSystemError(me->clientnum, "No character data!"); return; } if (msg.skill == "all") { for (int i=0; iSetSkillRank(skill->id, msg.value); } } else { psSkillInfo * skill = CacheManager::GetSingleton().GetSkillByName(msg.skill); if (skill == NULL) { psserver->SendSystemError(me->clientnum, "Skill not found"); return; } // Validate that if the skill is a stat it's limit is MAX_STAT if ( skill->category == PSSKILLS_CATEGORY_STATS && msg.value > MAX_STAT ) { psserver->SendSystemError(me->clientnum, "Stats have a limit of %i", MAX_STAT); return; } pchar->SetSkillRank(skill->id, msg.value); } psserver->SendSystemInfo(me->clientnum, "Fine"); } void AdminManager::Inspect(MsgEntry* me, psAdminCmdMessage& msg, Client* client, gemActor* target) { if (!target) { psserver->SendSystemError(me->clientnum,"You need to specify or target a player or NPC"); return; } if (!target->GetCharacterData()) { psserver->SendSystemError(me->clientnum,"Critical error! The entity hasn't got any character data!"); return; } // We got our target, now let's print it's inventory csString message; // Dump all data formated in this bool npc = (target->GetClientID() == 0); message.Format("Inventory for %s %s:\n", npc?"NPC":"player", target->GetName() ); message.AppendFmt("Total weight is %d / %d\nTotal money is %d\n", (int)target->GetCharacterData()->Inventory().Weight(), (int)target->GetCharacterData()->Inventory().MaxWeight(), target->GetCharacterData()->Money().GetTotal() ); bool found = false; message += "Equipment:\n"; for (int i = 0; i < PSCHARACTER_SLOT_COUNT;i++) { psItem* item = target->GetCharacterData()->Inventory().GetEquipmentItem(i); if (item) { found = true; message += item->GetName(); if (item->GetStackCount() > 1) message.AppendFmt(" (x%u)", item->GetStackCount() ); message += "\n"; } } if (!found) message += "(none)\n"; found = false; message += "Bulk:\n"; for (int i = 0; i < PSCHARACTER_BULK_COUNT;i++) { psItem* item = target->GetCharacterData()->Inventory().GetBulkItem(i); if (item) { found = true; message += item->GetName(); if (item->GetStackCount() > 1) message.AppendFmt(" (x%u)", item->GetStackCount() ); message += "\n"; } } if (!found) message += "(none)\n"; message.Truncate(message.Length() -1); psserver->SendSystemInfo(me->clientnum,message); } void AdminManager::RenameGuild(MsgEntry* me, psAdminCmdMessage& msg, Client* client) { if(msg.target.IsEmpty() || msg.newName.IsEmpty()) { psserver->SendSystemError(me->clientnum,"Syntax: /changeguildname guildname newguildname"); return; } psGuildInfo* guild = CacheManager::GetSingleton().FindGuild(msg.target); if(!guild) { psserver->SendSystemError(me->clientnum,"No guild with that name"); return; } guild->SetName(msg.newName); psserver->GetGuildManager()->ResendGuildData(guild->id); // Notify the guild leader if he is online psGuildMember* gleader = guild->FindLeader(); if(gleader) { if(gleader->actor && gleader->actor->GetActor()) { psserver->SendSystemInfo(gleader->actor->GetActor()->GetClientID(), "Your guild has been renamed to %s by a GM", msg.newName.GetData() ); } } psserver->SendSystemOK(me->clientnum,"Guild renamed to '%s'",msg.newName.GetData()); // Get all connected guild members csArray array; for (size_t i = 0; i < guild->members.Length();i++) { psGuildMember* member = guild->members[i]; if(member->actor) array.Push(member->actor->GetActor()->GetEntity()->GetID()); } // Update the labels int length = (int)array.Length(); psUpdatePlayerGuildMessage newNameMsg(0,length,msg.newName,false); // Copy array for(size_t i = 0; i < array.Length();i++) newNameMsg.AddPlayer(array[i]); // Broadcast to everyone psserver->GetEventManager()->Broadcast(newNameMsg.msg,NetBase::BC_EVERYONE); } void AdminManager::Thunder(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { // Find the sector psSectorInfo *sectorinfo = NULL; if (!msg.sector.IsEmpty()) sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(msg.sector); else { csVector3 pos; iSector* sect; // Get the current sector client->GetActor()->GetPosition(pos,sect); if(!sect) { psserver->SendSystemError(me->clientnum,"Invalid sector"); return; } sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(sect->QueryObject()->GetName()); } if (!sectorinfo) { psserver->SendSystemError(me->clientnum,"Sector not found!"); return; } if (sectorinfo->lightning_max_gap == 0) { psserver->SendSystemError(me->clientnum, "Lightning not defined for this sector!"); return; } if (!sectorinfo->is_raining) { psserver->SendSystemError(me->clientnum, "You cannot create a lightning " "if no rain or rain is fading out!"); return; } // Queue thunder psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::LIGHTNING, 0, 0, 0, sectorinfo->name, sectorinfo, client->GetActor()->GetEntity()->GetID()); } void AdminManager::Fog(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if( !msg.sector.Length() ) { psserver->SendSystemError(me->clientnum, "Syntax: /fog sector [density [[r g b] fade]|off]"); return; } // Find the sector psSectorInfo *sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(msg.sector); if(!sectorinfo) { psserver->SendSystemError(me->clientnum,"Sector not found!"); return; } // Queue fog if(msg.density == -1) { if ( !sectorinfo->fog_density ) { psserver->SendSystemInfo( me->clientnum, "You need to have fog in this sector for turning it off." ); return; } psserver->SendSystemInfo( me->clientnum, "You have turned off the fog." ); // Reset fog psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::FOG, 0, 0, 0, sectorinfo->name, sectorinfo,0,0,0,0); } else { // Set fog psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::FOG, msg.density, 0, msg.fade, sectorinfo->name, sectorinfo,0, (int)msg.x,(int)msg.y,(int)msg.z); //rgb } } void AdminManager::Rain(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if(msg.sector.Length() == 0) { psserver->SendSystemError(me->clientnum, "Syntax: /rain sector [drops [length [fade]]|start|stop|off]"); return; } if (msg.rainDrops < 0 || msg.rainDrops > WEATHER_MAX_RAIN_DROPS) { psserver->SendSystemError(me->clientnum, "Rain drops should be between %d and %d", 0,WEATHER_MAX_RAIN_DROPS); return; } // Find the sector psSectorInfo *sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(msg.sector); if(!sectorinfo) { psserver->SendSystemError(me->clientnum,"Sector not found!"); return; } if (msg.interval == -1) // Start automatic weather { if (!sectorinfo->rain_enabled) { psserver->SendSystemInfo(me->clientnum,"Automatic weather started in sector %s", msg.sector.GetDataSafe()); sectorinfo->rain_enabled = true; psserver->GetWeatherManager()->StartWeather(sectorinfo); } else { psserver->SendSystemInfo(me->clientnum,"The weather was already automatic in sector %s", msg.sector.GetDataSafe()); } } else if (msg.interval == -2) // Stop automatic weather { if ( sectorinfo->rain_enabled ) { psserver->SendSystemInfo(me->clientnum,"Automatic weather stopped in sector %s", msg.sector.GetDataSafe()); sectorinfo->rain_enabled = false; // Disable this will prevent any new // rain events from beeing started. Will not stop current shower and // that is ok since we don't have a user determined fade factor here. } else { psserver->SendSystemInfo(me->clientnum,"The automatic weather was already off in sector %s", msg.sector.GetDataSafe()); } } else if (msg.interval == -3 || (msg.interval == 0 && msg.rainDrops == 0 && msg.fade == 0 )) //Stop weather if any { if( !sectorinfo->is_raining) //If it is not raining already then you don't stop anything. { psserver->SendSystemInfo( me->clientnum, "You need some weather, first." ); return; } else { psserver->SendSystemInfo( me->clientnum, "The weather was stopped." ); // queue the event psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::RAIN, 0, 0, 0, msg.sector, sectorinfo); } } else { // queue the event psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::RAIN, msg.rainDrops, msg.interval, msg.fade, msg.sector, sectorinfo); } } void AdminManager::Snow(MsgEntry* me, psAdminCmdMessage& msg, Client *client) { if(msg.sector.Length() == 0) { psserver->SendSystemError(me->clientnum, "Syntax: /snow sector [flakes [length [fade]]|start|stop |off]"); return; } if (msg.rainDrops < 0 || msg.rainDrops > WEATHER_MAX_SNOW_FALKES) { psserver->SendSystemError(me->clientnum, "Snow flakes should be between %d and %d", 0,WEATHER_MAX_SNOW_FALKES); return; } // Find the sector psSectorInfo *sectorinfo = CacheManager::GetSingleton().GetSectorInfoByName(msg.sector); if(!sectorinfo) { psserver->SendSystemError(me->clientnum,"Sector not found!"); return; } if (msg.interval == -1 || msg.interval == -2) // Start automatic weather { psserver->SendSystemInfo(me->clientnum,"Automatic weather is not yet implemented for snow", msg.sector.GetDataSafe()); return; } else if (msg.interval == -3 || (msg.interval == 0 && msg.fade == 0 && msg.rainDrops == 0 )) //Stop weather if any { if( !sectorinfo->is_snowing) //If it is not raining already then you don't stop anything. { psserver->SendSystemInfo( me->clientnum, "You need some snow, first." ); return; } else { psserver->SendSystemInfo( me->clientnum, "The snow was stopped." ); // queue the event psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::SNOW, 0, 0, 0, msg.sector, sectorinfo); } } else { // queue the event psserver->GetWeatherManager()->QueueNextEvent(0, psWeatherMessage::SNOW, msg.rainDrops, msg.interval, msg.fade, msg.sector, sectorinfo); } } void AdminManager::ModifyHuntLocation(MsgEntry* me, psAdminCmdMessage& msg, Client* client, gemObject* object) { if (!object) { psserver->SendSystemError(me->clientnum,"You need to specify an item in the world with 'target' or 'eid:#'"); return; } psItem* item = object->GetItem(); if (!item) { psserver->SendSystemError(me->clientnum,"You can only use modify on items"); return; } if (msg.action == "remove") { if (item->GetScheduledItem()) { item->GetScheduledItem()->Remove(); psserver->SendSystemInfo(me->clientnum,"Spawn point deleted for %s",item->GetName()); } EntityManager::GetSingleton().RemoveActor(object); // Remove from world psserver->SendSystemInfo(me->clientnum,"%s was removed from the world",item->GetName()); item->Destroy(); // Remove from db delete item; item = NULL; } else if (msg.action == "intervals") { if (msg.interval < 0 || msg.random < 0 || msg.interval != msg.interval || msg.random != msg.random) { psserver->SendSystemError(me->clientnum,"Invalid intervals specified"); return; } // In seconds int interval = 1000*msg.interval; int random = 1000*msg.random; if (item->GetScheduledItem()) { item->GetScheduledItem()->ChangeIntervals(interval,random); psserver->SendSystemInfo(me->clientnum,"Intervals for %s set to %d base + %d max modifier",item->GetName(),msg.interval,msg.random); } else psserver->SendSystemError(me->clientnum,"This item does not spawn; no intervals"); } else { bool onoff; if (msg.setting == "true") onoff = true; else if (msg.setting == "false") onoff = false; else { psserver->SendSystemError(me->clientnum,"Invalid settings"); return; } if (msg.action == "pickupable") { item->SetIsPickupable(onoff); psserver->SendSystemInfo(me->clientnum,"%s is now %s",item->GetName(),(onoff)?"pickupable":"un-pickupable"); } else if (msg.action == "transient") { item->SetIsTransient(onoff); psserver->SendSystemInfo(me->clientnum,"%s is now %s",item->GetName(),(onoff)?"transient":"non-transient"); } // TODO: Add more flags else psserver->SendSystemError(me->clientnum,"Invalid action"); item->Save(); } } void AdminManager::Morph(MsgEntry* me, psAdminCmdMessage& msg, Client *client, Client *targetclient) { if (msg.player == "list" && msg.mesh.IsEmpty()) { static csString list; if (list.IsEmpty()) // Construct list once { // Get array of mounted model directories const char* modelsPath = "/planeshift/models/"; size_t modelsPathLength = strlen(modelsPath); csRef vfs = CS_QUERY_REGISTRY(psserver->GetObjectReg(),iVFS); csRef dirPaths = vfs->FindFiles(modelsPath); csStringArray dirNames; for (size_t i=0; i < dirPaths->Length(); i++) { csString path = dirPaths->Get(i); csString name = path.Slice( modelsPathLength, path.Length()-modelsPathLength-1 ); if (name.Length() && name.GetAt(0) != '.') { if ( vfs->Exists(path+name+".cal3d") ) dirNames.Push(name); else Error2("Model dir %s lacks a valid cal3d file!", name.GetData() ); } } // Make alphabetized list dirNames.Sort(); list = "Available models: "; for (size_t i=0; iSendSystemInfo(me->clientnum, "%s", list.GetData() ); return; } if (!targetclient || !targetclient->GetActor()) { psserver->SendSystemError(me->clientnum,"Invalid target for morph"); return; } gemActor* target = targetclient->GetActor(); if (msg.mesh == "reset") { if ( target->ResetMesh() ) psserver->SendSystemInfo(me->clientnum,"Resetting mesh for %s", targetclient->GetName() ); else psserver->SendSystemError(me->clientnum,"Error resetting mesh for %s!", targetclient->GetName() ); } else { if ( target->SetMesh(msg.mesh) ) psserver->SendSystemInfo(me->clientnum,"Setting mesh for %s to %s", targetclient->GetName(), msg.mesh.GetData() ); else psserver->SendSystemError(me->clientnum,"Error setting mesh %s!", msg.mesh.GetData() ); } } void AdminManager::TempSecurityLevel(MsgEntry* me, psAdminCmdMessage& msg, Client *client, Client *target) { if (!target || !target->GetActor()) { psserver->SendSystemError(me->clientnum,"Invalid target"); return; } // Can only set others to a max of 3 levels below own level (ex: GM4 can set someone to GM1) int maxleveltoset = client->GetSecurityLevel() - 3; int value; msg.setting.Downcase(); if (msg.setting == "reset") { int trueSL = GetTrueSecurityLevel( target->GetAccountID() ); if (trueSL < 0) { psserver->SendSystemError(client->GetClientNum(), "Cannot reset access level for %s!", target->GetName() ); return; } target->SetSecurityLevel(trueSL); target->GetActor()->SetSecurityLevel(trueSL); psserver->SendSystemOK(target->GetClientNum(),"Your access level was reset"); if (target != client) psserver->SendSystemOK(me->clientnum,"Access level for %s was reset",target->GetName()); if (trueSL) psserver->SendSystemInfo(target->GetClientNum(),"Your access level has been reset to %d",trueSL); return; } else if (msg.setting == "player") value = 0; else if (msg.setting == "tester") value = 10; else if (msg.setting == "gm") value = GM_LEVEL_1; else if (msg.setting.StartsWith("gm",true) && msg.setting.Length() == 3) value = atoi(msg.setting.Slice(2,1)) + GM_LEVEL_0; else { psserver->SendSystemError(me->clientnum,"Valid settings are: player, tester, GM, or reset. GM levels may be specified: GM1, ... GM5"); return; } if (!CacheManager::GetSingleton().GetCommandManager()->GroupExists(value) ) { psserver->SendSystemError(me->clientnum,"Specified access level does not exist!"); return; } if ( target == client && value > GetTrueSecurityLevel(target->GetAccountID()) ) { psserver->SendSystemError(me->clientnum,"You cannot upgrade your own level!"); return; } else if ( target != client && value > maxleveltoset ) { psserver->SendSystemError(me->clientnum,"Max access level you may set is %d", maxleveltoset); return; } if (target->GetSecurityLevel() == value) { psserver->SendSystemError(me->clientnum,"%s is already at that access level", target->GetName()); return; } if (value == 0) { psserver->SendSystemInfo(target->GetClientNum(),"Your access level has been disabled for this session."); } else // Notify of added/removed commands { psserver->SendSystemInfo(target->GetClientNum(),"Your access level has been changed for this session. " " Use \"/admin\" to enable and list available GM commands."); } if (value < GM_LEVEL_4) // Cannot access this command, but may still reset { psserver->SendSystemInfo(target->GetClientNum(),"You may do \"/deputize me reset\" at any time to reset yourself. " " The temporary access level will also expire on logout."); } // Set temporary security level (not saved to DB) target->SetSecurityLevel(value); target->GetActor()->SetSecurityLevel(value); // Refresh the label target->GetActor()->UpdateProxList(true); psserver->SendSystemOK(me->clientnum,"Access level for %s set to %s",target->GetName(),msg.setting.GetData()); if (target != client) { psserver->SendSystemOK(target->GetClientNum(),"Your access level was set to %s by a GM",msg.setting.GetData()); } } int AdminManager::GetTrueSecurityLevel(int accountID) { Result result(db->Select("SELECT security_level FROM accounts WHERE id='%d'", accountID )); if (!result.IsValid() || result.Count() != 1) return -99; else return result[0].GetUInt32("security_level"); }