Требуется помощь в реализации

Версия Minecraft
1.7.10
224
5
28
Есть мод, в котором есть проблема с многопоточностью. Его автор очень сильно обосрался с её реализацией. Как я думаю уже понятно - мне выпала "честь" исправить это.

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

К примеру приходит пакет на сервер, который говорит, что нужно сделать с объектом с id таким-то некое действие описанное с помощью Consumer.

Java:
public static void receive(String id, PacketBuffer buffer, EntityPlayer player) {
        switch (id) {
            case "simple_action_1":
                
                int objectId = buffer.readInt();

                Messenger.getObjectById(objectId, object -> {
                    
                    //simple actions with the object...
                    
                });
                
                break;
            case "simple_action_2":
                //todo
                break;
            case "simple_action_3":
                //todo
                break;
            case "simple_action_4":
                //todo
                break;
        }
    }

Но перед тем как это дело делать, нужно получить и сформировать этот объект из базы данных.
Конечно же это делается в отдельном треде.

Java:
public static void getObjectById(int id, Consumer<MessageContainer> consumer) {
        CoreServer.listener().connect((connection, statement) -> {
            try (PreparedStatement ps = connection.prepareStatement("SELECT * FROM [ICODE]table[/ICODE] WHERE id = ?")) {
                ps.setInt(1, id);
                try (ResultSet rs = ps.executeQuery()) {
                    if (rs.next()) {
                        SimpleObject object = new SimpleObject(rs);
                        CoreServer.add(e -> {
                            try {
                                consumer.accept(object);
                            } catch (Throwable ex) {
                                CoreServer.listener().unlock();
                                throw ex;
                            }
                        });
                    }
                }
            } catch (Exception e) {
                LOGGER.error("Exception while get object by id: '" + id + "'", e);
            }
        });
    }

После получения и сформирования объекта мы передадим его на съедение Consumer, но сделаем это добавив задачу в очередь, которая будет выполнена при следующем тике сервера:

Java:
    private static final Queue<ITickUpdate> tickUpdates = new ArrayDeque<>();

    public static void add(ITickUpdate update) {
        synchronized (tickUpdates) {
            tickUpdates.add(update);
        }
    }

    @SubscribeEvent
    public void update(TickEvent.ServerTickEvent event) {
         synchronized (tickUpdates) {
             while (!tickUpdates.isEmpty()) {
                 tickUpdates.poll().tick(event);
             }
         }
    }

    @FunctionalInterface
    public interface ITickUpdate {

        void tick(TickEvent.ServerTickEvent e);

    }

А теперь сама проблема. Представьте, что то самое получение пакета является нажатием на кнопку в GUI. И что нажатий может быть овер дохрена тем же кликером.
Т.е. что мы имеет - при многократном нажатии на кнопку отправляется кучу пакетов, эти кучу пакетов первым делом получают в async треде объект из базы базы данных, с которым им предстоит работать. И проблема в том, что действия начинают выполняться последовательно и если на первой задачи тот самый объект изменился, то следующая задача дубликат об этом ничего не знает и выполняет действие по новой. Конечно же нельзя сделать так, чтобы получение объекта всегда была "свежее", так как оно будет выполняться в главном потоке, а это уже лаги.

Автор попытался справится с данной проблемой временной "блокировкой" конекта(даже не вдумывайтесь, полнейшая параша гавнокода), это помогло, но в итоге это привело к тому, что игроки догадались делать это во время сохранения мира или лага сервера, тем самым забивая очередь одинаковыми задачами. Позже я узнал, что даже без лага получилось воспроизвести проблему.

Конечно, я мог бы сделать простейшую защиту от флуда(по типам действий и id объектов), но так как я считаю что это будет костыль и подобная система мне будет нужна 100% в другом моде\плагина я бы хотел сделать её сразу хорошо и правильно.
Собственно главный вопрос - как решить данную проблему максимально правильно и без костыльно? Как правильно спроектировать архитектуру в данном случае мода для подобной задачи?
 
Решение
Если игрок хочет посмотреть инфу о истории обмена, то только первый его запрос вызовет обращение к бд. После пусть будет только обращение к кэшу. Это позволит избежать лагов от частого обращения к бд и то, что игрок флудит пакетами будет неважно
Расскажи подробнее про задачу, которая решается этой хренью. Возможно, ее можно решить проще
 
Расскажи подробнее про задачу, которая решается этой хренью. Возможно, ее можно решить проще
Это трейд чат между игроками.
Player1 отправил Player2 вещи либо деньги и запросил взамен либо другие вещи либо деньги.
И между ними двухстороннее подтверждение обмена ну и сам процесс получения вещей.
Так же трейд можно отклонить.
Собственно на отклонении(ибо вещи возвращаются в зафлудженном количестве) и принятии и происходит это проблема с пакета о которой я написал.
 
Полное цитирование предыдущего сообщения запрещено
А зачем обращаться к бд?
Чтобы получить трейд, вещи в нём, текст сообщения, запрашиваемые монеты и т.д.
Т.е. это трейд отправил и ждёшь пока его получать прочитает и решит что с ним делать.
Там не требуется ответ в сию же минуту после получения.
 
Типо, межсерверная торговля? Если в пределах одного сервера, то можно хранить не в бд, а в какой-то коллекции прямо в сервере
 
Тогда бд это излишняя деталь. И вообще, можно найти готовое решение [1.12.2] Oxygen
 
Мне кажется там приличный объём информации придётся хранить.
Кажется? А как оценивал? Можно сделать оценку худшего случая как объем структуры данных * количество юзеров в онлайне / 2 . Это для случая, что все игрока торгуются одновременно
 
После успешного обмена можно выбрасывать историю из коллекции в бд. Или что лучше, накапливать историю в отдельной коллекции и сбрасывать в бд сразу большим кусокм
 
Это конечно понятно, но всё настолько плохо, что игроки могут просматривать сами свои обмены и даже то, что запрашивали и получали в нём.
И этот дибильный мод устроен таким макаром, что когда игрок открывает список обменов то ему грузятся сразу все и получается что мне всё равно придётся достать все завершённые обмены из бд.
 
Тогда можно кэшировать результаты запросов к бд, таким образом один игрок не сможет вызвать лаги, потому что только первый его клик будет вызывать запрос к бд
 
Если игрок хочет посмотреть инфу о истории обмена, то только первый его запрос вызовет обращение к бд. После пусть будет только обращение к кэшу. Это позволит избежать лагов от частого обращения к бд и то, что игрок флудит пакетами будет неважно
 
Назад
Сверху