PWE_PRC8/_module/nss/ff_inc_server.nss
Jaysyn904 ee1dc35889 Initial Commit
Initial Commit
2025-04-03 10:29:41 -04:00

338 lines
13 KiB
Plaintext
Raw Permalink Blame History

//*************************************
//* 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<iLen; i++)
{
sSub = GetSubString(sName, i, 1);
bUp = ff_IsUpperCase(sSub);
bLow = ff_IsLowerCase(sSub);
if (bLow==bUp)
{NbDigit++;if (sSub == "[" || sSub == "]" || sSub == "{" || sSub == "}" || sSub == "^" || sSub == "<22>") bValid = FALSE;}
else
if (bLow) NbLowercase++;
else
NbUpcase++;
}
if (NbUpcase>=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!");
}