338 lines
13 KiB
Plaintext
338 lines
13 KiB
Plaintext
//*************************************
|
||
//* 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!");
|
||
}
|