PDA

Просмотр полной версии : [Вопрос] Удобный перенос предметов с доп. ифнормацией в другой таблицей в разные инвентари



execution
13.02.2021, 10:32
Здравствуйте.
Никак не могу определиться, как лучше организовать перенос предметов с доп. информацией в других таблицах в разные инвентари так, чтобы в случае удаления самого предмета в каком-либо инвентаре - удалялась вся доп. информация этого предмета, но не при перемещении.

Например, есть инвентари (player_inventory, vehicle_inventory, house_inventory), есть предмет "Записная книга", которая хранится в таблице diary, и её записи в diary_note.

1. В такой реализации видов инвентарей, нет никакого смысле привязываться по ключу к каждому инвентарю (ибо при удалении в инвентаре - предмет автоматически удалится в зависимых таблицах).

2. Когда удаляют из бд аккаунт игрока, автомобиля или дома, то все предметы находящиеся в их инвентарях - удаляются, и тут мне в ручную никак не отследить, только если удаление из самого сервера. Либо же триггер (либо триггер + процедура) и делать танцы с бубнами, чтобы подчищать всё на свете (и так со всеми предметами с доп. информацией в других таблицах?)

DeimoS
13.02.2021, 11:03
Если честно, складывается впечатление, что ты сам себе придумываешь проблемы, с которыми потом хочешь героически бороться) Ибо непонятно зачем, например, удалять какие-либо аккаунты.

Ну касаемо твоего вопроса - можно попытаться настроить связи между таблицами. Но, вероятнее всего, придётся вводить дополнительные столбцы с индексами и т.п. Либо удалять данные не сразу, а подчищать всё в момент запуска сервера.
Хотя если отбросить идею удаления аккаунта, то не вижу какой-то проблемы в том, чтоб удалять данные из таблицы записных книжек отдельным запросом в момент удаления этой записной книжки из инвентаря.

execution
13.02.2021, 11:53
Если честно, складывается впечатление, что ты сам себе придумываешь проблемы, с которыми потом хочешь героически бороться)
Обидно как-то



Ибо непонятно зачем, например, удалять какие-либо аккаунты


Ну, бывает так, что удаляются аккаунты, что не так-то? Или имеется ввиду реализовать пометку is_delete?
В таком случае необходимо вручную удалять все предметы или вовсе не трогать их?



Хотя если отбросить идею удаления аккаунта, то не вижу какой-то проблемы в том, чтоб удалять данные из таблицы записных книжек отдельным запросом в момент удаления этой записной книжки из инвентаря.


Получается, рассматривать ситуацию, когда мы удаляем из таблицы diary какую-то книгу, то чтобы каким-то образом очищались предметы с книгой в самих инветарях не стоит, верно?



Ну и хотелось бы уточнить, правильно я делаю, что храню сами записные книги в diary, и их индекс устанавливаю в, допустим, в extra_int в инвентаре, и по нему удаляю саму запись в diary а сама таблица удаляет записи в diary_note?

DeimoS
13.02.2021, 13:27
Обидно как-то

Ну как есть :)


Ну, бывает так, что удаляются аккаунты, что не так-то?

Удаляются чтоб что? Какая реальная проблема этим удалением решается? И является ли удаление единственным решением этой проблемы?
Я бы мог сейчас тут начать рассказывать про фрагментацию базы данных из-за ненужных удалений (из-за которой размер базы может только расти, а не уменьшаться), и прочие вещи, но что-то мне подсказывает, что даже на предыдущие вопросы ответов нормальных не будет, ибо лично я не могу представить ситуацию, в которой без удаления аккаунта ну совершенно нельзя было бы. А вот ситуацию, при которой удаление создаёт лишь больше проблем, чем пользы - легко (собственно, твоя ситуация такая).

Опять же, это не значит, что теперь вообще не нужно удалять никакие данные из таблицы. Если какая-то запись не нужна и её удаление не потребует почистить ещё десяток таблиц от остаточных данных, то почему бы не удалить? А если потребует, то либо изначально нужно таблицы проектировать с учётом всех связей, либо придётся страдать. И под страданиями я подразумеваю различные реализации того, как с такими ненужными данными можно работать.


