Перейти к содержанию
Авторизация  
Relvl

[Lowlevelcode] [L2Jserver] Динамические Переменные

Рекомендуемые сообщения

Динамические переменные.

 

Без доработок работает только с исходниками L2JServer.

Если у Вас другие исходники - допиливайте сами, фактически, я просто предлагаю Вам идею и пример реализации.

 

Позволяют для каждого персонажа хранить индивидуальный набор переменных.

Стоит оговориться, что "переменными" я называю это чисто условно, для удобства.

Формально же это - просто механизм чтения/записи строк и работы с листом.

 

Данный механизм позволяет без дальнейшего исправления класса L2PcInstance и без добавления настоящих переменных в этот или другой класс сделать гибкую настройку персонажа.

 

К примеру, у меня в сервере так реализовано сохранение настроек индивидуального автолута, экономии траффика, показа цен на телепорты и многое другое.

Оратор из меня плохой, так что кто не понял - извиняйте :)

 

Внешне этот код ни каких изменений не принесет, но будет часто встречаться в моих следующих мануалах.

 

Так же сразу хочу заметить, что в случаях очень частого доступа лучше сделать внешнюю переменную, в которую и будут записываться данные из динамической.

Дальше в коде покажу это на примере конфига автолута.

Делать это стоит для повышения производительности, ведь чтение непосредственно переменной, или её геттера гораздо быстрее, чем чтение из фастлиста.

 

Итак, поехали.

 

Для начала необходимо создать таблицу в базе данных.

Для этого можно сделать SQL-запрос, а можно сделать импорт из файла.

Как именно добавлять - решать Вам, я опишу второй способ и приведу код запроса.

У большинства стоит программа Navicat, для неё и пример:

В любом месте на компьютере создаем файл character_variables.sql

Открываем его Блокнотом, и вставляем вот такой код:

SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `character_variables`;
CREATE TABLE `character_variables` (
`OID` int(11) NOT NULL DEFAULT '0',
`VarName` varchar(50) NOT NULL DEFAULT '0',
`VarValue` varchar(255) NOT NULL DEFAULT '0',
UNIQUE KEY `prim` (`OID`,`VarName`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Сохраняем файл, и открываем Navicat.

В списке соединений (слева) открываем нашу базу и кликаем по ней правой кнопкой мыши.

Выбираем "Execute SQL File..."

В открывшемся окне выбираем созданный ранее файл, и жмем Start

После загрузки закрываем окно и обновляем список таблиц. Должна появиться новая таблица character_variables.

 

 

С таблицей готово, можно закрывать Навикат, далее пишем код ядра.

 

Открываем класс java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

В самый конец, перед последней закрывающей фигурной скобкой добавляем объявления и методы:


/////////////////////////////////////////////////////////////////////////////////////////
// CHARACTER_VARIABLES
/////////////////////////////////////////////////////////////////////////////////////////
private Map<String, String> _vars = new FastMap<String, String>();
public boolean _autoloot = false;
public boolean _autolootherbs = false;

//-----------------------------------------------------------------
public void setVar(String vName, String vValue){
_vars.put(vName, vValue);
}
public void setBoolVar(String vName, boolean vValue){
if (vValue) _vars.put(vName, "true");
else _vars.put(vName, "false");
}
//-----------------------------------------------------------------
public String getVar(String vName){
String tmpVal = _vars.get(vName);
return tmpVal != null ? tmpVal : "";
}
public boolean getBoolVar(String vName){
String tmpVal = _vars.get(vName);
return (tmpVal == null || tmpVal.equals("false")) ? false : true;
}
//-----------------------------------------------------------------
public final void delVar(String vName){
_vars.remove(vName);
}
//-----------------------------------------------------------------
public final void StoreVars(){
try (Connection con = L2DatabaseFactory.getInstance().getConnection()){
con.prepareStatement("DELETE FROM `character_variables` WHERE `OID`="+getObjectId()).executeUpdate();

if (_vars.size() > 0){
PreparedStatement statement = con.prepareStatement("INSERT INTO `character_variables` (`OID`, `VarName`, `VarValue`) VALUES (?,?,?)");
for (final String key : _vars.keySet()){
final String value = _vars.get(key);
statement.setInt(1, getObjectId());
statement.setString(2, key);
statement.setString(3, value);
statement.executeUpdate();
statement.clearParameters();
}
statement.close();
}
}
catch (Exception e) {_log.log(Level.WARNING, "", e);}
}
private final void RestoreVars(){
try (Connection con = L2DatabaseFactory.getInstance().getConnection()){
ResultSet vars = con.prepareStatement("SELECT * FROM character_variables WHERE OID="+getObjectId()).executeQuery();
while (vars.next()){
_vars.put(vars.getString("VarName"),vars.getString("VarValue"));
}
vars.close();
}
catch (Exception e) {_log.log(Level.WARNING, "", e);}
finally {
_autoloot = getBoolVar("autoloot");
_autolootherbs = getBoolVar("autolootherbs");
}
}
//
//
//
public boolean getAutolootherbs() {
return _autolootherbs;
}
public void setAutolootherbs(boolean _autolootherbs) {
this._autolootherbs = _autolootherbs;
setBoolVar("autolootherbs", _autolootherbs);
}
public boolean getAutoloot() {
return _autoloot;
}
public void setAutoloot(boolean _autoloot) {
this._autoloot = _autoloot;
setBoolVar("autoloot", _autoloot);
}

Заодно привел пример чтения параметров автолута, а куда его применить - думайте сами :)

 

Далее ищем в этом классе метод public synchronized void store(boolean storeActiveEffects){

И в самый его конец добавляем вызов метода сохранения нашего кода: StoreVars();

 

Ищем метод private static L2PcInstance restore(int objectId){ и куда-нибудь после создания нового объекта L2PcInstance вызываем метод загрузки: player.RestoreVars();

Стоит заметить, что метод этот обязательно вызывать у нового созданного объекта.

У меня получилось нечто такое:


private static L2PcInstance restore(int objectId){
L2PcInstance player = null;
try (Connection con = L2DatabaseFactory.getInstance().getConnection()){
final PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER);
statement.setInt(1, objectId);
final ResultSet rset = statement.executeQuery();
double currentCp = 0;
double currentHp = 0;
double currentMp = 0;

while (rset.next()){
final int activeClassId = rset.getInt("classid");
final boolean female = rset.getInt("sex") != Sex.MALE;
final L2PcTemplate template = CharTemplateTable.getInstance().getTemplate(activeClassId);
PcAppearance app = new PcAppearance(rset.getByte("face"), rset.getByte("hairColor"), rset.getByte("hairStyle"), female);
player = new L2PcInstance(objectId, template, rset.getString("account_name"), app);
player.RestoreVars();

 

Вот, в принципе и всё.

Код не сложный, как пользоваться разберетесь сами.

 

Автор мануала и кода я, Relvl (он же Johnson).

Копирование разрешаю только с указанием копирайтов на меня и на forummaxi.ru

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Использую похожую реализацию для автолута, автолута хербов, вкл/откл получения опыта и т.д.

Немножко не увидел смысла перегонять ResultSet в HashMap, чтобы потом из него заполнить поля, можно же сразу дергать данные из резалт сета по именам колонок в sql:

setAutoLoot(rs.getString("auto_loot"));

 

И слегка не понял вот этой фразы:

Данный механизм позволяет без дальнейшего исправления класса L2PcInstance и без добавления настоящих переменных в этот или другой класс сделать гибкую настройку персонажа.

В классе переменные (которые, кстати, в ООП принято называть полями) придется создавать для любого параметра, + сеттеры-геттеры + по строке в store и restore методы.

Хотя, бесспорно, такой подход значительно выгоднее, чем создавать поля + запросы + сеттеры-геттеры + store-restore для каждого поля.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Нужно было немного внимательнее читать. :)

 

Поля я заполняю только для опций, которые вызываются очень часто.

А вот опции, которые читаются редко (максимум раз в секунду) я читаю напрямую из фастмапа, через геттеры getBoolVar, getVar

 

Если частоиспользуемые опции вызывать из фастмапа - немного проседает производительность сервера.

А автолуты используются ОЧЕНЬ часто на средненаселенных серверах, ведь они вызываются по 3+ раз после каждого убийства моба...

 

Отсюда и следуюет логика для второй цитаты, большинство переменных я читаю геттером сразу (а точнее все, кроме экономии траффика и автолутов).

 

А запихнуть сюда можно что угодно: автолут адены/итемов/хербов, опыт вкл/выкл, цвет ника/титула, экономия траффика, альтернативное построение дерева скиллов для изучения (обычное, или учить сразу максимально доступный лвл скилла у учителя), опция и время для получения геройства на фиксированный срок... Да что угодно, это примерно треть опций, которые я использую.

 

Кстати, все эти настройки (кроме донаторских) я позволяю переключить из комьюнити. Обработчик комманд таким образом получается крайне маленький...

 

Сейчас я перепиливаю комьюнити полностью, возможно позже выложу (если, конечно, другие участники форума будут награздать меня за все шары и баг-фиксы Спасибками :) )...

На данный момент это уже одна из самых продвинутых Комьюнити, которые я видел на серверах (на правах хвастовства :) )...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Читаю с работы, иногда отвлекаюсь, не заметил :)

Честно говоря, не вижу какой-то целесообразности в этом. Избежать лишних полей и сеттеров-геттеров? Ну не знаю, сколь круто это.

FastMap интересная штука, не вникал в его реализацию особо, но судя по всему это что-то типа LinkedHashMap с временем чтения O(1), но с бОльшим ростом capacity по сравнению в HashMap и, скорее всего, бОльшим временем заполнения. Однако, меня устраивают обычные сеттеры-геттеры :)

 

Ну и да, вот еще кое что. Зачем городить такое:

public boolean getBoolVar(String vName){
	 String tmpVal = _vars.get(vName);
	 return (tmpVal == null || tmpVal.equals("false")) ? false : true;
}

 

Когда можно написать просто:

public boolean getBoolVar(String vName){
	 return "true".equals(_vars.get(vName));
}

 

И да, у меня тоже переключалки все в коммунке, очень удобно и писать под нее и пользоваться :)

  • Upvote 1

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
Авторизация  

  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...