Обычные варианты логин+СДКЕЙ+имя персонажа - не являются 100% надежными, ведь можно создать второго персонажа с таким же именем.
Вот скрипт, "считающих" всех персонажей и выдающий им номер. Для простоты\быстроты номер хранится в Deity, но проверяется в любом случае.
Версия скрипта еще не последняя и возможны ошибки, но в целом - скрипт проверенный и работоспособный.
Код
//возвращает UNIQ_ID, -1 - если ошибка
int ID_GetID(object oPC);
//возвращает объект-игрока с номером ID или OBJECT_INVALID - если такого игрока нет онлайн
object ID_FindPC(int ID);
///////////////////////////////////////////////////////////////////////
object ID_FindPC(int ID)
{
object oPC = GetFirstPC();
while (GetIsObjectValid(oPC))
{
if ( GetLocalInt(oPC,"UNIQ_ID") == ID)
return oPC;
oPC = GetNextPC();
}
return OBJECT_INVALID;
}
int ID_GetID(object oPC)
{
while (GetIsObjectValid(GetMaster(oPC)))
{
oPC=GetMaster(oPC);
}
if (!GetIsPC(oPC))
{
return -1;
}
//кэширование результата, чтобы не лазить в БД каждый раз
int id = GetLocalInt(oPC,"UNIQ_ID");
if (id > 0)
{
return id;
}
//длЯ ДМ-ов упрощенна проверка
if (GetIsDM(oPC))
{
id = GetCampaignInt("ident_dm",GetPCPublicCDKey(oPC)+"_"+GetPCPlayerName(oPC));
if (id > 0)
{
SetLocalInt(oPC,"UNIQ_ID",id);
return id;
}
//maybe first time? assign new ID
int id = GetCampaignInt("ident_dm","LAST_UNIQ_ID");
if (id == 0)
{
//ДМ-ы получают номера начинаЯ с 101
id = 100;
}
id++;
SetCampaignInt("ident_dm","LAST_UNIQ_ID",id);
SetLocalInt(oPC,"UNIQ_ID",id);
SetCampaignInt("ident_dm",GetPCPublicCDKey(oPC)+"_"+GetPCPlayerName(oPC),id);
return id;
}
//проверочнаЯ строка, котораЯ записываетсЯ в БД
string sID_STRING = "mg_ident"+"_"+GetPCPublicCDKey(oPC)+"_"+GetPCPlayerName(oPC)+"_"+GetName(oPC);
int iDeity = StringToInt(GetDeity(oPC));
//Если игрок в первый раз пришел - обнулим ему поле Deity.
//!!! Не забудьте выдать игроку хотЯ-бы 1 XP, чтобы он не считалсЯ впервые зашедшим на щард
if (GetXP(oPC) == 0)
{
iDeity =0;
SetDeity(oPC,"");
}
//Если в Deity есть число - проверЯем по базе с ID_STRING
//Сама база устроена в виде записей номер:ID_строка
if (iDeity > 0)
{
string sDB = GetCampaignString("ident",IntToString(iDeity));
if (sDB == sID_STRING)
{
//check ok! cache & return
SetLocalInt(oPC,"UNIQ_ID",iDeity);
return iDeity;
}
else if (sDB != "") //may be corrupted DB?
//Вариант, если испортилась БД (например удалили всех персонажей, но не почистили каталог с БД.
{
BootPC(oPC);
return -1;
}
}
//Если дошли до сюда - видимо новый персонаж, провери его имЯ и логин по всей базе и если найдем такого-же - значит кто-то пытаетсЯ создать "дублЯ"
int iLastPC = GetCampaignInt("ident","LAST_UNIQ_ID");
if (iLastPC == 0)
{
iLastPC = 1000; //Игроки нумеруютсЯ с 1001
}
int k;
string sTest;
for (k = 1000;k <= iLastPC; k++)
{
sTest =GetCampaignString("ident",IntToString(k));
if (sTest == sID_STRING)
{
//Ага, это читер. Выбрасываем его.
BootPC(oPC)
return -1;
}
}
//Все нормально, просто новый игрок - запишем его в БД и выставим ему Deity- длЯ более быстрого поиска в следующий раз
iLastPC++;
SetCampaignInt("ident","LAST_UNIQ_ID",iLastPC);
SetLocalInt(oPC,"UNIQ_ID",iLastPC);
SetCampaignString("ident",IntToString(iLastPC),sID_STRING);
SetDeity(oPC,IntToString(iLastPC));
//На всЯкий запишем персонажа
DelayCommand(4.0,ExportSingleCharacter(oPC));
//и вернем номер.
return iLastPC;
}
После такой нумерации мы получаем уникальный номер для каждого персонажа, что открывает нам новые возможности - мы можем сохранять инфромацию в базе данных для персонажа даже когда сам игрок не на шарде (оффлайн)
Если на шарде используется БД - очень удобно хранить всю информацию, относящиюся к данному игроку, в отдельной БД (лучше несколько маленьких БД, чем одна большая - скорость куда выше).
Как это сделанно у нас - есть библиотека для работы с БД, очень похожая на обычные SetLocal*, GetLocal*, только пишет инфу кроме памяти в БД (если флаг стоит). Она хранится в памяти, использую в виде префикса номер игрока, поэтому переменные с одним именем не пересекаются для разных игроков. Кроме того, позволяет писать в БД информацию по оффлайн-игрокам (к примеру нужно для обработки события onClientLeve)