Получается, рассматривать ситуацию, когда мы удаляем из таблицы diary какую-то книгу, то чтобы каким-то образом очищались предметы с книгой в самих инветарях не стоит, верно?

Не очень понял. Речь про удалении книги из всей системы (чтоб её вообще нельзя было больше получить никакому игроку и чтоб у текущих игроков она удалилась) или про удаление книги из инвентаря конкретного игрока?


Ну и хотелось бы уточнить, правильно я делаю, что храню сами записные книги в diary, и их индекс устанавливаю в, допустим, в extra_int в инвентаре, и по нему удаляю саму запись в diary а сама таблица удаляет записи в diary_note?

Подробнее распиши что это за система у тебя и какие функции она должна иметь.

execution
13.02.2021, 14:25
Опять же, это не значит, что теперь вообще не нужно удалять никакие данные из таблицы. Если какая-то запись не нужна и её удаление не потребует почистить ещё десяток таблиц от остаточных данных, то почему бы не удалить? А если потребует, то либо изначально нужно таблицы проектировать с учётом всех связей, либо придётся страдать. И под страданиями я подразумеваю различные реализации того, как с такими ненужными данными можно работать.


Ну в общей сложности, раз уж об этом заговорили, не следует удалять аккаунты и стоит помечать их как удалённые, чтобы игрок мог создать, например, аккаунт с таким ником?



Не очень понял. Речь про удалении книги из всей системы (чтоб её вообще нельзя было больше получить никакому игроку и чтоб у текущих игроков она удалилась) или про удаление книги из инвентаря конкретного игрока?




Подробнее распиши что это за система у тебя и какие функции она должна иметь.


На данный момент, при покупке записной книги, добавляем в таблицу diary (id, account_id).
При создании какой-либо записи, добавляем в таблицу diary_note (id, diary_id, /*some column*/).

Таблица diary связана с таблицей accounts (при удалении аккаунта - удаляется книжка).
Таблица diary_note связана с таблицей diary (при удалении книжки - удаляются все записи).

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

Теперь же реализую хранение всех предметов в инвентаре (создаю предмет отдельно ITEM_TYPE_DIARY)

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

DeimoS
13.02.2021, 14:29
Ну в общей сложности, раз уж об этом заговорили, не следует удалять аккаунты и стоит помечать их как удалённые, чтобы игрок мог создать, например, аккаунт с таким ником?

Либо помечай, либо переделывай все таблицы так, чтоб удаление данных о игроке не было сложным (что стоило сделать изначально), либо просто переименовывай, например.
Но лучше, конечно, второй вариант (с переработкой структуры таблиц).






На данный момент, при покупке записной книги, добавляем в таблицу diary (id, account_id).
При создании какой-либо записи, добавляем в таблицу diary_note (id, diary_id, /*some column*/).

Таблица diary связана с таблицей accounts (при удалении аккаунта - удаляется книжка).
Таблица diary_note связана с таблицей diary (при удалении книжки - удаляются все записи).

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

Теперь же реализую хранение всех предметов в инвентаре (создаю предмет отдельно ITEM_TYPE_DIARY)

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

Меня не то, как у тебя сейчас всё реализовано, интересует, а то, что ты хочешь сделать. Что за записи это? Какими они могут быть по длинне? Сколько их может быть? С чем/кем они связаны? Какой функционал на этих записях завязан в игре?

execution
13.02.2021, 15:02
Меня не то, как у тебя сейчас всё реализовано, интересует, а то, что ты хочешь сделать. Что за записи это? Какими они могут быть по длинне? Сколько их может быть? С чем/кем они связаны? Какой функционал на этих записях завязан в игре?

Я не совсем понимаю, зачем тебе данные о том, какие записи и т.п.?

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

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

DeimoS
13.02.2021, 15:05
Затем, чтоб придумать как нормально организовать таблицы, чтоб и работать с данными удобно было, и удалять их.

