//************************************* //* NWN-FF 4.0 (c) 2004 FastFrench * //************************************* // This file is licensed under the terms of the // GNU GENERAL PUBLIC LICENSE (GPL) Version 2 // ************************ // ** ff_inc_server.nss ** // ************************ // ** All server specific scripts and options come here // ** Some general purpose (but all optional) script are also provided here /************************************/ /* Function prototypes */ /************************************/ //============= log helper. Each of this 3 function correspond to a different specific table. void pwWriteLog(object oPlayer, string sCategorie, string sSubCategorie, string sTexte); void pwWriteLogCon(object oPlayer, string sCategorie); //============= function used to update the data that need to be often updated // (call it periodically) void ff_FlushData(object oPlayer); //============= PC login void InitValues(object oPlayer); int HasValidName(object oPlayer); // An important step to include in OnClientEnter event // proceed validation of the player, and all database-related stuff int ff_SelectPersistentChar(object oPlayer); //============== Speech processing: function called if a PC speaks while he's dead void ff_TalkingDead(object oPC, int iCanal); /************************************/ /* Implementation */ /************************************/ string SQLEncodeSpecialChars(string sTexte); void SQLExecDirect(string sSQL); string ff_LocationToString(location lLoc); int ff_IsUpperCase(string sTexte); int ff_IsLowerCase(string sTexte); int SQLLocalExecAndFetchDirect(string sSQL); int SQLLocalEatDataInt(); string SQLLocalEatData(); location ff_StringToLocation(string sLoc); string ff_Colorize(string sTexte); int SQLExecAndFetchSingleInt(string sSQL, int DefaultValue=0); // log informations into the log database, with 3 classification fields void pwWriteLog(object oPlayer, string sCategorie, string sSubCategorie, string sTexte) { string sId = GetLocalString(oPlayer, "PWId"); if (StringToInt(sId)<=0) if (GetIsPC(oPlayer)) sId = "99999"; // Error else sId = "99998"; // Not a PC (module or whatever) string sSQL = "INSERT INTO log (Id,Categorie,SubCategorie,Texte) VALUES(" + sId + ",'"+SQLEncodeSpecialChars(sCategorie)+"','"+SQLEncodeSpecialChars(sSubCategorie)+"','"+SQLEncodeSpecialChars(sTexte)+"')"; SQLExecDirect(sSQL); } // log connexion information only void pwWriteLogCon(object oPlayer, string sCategorie) { string sId = GetLocalString(oPlayer, "PWId"); if (sId=="") return; string sSQL; if(sId=="0") sSQL = "INSERT INTO login (Id,Event) VALUES(" + sId + ",'"+sCategorie+"')"; else sSQL = "INSERT INTO login (Id,Event,Gold,xp,IP,sKey) VALUES(" + sId + ",'"+sCategorie+"',"+IntToString(GetGold(oPlayer))+","+IntToString(GetXP(oPlayer))+",'"+SQLEncodeSpecialChars(GetPCIPAddress(oPlayer))+"','"+SQLEncodeSpecialChars(GetPCPublicCDKey(oPlayer))+"')"; SQLExecDirect(sSQL); } void InitValues(object oPlayer) { int i; SetLocalLocation(oPlayer, "Position", Location(OBJECT_INVALID, Vector(),0.0)); SetLocalInt( oPlayer, "Time", 0); SetLocalInt( oPlayer, "Damage", 0); } // Call this function as often as you wish. // For instance in each area transition, after each rest, death, cure, resur, end of fight... void ff_FlushData(object oPC) { //// if (oPC==OBJECT_INVALID) return; //if (!GetIsPC(oPC)) return; if (GetLocalInt(oPC, "InitRunning")==1) return; string sId = GetLocalString(oPC, "PWId"); if (sId=="") return; if (StringToInt(sId)<=0) return; //int bAnyChange = TRUE; int iMaxHP = GetMaxHitPoints(oPC); if (iMaxHP<=0) return; location lPos = GetLocation(oPC); object oArea = GetAreaFromLocation(lPos); string sSQL = "UPDATE PlayerData SET "; sSQL += "Time="+IntToString(GetLocalInt(oPC, "Time")); if (GetIsObjectValid(oArea)) { object oldArea = GetLocalObject(oPC,"Area"); if (oldArea!=oArea) { // Increase the area counter string sTag = GetTag(oArea); int iCount = SQLExecAndFetchSingleInt("SELECT iCount FROM areatransition WHERE sTag='"+sTag+"'"); if (iCount <= 0) // Does this area allready exists ? SQLExecDirect("INSERT INTO areatransition (sTag,sName,iCount) VALUES('" + sTag + "','" + SQLEncodeSpecialChars(GetName(oArea)) +"',1)"); else SQLExecDirect("UPDATE areatransition SET iCount="+IntToString(iCount+1)+" WHERE sTag='" + sTag+"'"); } SetLocalObject(oPC, "Area", oArea); SetLocalLocation(oPC, "Position", lPos); sSQL += ", Position='"+ff_LocationToString(lPos)+"'"; } if (GetIsObjectValid(oPC)) { int iCurrentHP = GetCurrentHitPoints(oPC); int iDamage = iMaxHP-iCurrentHP; if (GetIsDead(oPC)) iDamage = iMaxHP+50; if (iDamage<0) iDamage=0; SetLocalInt(oPC, "Damage", iDamage); sSQL += ", Damage="+IntToString(iDamage); } sSQL += " WHERE Id="+sId; SQLExecDirect(sSQL); } /* * This function is used to validate names of the player. * You can freely change it to fit your needs. * Default rules are: * - More lower cases than uppercases, * - More lower cases than non-letters * - At least 2 lower cases * - no '[', ']', '^', '{', '}', '(' or ')' * - max total length of 15 characters */ int HasValidName(object oPlayer) { if (FF_PCC_DM_CONTROL && GetIsDM(oPlayer)) return FALSE; if (!FF_PCC_NAME_CONTROL) return TRUE; int NbLowercase=0; int NbDigit=0; int NbUpcase=0; string sName = GetName(oPlayer); int i; int iLen = GetStringLength(sName); int bUp, bLow; string sSub; int bValid=TRUE; if (iLen>15) bValid = FALSE; else for (i=0; i=NbLowercase) return FALSE; if (NbDigit>=NbLowercase) return FALSE; if (NbLowercase<3) return FALSE; int NbMaxi = 8; if ((NbDigit + NbUpcase) > NbMaxi) return FALSE; return bValid; } int ff_SelectPersistentChar(object oPlayer) { string sPlayer = SQLEncodeSpecialChars(GetPCPlayerName(oPlayer)); string sName = SQLEncodeSpecialChars(GetName(oPlayer)); string sKey = GetPCPublicCDKey(oPlayer); string sKey2, sSQL, sId, sNewRecord; int iVersion=1; // Update CD keys table int iRes = SQLLocalExecAndFetchDirect("SELECT Ban FROM cdkeys WHERE (Player='" + sPlayer + "' OR Player='*') AND (CDKey='" + sKey+"' OR CDKey='*')"); if (iRes != SQL_SUCCESS) // This player/CDKey pair doesn't exist yet SQLExecDirect("INSERT INTO cdkeys (Player,CDKey) VALUES('" + sPlayer + "','" + sKey + "')"); else { if (SQLLocalEatDataInt()==1) { SetLocalInt(GetModule(),GetPCPlayerName(oPlayer)+"_BAN", 1); // Banni par un DM online -> ejecte (jusqu'au prochain demarrage) pwWriteLog(oPlayer, "LOGIN", "PC REJECTION", "cdkeys Ban=1 for "+GetName(oPlayer)+": Player="+sPlayer+", CDKey="+sKey); BootPC(oPlayer); return FALSE; } } // Mise a jour iRes = SQLLocalExecAndFetchDirect("SELECT Id,Version,Ban FROM idplayer WHERE Player='" + sPlayer + "' AND Name='" + sName + "'"); int bNewRecord = FALSE; int bValid = TRUE; if (iRes != SQL_SUCCESS) // New character { bValid = HasValidName(oPlayer); if (bValid) { if (FF_NEED_UNIK_NAME) { iRes = SQLLocalExecAndFetchDirect("SELECT Id FROM idplayer WHERE Name='" + sName + "' AND Ban=0"); if (iRes==SQL_SUCCESS) // This name is allready currently used ! { bValid = FALSE; pwWriteLog(oPlayer, "LOGIN", "PC REJECTION", "Name allready used:"+GetName(oPlayer)+" (Id:"+SQLLocalEatData()+")"); SendMessageToAllDMs(GetName(oPlayer)+" (new PC) has been automatically rejected (Name allready used)"); } } } else { pwWriteLog(oPlayer, "LOGIN", "PC REJECTION", "Invalid name:"+GetName(oPlayer)); SendMessageToAllDMs(GetName(oPlayer)+" (new PC) has been automatically rejected (invalid name)"); } SQLExecDirect("INSERT INTO idplayer (Player,Name,Version,DM,Creation,Ban) VALUES('" + sPlayer + "','" + sName + "',1,"+IntToString(GetIsDM(oPlayer))+",NOW(),"+IntToString(!bValid)+")"); // Actuellement version 1 iRes = SQLLocalExecAndFetchDirect("SELECT Id,Version,Ban FROM idplayer WHERE Player='" + sPlayer + "' AND Name='" + sName + "'"); bNewRecord = TRUE; } if (iRes == SQL_SUCCESS) // This character allready exists { sId = SQLLocalEatData(); iVersion = SQLLocalEatDataInt(); if (SQLLocalEatDataInt()) { pwWriteLog(oPlayer, "LOGIN", "PC REJECTION", "Ban=1 for "+GetName(oPlayer)); BootPC(oPlayer); return FALSE; } SetLocalString(oPlayer, "PWId", sId); int iId = StringToInt(sId); if (iId <= 0) {BootPC(oPlayer); return FALSE;} if (!bValid) { DelayCommand(5.0,SendMessageToPC(oPlayer,"Your name is not valid or is allready in use, try something else more... original")); DelayCommand(80.0,BootPC(oPlayer)); return FALSE; } int bReset = (iVersion != 1 || bNewRecord); if (!bReset) { iRes = SQLLocalExecAndFetchDirect("SELECT Position,Time,Damage FROM PlayerData WHERE Id=" + sId); if (iRes == SQL_SUCCESS) { SetLocalLocation(oPlayer, "Position", ff_StringToLocation(SQLLocalEatData())); SetLocalInt( oPlayer, "Time", SQLLocalEatDataInt()); SetLocalInt( oPlayer, "Damage", SQLLocalEatDataInt()); } else bReset = TRUE; // Donnees invalides } if (bReset) // INSERT DATA { sSQL = "REPLACE INTO PlayerData (Id,Position) VALUES("+sId+",'')"; InitValues(oPlayer); SQLExecDirect(sSQL); if (FF_PCC_CLERIC_NEED_DEITY) if (GetDeity(oPlayer)=="") if (GetLevelByClass(CLASS_TYPE_CLERIC,oPlayer)>0 || GetLevelByClass(CLASS_TYPE_PALADIN,oPlayer)>0) { SQLExecDirect("UPDATE idplayer SET Ban=1 WHERE Id=" + sId); SendMessageToAllDMs(GetName(oPlayer)+" (new PC) has been automatically rejected (no God for a cleric or a paladin)"); pwWriteLog(oPlayer, "LOGIN", "PC REJECTION", "No proper God for "+GetName(oPlayer)); DelayCommand(5.0,SendMessageToPC(oPlayer,ff_Colorize("[r]How do you want to play a cleric or a paladin without a God ?!![y]"))); DelayCommand(80.0,BootPC(oPlayer)); return FALSE; } } if (bReset) sNewRecord = "CONVERT"; else if (GetIsDM(oPlayer)) sNewRecord = "DM_IN"; else if (GetLocalInt(oPlayer, "Damage")>=GetMaxHitPoints(oPlayer)) sNewRecord = "DEAD_IN"; else sNewRecord = "PC_IN"; pwWriteLogCon(oPlayer, sNewRecord); SQLExecDirect("UPDATE idplayer SET Actif=1 WHERE Id=" + sId); } else // error : No MySQL database available ! { DeleteLocalString(oPlayer, "PWId"); WriteTimestampedLogEntry("[MySQL] No database available -> BootPC("+GetName(oPlayer)+")"); DelayCommand(5.0,SendMessageToPC(oPlayer,"No database available. Sorry, but the server is not properly running yet")); DelayCommand(80.0,BootPC(oPlayer)); return FALSE; } return TRUE; } void ff_TalkingDead(object oPC, int iCanal) { int NbCheat = GetLocalInt(oPC,"Cheater"); NbCheat++; int nXP = NbCheat * NbCheat * GetHitDice(oPC); SetLocalInt(oPC, "Cheater", NbCheat); int NewXP = GetXP(oPC) - nXP; if (NewXP<=0) NewXP = 0; SetXP(oPC, NewXP); SendMessageToPC(oPC,ff_Colorize("[r] Warning, you're not supposed to speak when you're dead![w]")); SendMessageToAllDMs(GetName(oPC)+" has lost "+IntToString(nXP)+" xp because he speaked while dead!"); pwWriteLog(oPC, "SPEECH", "DEAD", GetName(oPC)+" has lost "+IntToString(nXP)+" xp because he speaked while dead!"); }