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

Версия Minecraft
1.7.10
222
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% в другом моде\плагина я бы хотел сделать её сразу хорошо и правильно.
Собственно главный вопрос - как решить данную проблему максимально правильно и без костыльно? Как правильно спроектировать архитектуру в данном случае мода для подобной задачи?
 
Решение
Если игрок хочет посмотреть инфу о истории обмена, то только первый его запрос вызовет обращение к бд. После пусть будет только обращение к кэшу. Это позволит избежать лагов от частого обращения к бд и то, что игрок флудит пакетами будет неважно
7,099
324
1,510
Расскажи подробнее про задачу, которая решается этой хренью. Возможно, ее можно решить проще
 
222
5
28
Расскажи подробнее про задачу, которая решается этой хренью. Возможно, ее можно решить проще
Это трейд чат между игроками.
Player1 отправил Player2 вещи либо деньги и запросил взамен либо другие вещи либо деньги.
И между ними двухстороннее подтверждение обмена ну и сам процесс получения вещей.
Так же трейд можно отклонить.
Собственно на отклонении(ибо вещи возвращаются в зафлудженном количестве) и принятии и происходит это проблема с пакета о которой я написал.
 
222
5
28
Полное цитирование предыдущего сообщения запрещено
А зачем обращаться к бд?
Чтобы получить трейд, вещи в нём, текст сообщения, запрашиваемые монеты и т.д.
Т.е. это трейд отправил и ждёшь пока его получать прочитает и решит что с ним делать.
Там не требуется ответ в сию же минуту после получения.
 
7,099
324
1,510
Типо, межсерверная торговля? Если в пределах одного сервера, то можно хранить не в бд, а в какой-то коллекции прямо в сервере
 
7,099
324
1,510
7,099
324
1,510
Тогда бд это излишняя деталь. И вообще, можно найти готовое решение [1.12.2] Oxygen
 
7,099
324
1,510
Мне кажется там приличный объём информации придётся хранить.
Кажется? А как оценивал? Можно сделать оценку худшего случая как объем структуры данных * количество юзеров в онлайне / 2 . Это для случая, что все игрока торгуются одновременно
 
7,099
324
1,510
После успешного обмена можно выбрасывать историю из коллекции в бд. Или что лучше, накапливать историю в отдельной коллекции и сбрасывать в бд сразу большим кусокм
 
222
5
28
Это конечно понятно, но всё настолько плохо, что игроки могут просматривать сами свои обмены и даже то, что запрашивали и получали в нём.
И этот дибильный мод устроен таким макаром, что когда игрок открывает список обменов то ему грузятся сразу все и получается что мне всё равно придётся достать все завершённые обмены из бд.
 
7,099
324
1,510
Тогда можно кэшировать результаты запросов к бд, таким образом один игрок не сможет вызвать лаги, потому что только первый его клик будет вызывать запрос к бд
 
7,099
324
1,510
Если игрок хочет посмотреть инфу о истории обмена, то только первый его запрос вызовет обращение к бд. После пусть будет только обращение к кэшу. Это позволит избежать лагов от частого обращения к бд и то, что игрок флудит пакетами будет неважно
 
Сверху