execution
13.02.2021, 15:10
Затем, чтоб придумать как нормально организовать таблицы, чтоб и работать с данными удобно было, и удалять их.

Что за записи это? - в дневнике, они создаются в таблице diary_note (неограниченное количество)
С чем/кем они связаны? - дневник связан с игроком, записи с дневником, показываются прямо из бд.
Какой функционал на этих записях завязан в игре? - создать запись, дать посмотреть игроку запись (и сам могу), удалить

Когда игрок логинится, смотрю есть ли в таблице дневник и загружаю для игрока только информацию об дневнике

UPD: функционал можно урезать, по необходимости

DeimoS
13.02.2021, 15:21
То бишь, там хранится текст, который игрок сам и записывает в него. И, при этом, текст привязан к предмету инвентаря?

И что у тебя сейчас за структура таблиц? Не очень понятно зачем две таблицы и что за привязка к игроку.

execution
13.02.2021, 15:42
То бишь, там хранится текст, который игрок сам и записывает в него.
Да.


И, при этом, текст привязан к предмету инвентаря
Нет, сейчас просто привзян к книге, которая в diary. Сейчас же хочу как-то привязать к инвентарю или т.п.


И что у тебя сейчас за структура таблиц? Не очень понятно зачем две таблицы и что за привязка к игроку.
Одна таблица, где хранится сама книга, вторая - где её записи. Раньше просто не хотел привязываться к столбцу в accounts и на будущее для, например, каких-то нововведений в книгу.

Сейчас же, я так понял, необходимо так-же оставить всё, но не привязываться к игроку, к которому принадлежит, а чтобы книга была сама по себе. И в инвентаре, например, создать extra_int где будет хранится ID строки книги и по ней загружать остальные данные, что-ли



mysql_query(DIARY_MYSQL_CONNECTION_ID, "\
CREATE TABLE IF NOT EXISTS `diary` ( \
`id` int(11) NOT NULL AUTO_INCREMENT, \
`owner_id` int(11) NOT NULL, \
PRIMARY KEY (`id`) \
) ENGINE=InnoDB DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ;", false);

mysql_query(DIARY_MYSQL_CONNECTION_ID, !"\
CREATE UNIQUE INDEX \
owner_id \
ON \
diary(owner_id)", false);

mysql_query(DIARY_MYSQL_CONNECTION_ID, !"\
ALTER TABLE \
`diary`\
ADD CONSTRAINT \
`diary_accounts_fk_1` \
FOREIGN KEY \
(`owner_id`) \
REFERENCES \
`accounts` (`pID`) \
ON DELETE CASCADE ON UPDATE CASCADE", false);





mysql_format(DIARY_MYSQL_CONNECTION_ID, query_string, sizeof query_string, "\
CREATE TABLE IF NOT EXISTS `diary_notation` ( \
`id` int(11) NOT NULL AUTO_INCREMENT, \
`diary_id` int(11) NOT NULL, \
`caption` varchar(%d) NOT NULL, \
`contents` varchar(%d) NOT NULL, \
`date` date NOT NULL, \
PRIMARY KEY (`id`) \
) ENGINE=InnoDB DEFAULT CHARSET=cp1251 AUTO_INCREMENT=1 ;",
DIARY_MAX_CAPTION_LENGTH, DIARY_MAX_CONTENTS_LENGTH);
mysql_query(DIARY_MYSQL_CONNECTION_ID, query_string, false);

mysql_query(DIARY_MYSQL_CONNECTION_ID, !"\
CREATE INDEX \
diary_id \
ON \
diary_notation(diary_id)", false);

mysql_query(DIARY_MYSQL_CONNECTION_ID, !"\
CREATE INDEX \
date \
ON \
diary_notation(date)", false);

mysql_query(DIARY_MYSQL_CONNECTION_ID, !"\
ALTER TABLE \
`diary_notation`\
ADD CONSTRAINT \
`diary_notation_diary_fk_1` \
FOREIGN KEY \
(`diary_id`) \
REFERENCES \
`diary` (`id`) \
ON DELETE CASCADE ON UPDATE CASCADE", false);

