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!");
|
|||
|
}
|