DeimoS
13.02.2021, 15:52
Создаёшь таблицу со структурой
id | diary_id | header | text | date
id - это, соответственно, primary key + AUTO_INCREMENT
diary_id - тут будет хранится ID дневника, который уже будет указываться в инвентаре и т.п.
На diary_id и date цепляешь составной индекс (именно в порядке: diary_id + date).

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

execution
13.02.2021, 16:45
Спасибо в очередной раз.
Немного не понял - каким образом я тогда буду генерировать уникальный ид diary_id, если не будет бд со всеми дневниками?

DeimoS
13.02.2021, 20:44
SELECT MAX(diary_id)+1 as unique_diary_id FROM table

tnc
13.02.2021, 21:56
Небольшой совет: лучше подобные запросы (CREATE TABLE IF NOT EXISTS) запускать из файла (https://sampwiki.blast.hk/wiki/MySQL#mysql_tquery_file)

execution
13.02.2021, 22:50
Небольшой совет: лучше подобные запросы (CREATE TABLE IF NOT EXISTS) запускать из файла (https://sampwiki.blast.hk/wiki/MySQL#mysql_tquery_file)

Почему?

tnc
13.02.2021, 22:58
Почему?

Меньше проблемы с экранированием / переносом строк. Легче вставить экспорт из БД

DeimoS
14.02.2021, 05:34
Небольшой совет: лучше подобные запросы (CREATE TABLE IF NOT EXISTS) запускать из файла (https://sampwiki.blast.hk/wiki/MySQL#mysql_tquery_file)

имхо, это работает только если запрос в будущем будет часто видоизменяться (что бывает крайне редко). В остальных случаях, как по мне, проще вставить запрос в код и забыть о нём. Ибо файл и перекидывать постоянно нужно при переезде с хостинга на хостинг (что легко забыть и тот или иной файл легко потерять), и в случае взлома хоста, взломавший легко получает доступ к этим самым файлам.



Меньше проблемы с экранированием / переносом строк. Легче вставить экспорт из БД

С нормальным редактором кода, поддерживающим функцию многострочного курсора, каких-то проблем с подобным добавлением нет.

UPD: От себя я бы добавил, что не стоит забывать о конструкции
SHOW TABLES LIKE ...
если нужно не просто проверить, создана ли таблица, а либо удостовериться в существовании таблицы, либо загрузить из неё данные.

tnc
14.02.2021, 07:03
имхо, это работает только если запрос в будущем будет часто видоизменяться (что бывает крайне редко).
В таблицы часто добавляют новые столбцы => идет обновление структуры таблицы. Это может быть довольно часто (например: таблица аккаунтов).

Ибо файл и перекидывать постоянно нужно при переезде с хостинга на хостинг (что легко забыть и тот или иной файл легко потерять)
Ну так не терять файл? :) Хранить его в scriptfiles и нет проблем?

P.S: Я конечно не проверял, но mysql_tqeury_file возвращает 0 в любом другом случаи, если не получилось выполнить (например: нет нужного файла в папке) запрос. Можно добавить проверку на возвращаемое значение и выводить, что mysql_tquery_file не был выполнен.


и в случае взлома хоста, взломавший легко получает доступ к этим самым файлам.

Если получили доступ до сервера, то тут уже никакая защита не поможет, а это проблема безопасности. Тем более, какие данные получить взломщик? Структуру таблицы? Что ему дальше с ней делать?

P.S: Если взломали сервер, ничего не стоит подключить свой плагин и перехватить вызов mysql_connect и достать данные, так что тут нет смысла об этом говорить, что какие-то файлы лежат в корне сервера (или ещё где-то с серверными файлами)

DeimoS
14.02.2021, 17:08
В таблицы часто добавляют новые столбцы => идет обновление структуры таблицы. Это может быть довольно часто (например: таблица аккаунтов).

Ну так в этом случае разницы между добавлением в код и добавлением в мод особой не будет. В коде тебе нужно лишь добавить обратные слэши в конец строки и заменить уже вставленный ранее запрос, а в случае с файлом, тебе нужно сначала заменить содержимое файла и потом ещё на хостинг его залить. По трудозатратам это +- одинаковые телодвижения.

Ну и в таких случаях нужно просто изучить синтаксис запроса на создание таблицы, дабы не просить каждый раз phpMyAdmin составить новый запрос, а просто вручную добавить нужный столбец в старый.
Либо можно просто через ALTER TABLE ADD COLUMN добавлять новые столбцы, не редактируя основной запрос на создание таблицы.


Ну так не терять файл? :) Хранить его в scriptfiles и нет проблем?

Речь о том, что это лишние файлы, которые нужно постоянно подтягивать и держать в актуальном состоянии. Это, конечно, не смертельно, но если есть вариант избежать подобного, лично я лучше выберу его. Ибо добровольно создавать в своей работе лишний простор для человеческой ошибки я как-то не очень хочу.


Если получили доступ до сервера, то тут уже никакая защита не поможет, а это проблема безопасности. Тем более, какие данные получить взломщик? Структуру таблицы? Что ему дальше с ней делать?

Как минимум, это открывает доступ к SQL-инъекциям.


P.S: Если взломали сервер, ничего не стоит подключить свой плагин и перехватить вызов mysql_connect и достать данные, так что тут нет смысла об этом говорить, что какие-то файлы лежат в корне сервера (или ещё где-то с серверными файлами)

Вот только далеко не каждый сможет такой плагин написать :)


В любом случае, автор сам решит какой подход ему ближе.

tnc
14.02.2021, 19:15
Как минимум, это открывает доступ к SQL-инъекциям.

Эмм? Если строка экранированная, то каким образом?


Вот только далеко не каждый сможет такой плагин написать :)

Я тебе к тому, что если получили доступ до машины, то это в любом случаи уже "слитая каточка". Там есть другие способы, которые могу навредить проекту. Например: как раз такой плагин и достать данные. Это просто пример, но факт в том, что если получили доступ до машины, то защиты от этого никакой нет, кроме как сменить пароль и искать бэкдоры в системе.

DeimoS
14.02.2021, 20:01
Эмм? Если строка экранированная, то каким образом?

Если строка экранирована, то, естественно, никаким. Но у нас идёт речь о пчелике, у которого на руках есть только структура таблицы и большое желание повеселиться на сервере. Имея на руках правильные имена таблиц и полей, такой пчелик может начать перебирать все существующие системы, которые так или иначе отправляют запрос в БД (или перебирать вообще все системы, если этот момент не очевиден). И если разработчик в какой-то из систем забыл добавить экранирование, то итог, думаю, будет очевиден (а с учётом общего уровня знания MySQL в Pawn-сообществе и качества кода тех же слитых в сеть модов - шансы найти такую дырявую систему довольно велики).


Я тебе к тому, что если получили доступ до машины, то это в любом случаи уже "слитая каточка". Там есть другие способы, которые могу навредить проекту. Например: как раз такой плагин и достать данные. Это просто пример, но факт в том, что если получили доступ до машины, то защиты от этого никакой нет, кроме как сменить пароль и искать бэкдоры в системе.

Да причём тут какая-то защита или гипотетические супер-хакеры, которые могут взломать хостинг и напихать в него кучу своих плагинов? Это всё и так очевидно.
Ещё раз: речь сугубо о том, что, с подобными файлами на хостинге, даже Васян, сидящий на каком-нибудь blast.hk, сможет, потенциально, навредить твоему серверу, если ты где-то забудешь экранировать входящие данные.
И это лишь пример одного из нескольких, по моему мнению, недостатков предложенного тобой метода. Собственно, если тебя какие-то преимущества данного метода устраивают, несмотря на перечисленные мной аспекты - пожалуйста, пользуйся им. Я же не запрещаю :)